@amr-m-abdelgawad/devctl 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devctl.js +58 -58
- 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 mn(e,t){return DP(t).some((n)=>(e.name??"").toLowerCase()===n.name&&Boolean(e.ctrl)===n.ctrl&&Boolean(e.shift)===n.shift&&Boolean(e.meta||e.super)===n.meta&&Boolean(e.option??e.alt)===n.alt)}var sd=2000,Lo=14,Am="DEVCTL_TUI_CONFIG",LP;var ta=I(()=>{LP=AP()});function Se(e,t,r){return{type:e,timestamp:new Date().toISOString(),service:t,payload:r}}class ud{subs=new Map;nextID=0;buffer=[];maxBuf;constructor(e){this.maxBuf=e>0?e:1024}subscribe(e,t=[]){this.nextID+=1;let r=this.nextID;return this.subs.set(r,{types:new Set(t),handler:e}),()=>{this.subs.delete(r)}}publish(e){let t=e.timestamp===""?{...e,timestamp:new Date().toISOString()}:e;if(this.buffer.length>=this.maxBuf)this.buffer=this.buffer.slice(1);this.buffer.push(t);for(let r of this.subs.values())if(r.types.size===0||r.types.has(t.type))r.handler(t)}recent(){return[...this.buffer]}}var Mm="ServiceStarted",ca="ServiceStopped",la="ServiceFailed",Om="ServiceStateChanged",$m="ServiceHealthChanged",Io="LogReceived",Mo="TokenRefreshed",Gn="TokenRefreshFailed",ld="AuthenticationChanged",Fm="ProxyStarted",Nm="ProxyStopped",dd="ProxyRequest",da="ConfigurationChanged",Ti="ConfigurationReloadFailed",Hm="SessionRecovered";var jm=5000,Bm=500,pd=16384,Um=65536,ua="TRACE",pa="DEBUG",fa="INFO",Ai="WARN",Kn="ERROR",Oo="FATAL",ma="UNKNOWN",fd;var Li=I(()=>{fd={["TRACE"]:0,["DEBUG"]:1,["INFO"]:2,["WARN"]:3,["ERROR"]:4,["FATAL"]:5,["UNKNOWN"]:2}});function Wm(){return{name:"default",parse:(e)=>{let t=vd(e);if(t)return t;return{level:yd(e),request_id:bd(e)||void 0}}}}function Di(e,t){for(let r of t){let n=e[r];if(typeof n==="string"&&n.trim()!=="")return n}return}function Km(e){for(let t of jP){let r=e[t];if(typeof r==="number"){let n=YP[r];if(n)return n;continue}if(typeof r==="string"&&r.trim()!=="")return r.toUpperCase()}return}function Ii(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Zt(e){if(typeof e==="string"&&e.trim()!=="")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);return}function $o(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"&&e.trim()!==""){let t=Number(e);if(Number.isFinite(t))return t}return}function $r(e,t){for(let r of t)if(e[r]!==void 0&&e[r]!==null)return e[r];return}function hd(e){return e.filter((t)=>t!==void 0&&t!=="").join(" ")}function JP(e){if(e>=UP)return Kn;if(e>=BP)return Ai;return}function XP(e){if(Ii(e))return e;return{}}function qm(e){let t=Zt($r(e,["http.method","method","request_method"])),r=Zt($r(e,["http.route","url.path","path","route","request_uri"])),n=Zt($r(e,["http.status_code","status","status_code"])),o=hd([t,r,n]);return o===""?void 0:o}function Vm(e){if(!Ii(e))return Zt(e);if(typeof e.stringValue==="string")return e.stringValue.trim()===""?void 0:e.stringValue;return Zt(e.intValue??e.doubleValue??e.boolValue)}function QP(e){if(typeof e==="string"&&e.trim()!=="")return e;return Vm(e)}function zm(e){if(!Array.isArray(e)||e.length===0)return!1;return e.every((t)=>Ii(t)&&typeof t.key==="string"&&t.value!==void 0)}function ZP(e){let t={};if(!zm(e))return t;for(let r of e){let n=r.key;if(typeof n==="string"&&n!==""){let o=Vm(r.value);if(o!==void 0)t[n]=o}}return t}function e_(e){let t=Km(e);if(t)return t;let r=$o(e.severityNumber);if(r===void 0)return;if(r>=zP)return Oo;if(r>=VP)return Kn;if(r>=qP)return Ai;if(r>=KP)return fa;if(r>=GP)return pa;if(r>=WP)return ua;return}function t_(e){if(e.timeUnixNano!==void 0||e.observedTimeUnixNano!==void 0)return!0;if(Ii(e.body)&&(e.body.stringValue!==void 0||e.body.intValue!==void 0||e.body.doubleValue!==void 0))return!0;return zm(e.attributes)}function r_(e,t){if(!t_(e))return;let r=ZP(e.attributes),n=QP(e.body)??qm(r)??"log",o=Di(e,md)??Zt(r.trace_id??r.traceId);return{message:n,level:e_(e),request_id:o,raw:t}}function n_(e,t){let r=Zt(e.metric_name);if(r===void 0)return;let n=Zt(e.metric_type),o=$o(e.value);if(n===void 0&&o===void 0)return;let i=Zt(e.unit),s=o===void 0?void 0:i===void 0?String(o):`${o} ${i}`;return{message:hd([r,n,s,qm(XP(e.attributes))]),raw:t}}function o_(e){let t=e.trim().split(/\s+/);return{method:t[0],uri:t[1]}}function i_(e,t){if(Di(e,Gm)!==void 0)return;let r=Zt(e.request),n=r===void 0?{}:o_(r),o=Zt($r(e,["request_method","method","http_method","verb"]))??n.method,i=Zt($r(e,["request_uri","uri","url","path","request_path"]))??n.uri,s=$o($r(e,["status","status_code","statusCode","http_status"])),a=Zt($r(e,["remote_ip","remote_addr","client_ip"]));if(o===void 0||i===void 0)return;if(s===void 0&&a===void 0&&r===void 0)return;let c=$o($r(e,["body_bytes_sent","bytes_sent","bytes"])),l=$o($r(e,["request_time","duration"])),d=$o($r(e,["request_time_ms","latency_ms","duration_ms"])),u=c===void 0?void 0:`${c}B`,f=d!==void 0?`${d}ms`:l===void 0?void 0:`${l}s`;return{message:hd([a,o,i,s===void 0?void 0:String(s),u,f]),level:s===void 0?void 0:JP(s),request_id:Di(e,md),raw:t}}function s_(e,t){let r=Di(e,Gm),n=Km(e),o=Di(e,md);if(r===void 0&&n===void 0&&o===void 0)return;return{message:r??t,level:n,request_id:o,raw:t}}function gd(e){if(e.length<=pd)return e;return e.slice(0,pd)}function vd(e){let t=e.trim();if(!t.startsWith("{")||!t.endsWith("}"))return;if(t.length>Um)return;let r;try{r=JSON.parse(t)}catch{return}if(!Ii(r))return;return r_(r,t)??n_(r,t)??i_(r,t)??s_(r,t)}function yd(e){return c_.find((r)=>r.re.test(e))?.level??ma}function bd(e){return a_.exec(e)?.[1]??""}var Gm,jP,md,BP=400,UP=500,WP=1,GP=5,KP=9,qP=13,VP=17,zP=21,YP,a_,c_;var Ym=I(()=>{Li();Gm=["message","msg","text","log","event"],jP=["level","severity","severityText","levelname","loglevel","lvl"],md=["request_id","requestId","trace_id","traceId","correlation_id","correlationId"],YP={10:ua,20:pa,30:fa,40:Ai,50:Kn,60:Oo};a_=/(?:x-devctl-request-id|request[_-]?id)[=: ]+([A-Za-z0-9-]+)/i,c_=[{re:/\b(fatal|critical)\b/i,level:Oo},{re:/\b(error|err)\b/i,level:Kn},{re:/\b(warn|warning)\b/i,level:Ai},{re:/\b(debug|dbg)\b/i,level:pa},{re:/\b(trace)\b/i,level:ua},{re:/\b(info|information)\b/i,level:fa}]});function Mi(e){let t=l_(e);if(t===void 0)return;return new RegExp(t)}function l_(e){if(e.length===0||e.length>200)return;let t={raw:e,i:0,out:[],quantifiers:0,depth:0,lastAtom:"none",groupQuant:[]};if(!Jm(t)||t.i!==e.length||t.depth!==0)return;return t.out.join("")}function Jm(e){while(e.i<e.raw.length){if(e.raw[e.i]===")")return!0;if(!d_(e))return!1}return!0}function d_(e){let t=e.raw[e.i]??"";if(t==="|")return ha(e,"|","none");if(t==="^")return ha(e,"^","none");if(t==="$")return ha(e,"$","none");if(t===".")return ha(e,".","atom");if(t==="(")return u_(e);if(t==="[")return f_(e);if(t==="*"||t==="+"||t==="?"||t==="{")return p_(e);if(t==="\\")return Xm(e);return e.out.push(RegExp.escape(t)),e.i+=1,e.lastAtom="atom",!0}function ha(e,t,r){return e.out.push(t),e.i+=1,e.lastAtom=r,!0}function u_(e){if(e.i+=1,e.depth+=1,e.depth>2)return!1;if(e.groupQuant.push(!1),e.out.push("(?:"),!Jm(e)||e.raw[e.i]!==")")return!1;e.out.push(")"),e.i+=1,e.depth-=1;let t=e.groupQuant.pop()===!0;return e.lastAtom=t?"quantified-group":"atom",!0}function p_(e){let t=e.raw[e.i]??"";if(t==="{"||e.lastAtom==="none"||e.lastAtom==="quant"||e.lastAtom==="quantified-group")return!1;if(e.quantifiers>=8)return!1;if(t==="*")e.out.push("*");else if(t==="+")e.out.push("+");else if(t==="?")e.out.push("?");else return!1;e.i+=1,e.quantifiers+=1;let r=e.groupQuant.length-1;if(r>=0)e.groupQuant[r]=!0;if(e.raw[e.i]==="?")e.out.push("?"),e.i+=1;return e.lastAtom="quant",!0}function f_(e){if(e.i+=1,e.out.push("["),e.raw[e.i]==="^")e.out.push("^"),e.i+=1;if(e.raw[e.i]==="]")e.out.push(RegExp.escape("]")),e.i+=1;while(e.i<e.raw.length&&e.raw[e.i]!=="]"){let t=e.raw[e.i]??"";if(t==="\\"){if(!Xm(e))return!1}else if(t==="-")e.out.push("-"),e.i+=1;else e.out.push(RegExp.escape(t)),e.i+=1}if(e.raw[e.i]!=="]")return!1;return e.out.push("]"),e.i+=1,e.lastAtom="atom",!0}function Xm(e){let t=e.raw[e.i+1];if(t===void 0)return!1;if(t==="d")e.out.push("\\d");else if(t==="D")e.out.push("\\D");else if(t==="w")e.out.push("\\w");else if(t==="W")e.out.push("\\W");else if(t==="s")e.out.push("\\s");else if(t==="S")e.out.push("\\S");else if(t==="n")e.out.push("\\n");else if(t==="t")e.out.push("\\t");else if(t==="r")e.out.push("\\r");else if(t==="b")e.out.push("\\b");else if(t==="B")e.out.push("\\B");else e.out.push(RegExp.escape(t));return e.i+=2,e.lastAtom="atom",!0}function Fo(e,t){if(e.services&&e.services.length>0&&!e.services.includes(t.service))return!1;if(e.level&&(fd[t.level]??2)<(fd[e.level]??2))return!1;if(e.source&&t.source!==e.source)return!1;if(e.since&&t.timestamp<e.since)return!1;if(e.until&&t.timestamp>e.until)return!1;return!0}function xd(e){if(!e.search)return()=>!0;if(e.regex){let r=Mi(e.search);if(r)return(n)=>r.test(n.message)||n.raw!==void 0&&r.test(n.raw)}let t=e.search.toLowerCase();return(r)=>r.message.toLowerCase().includes(t)||r.raw!==void 0&&r.raw.toLowerCase().includes(t)}function wd(e){let t=xd(e);return(r)=>Fo(e,r)&&t(r)}var Qm=I(()=>{Li()});function Zm(e){if(!Number.isInteger(e)||(e??0)<=0)return Bm;return Math.min(e,jm)}var eh=I(()=>{Li()});var ga=I(()=>{Li();Ym();Qm();eh()});function E(e,t){return new Ve(e,t)}function Pe(e,t,r){let n=r instanceof Error?r:Error(String(r));return new Ve(e,t,{cause:n})}function Te(e,t,r,n=""){return new Ve(e,t,{hint:r,service:n})}function ba(e,t){return e instanceof Ve&&e.kind===t}function uh(e){if(e instanceof Ve)return e.exitCode();return 1}function ph(e){if(e instanceof Ve)return{error:L(e),kind:e.kind,hint:e.hint,service:e.service};return{error:L(e)}}function fh(e){if(typeof e==="string")return E("general",e);if(e.kind)return new Ve(e.kind,e.error.replace(`${e.kind}: `,""),{hint:e.hint,service:e.service});return E("general",e.error)}function L(e){if(e instanceof Ve){if(e.hint!=="")return`${ch(e)} \u2014 ${e.hint}`;return ch(e)}if(e instanceof Error)return e.message;return String(e)}function ch(e){let t=`${e.kind}: `;if(e.message.startsWith(t)){let r=e.message.slice(t.length),n=e.causeError?`: ${e.causeError.message}`:"";if(n!==""&&r.endsWith(n))return r.slice(0,r.length-n.length);return r}return e.message}var lh=1,Y="configuration",qn="configuration_missing",Ho="service_not_found",dh="dependency",He="process_start",hn="authentication",Vn="authorization",Fr="token",va="impersonation",ya="iap",Nr="proxy",Td="health_check",lt="general",Ve;var te=I(()=>{Ve=class Ve extends Error{kind;hint;service;causeError;constructor(e,t,r){super(r?.cause?`${e}: ${t}: ${r.cause.message}`:`${e}: ${t}`);this.name="DevctlError",this.kind=e,this.hint=r?.hint??"",this.service=r?.service??"",this.causeError=r?.cause}exitCode(){switch(this.kind){case"configuration":case"configuration_missing":case"service_not_found":case"dependency":return 2;case"authentication":case"token":case"iap":return 3;case"authorization":case"impersonation":return 4;case"process_start":return 5;case"health_check":return 6;case"proxy":return 7;default:return 1}}}});function Pa(e){return e>0?e:300}function Dd(){return{enabled:!1,paths:[],debounce_ms:300,ignore:[...$i]}}function Id(){return{enabled:void 0,host:"",port:""}}function Fi(){return{type:"",identity:{type:"",service_account:""},audience:"",service_account:"",client_id:"",client_secret:"",credentials:""}}function Bo(){return{args:[],shell:!1}}function Md(){return{vars:{},required:[],defaults:{}}}function Od(){return{type:"",url:"",address:"",command:Bo(),interval_seconds:0,timeout_seconds:0,start_period_seconds:0,unhealthy_threshold:3,healthy_reset_threshold:10}}function zt(e){return typeof e==="string"?e:e.service}function gn(e){return typeof e==="string"?"service_started":e.condition||"service_started"}function Ra(e){return gn(e)==="service_healthy"?`${zt(e)} (healthy)`:zt(e)}function $d(){return{type:"",mode:"",service_account:"",config:{}}}function vn(){return{extends:"",description:"",command:Bo(),shell:!1,working_dir:"",dependencies:[],ports:[],environment:Md(),health:Od(),identity:$d(),logs:{stdout:!1,stderr:!1},restart:{policy:"",max_retries:0,backoff_seconds:0},startup:{wait_for_healthy:!1,timeout_seconds:0},capabilities:[],proxy:[],expose:Id(),container:void 0,watch:Dd(),hooks:{pre_start:Bo(),post_start:Bo()}}}function Yn(){return{version:1,project:{name:""},google:{project_id:"",region:""},profiles:{},templates:{},services:{},tasks:{},proxy:{enabled:!1,gateway:!1,credentials:"",listen:{host:_a,port:0},token_endpoint:{enabled:!1,host:"",port:0},routes:[]},logs:{max_memory_events:O_,persistence:{enabled:!0,directory:"~/.devctl/logs",retention_days:$_,max_session_logs:0}},auth:{refresh_threshold_seconds:vh},shutdown:{grace_seconds:gh},ui:{theme:"system",keymap:{}},secrets:{extra_markers:[],extra_patterns:[]},doctor:{tools:[]},plugins:[],environment:{sources:[],secrets:{}},provenance:{},repoRoot:"",configPath:""}}function yn(e){return e.args.length===0||e.args.length===1&&(e.args[0]??"").trim()===""}function Hi(e){if(e.type!=="")return e.type;return e.mode}function en(e){let t=Hi(e).toLowerCase();return t==="service"||t==="service_account"}function yh(e){let t=Hi(e).toLowerCase();return t==="user"||t===""}function Ea(e){if(e.policy!==""){if(e.policy==="always"||e.policy==="on_failure"||e.policy==="never")return e.policy;return e.policy}if(e.enabled===!0)return"on_failure";return"never"}function bn(e){let t=e.host===""?_a:e.host;if(e.port===0)return t;return`${t}:${e.port}`}function bh(e){if(e.refresh_threshold_seconds<=0)return vh;return e.refresh_threshold_seconds}function Ta(e){if(e.stop_services_on_exit===void 0)return!0;return e.stop_services_on_exit}function xn(e){if(e.grace_seconds<=0)return gh;return e.grace_seconds}function xh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stdout}function wh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stderr}function Jn(e,t){return e.find((r)=>r.name===t)}function Sh(e){return e[0]}var Uo=1,Sa="never",ka="on_failure",Ca="always",$i,Ni=8080,gh=10,vh=300,O_=50000,$_=14,_a="127.0.0.1";var ht=I(()=>{$i=["**/node_modules/**","**/.git/**"]});var Nt=".devctl",lr="config.yaml";import{basename as F_}from"path";function wn(e,t=""){return{name:F_(e),project:t,profile:"",sa:"",audience:"",proxyPort:Ni}}function Aa(e,t=Ni){let r=Number(e);return Number.isFinite(r)&&r>0?r:t}function kh(e){let t=e;return`# yaml-language-server: $schema=https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/schema/devctl.config.schema.json
|
|
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 $r(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 Fr(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(Fr(e,["http.method","method","request_method"])),r=Zt(Fr(e,["http.route","url.path","path","route","request_uri"])),n=Zt(Fr(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(Fr(e,["request_method","method","http_method","verb"]))??n.method,i=Zt(Fr(e,["request_uri","uri","url","path","request_path"]))??n.uri,s=$o(Fr(e,["status","status_code","statusCode","http_status"])),a=Zt(Fr(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(Fr(e,["body_bytes_sent","bytes_sent","bytes"])),l=$o(Fr(e,["request_time","duration"])),d=$o(Fr(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,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 _("general",e);if(e.kind)return new Ve(e.kind,e.error.replace(`${e.kind}: `,""),{hint:e.hint,service:e.service});return _("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",Pr="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
|
|
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 La=I(()=>{ht();wt=[{id:"repo",title:"Repository",prompt:"Repository root"},{id:"name",title:"Environment",prompt:"Environment / project name"},{id:"project",title:"Google project",prompt:"Google Cloud project"},{id:"auth",title:"Authentication",prompt:"Run gcloud ADC login now? (y/N)"},{id:"sa",title:"Service account",prompt:"Service account email to record (optional)"},{id:"audience",title:"IAP",prompt:"IAP audience to record on a sample route (optional)"},{id:"port",title:"Ports",prompt:"Proxy listen port"},{id:"profile",title:"Profiles",prompt:"Default profile name (optional)"},{id:"write",title:"Validation",prompt:"Write starter configuration"}]});import{existsSync as Hd,mkdirSync as Fd,statSync as N_,writeFileSync as H_}from"fs";import{createInterface as j_}from"readline/promises";import{stdin as B_,stdout as U_}from"process";import{basename as Da,dirname as Nd,join as tn,resolve as W_}from"path";function jd(e,t){let r=e===""?process.cwd():e;if(t==="")return{repo:r,cfgPath:tn(r,Nt,lr)};let n=W_(t);if(!(Hd(n)?N_(n).isDirectory():!/\.ya?ml$/i.test(n))){let i=Nd(n);return{repo:Da(i)===Nt?Nd(i):i,cfgPath:n}}if(Da(n)===Nt)return{repo:Nd(n),cfgPath:tn(n,lr)};return{repo:n,cfgPath:tn(n,Nt,lr)}}function Ch(e,t=Da(e),r="",n="",o=!1){let i=tn(e,Nt,lr);if(!o&&Hd(i))throw
|
|
51
|
-
`)}var Ma=I(()=>{ht();La();te()});function xg(){return/\$\{(?:env\.)?([A-Za-z_][A-Za-z0-9_]*)\}/g}function tu(e){return[...e.matchAll(xg())].map((t)=>t[1]??"")}function wg(e,t){let r=[];return{value:e.replace(xg(),(o,i)=>{let s=t[i];if(s===void 0||s==="")return r.push(i),"";return s}),missing:r}}function Ba(e){let t=e.trim();if(t===""||tu(t).length===0)return;return t}function Sg(e){return/^\$\{(?:env\.)?[A-Za-z_][A-Za-z0-9_]*\}$/.test(e.trim())}function Pn(e){return Object.entries(e.provenance).sort(([t],[r])=>t.localeCompare(r)).flatMap(([t,r])=>{let n=r[r.length-1];if(!n)return[];return[{path:t,value:tE(e,t),...n,shadowed:r.slice(0,-1)}]})}function tE(e,t){let r=e;for(let n of t.split(".")){if(typeof r!=="object"||r===null)return;r=r[n]}return r}function qa(e){if(en(e))return dr({kind:ro,serviceAccount:e.service_account});if(yh(e)&&(e.type!==""||e.mode!==""))return dr({kind:Ka});let t=(e.type||e.mode).toLowerCase();if(t!=="")return dr({kind:t,providerConfig:e.config??{}});return dr({kind:Go})}function no(e){if(e.type.toLowerCase()==="none")return dr({kind:Go});let t=e.identity.type.toLowerCase(),r=e.identity.service_account||e.service_account;if(t==="service"||t==="service_account"||e.type==="service_account")return dr({kind:ro,serviceAccount:r});if(e.type.toLowerCase()==="iap"&&t==="")return dr({kind:Go});if(e.type===""&&t==="")return dr({kind:Go});return dr({kind:Ka})}function Lg(e){return e.kind===Ka||e.kind===ro}function Ko(e){if(e.tokenKey!=="")return e.tokenKey;if(e.kind===ro)return`sa:${e.serviceAccount}`;if(e.email!=="")return`user:${e.email}`;return"user"}function dr(e={}){return{kind:Go,email:"",serviceAccount:"",project:"",projectSource:"",adcAvailable:!1,providerConfig:{},tokenKey:"",...e}}function iu(e){let t=new Map,r=(n,o)=>{if(n!=="")t.set(n,(t.get(n)??!1)||o)};for(let n of Object.values(e.services))if(en(n.identity)&&n.identity.service_account!=="")r(n.identity.service_account,!0);for(let n of e.proxy.routes){let o=n.auth.type.toLowerCase(),i=n.auth.identity.type.toLowerCase(),s=n.auth.identity.service_account||n.auth.service_account;if(i==="service"||i==="service_account"||o==="service_account")r(s,o!=="none")}return[...t.entries()].map(([n,o])=>({email:n,active:o})).sort((n,o)=>n.email.localeCompare(o.email))}function Hr(e){return iu(e).filter((t)=>t.active).map((t)=>t.email)}function Dg(e,t,r){let n=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=qa(i.identity);if(en(i.identity)&&i.identity.service_account===""){n.push({name:o,message:"service account identity is not configured"});continue}if((Lg(s)||i.capabilities.some((c)=>c==="google_api"||c==="iap"||c==="service_identity"))&&!r)n.push({name:o,message:"ADC unavailable"})}return n}function Gi(e){if(Hr(e).length>0)return!0;if(e.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap"))return!0;return Object.values(e.services).some((t)=>{if(Lg(qa(t.identity)))return!0;return t.capabilities.some((r)=>r==="google_api"||r==="iap"||r==="service_identity")})}function su(){return{name:"user",accepts:(e)=>!en(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return dr({kind:Go});return{...await t(),kind:Ka}}}}function au(){return{name:"service_account",accepts:(e)=>en(e),resolve:async(e,t)=>{if(e.service_account==="")throw
|
|
50
|
+
`}var wt;var La=I(()=>{ht();wt=[{id:"repo",title:"Repository",prompt:"Repository root"},{id:"name",title:"Environment",prompt:"Environment / project name"},{id:"project",title:"Google project",prompt:"Google Cloud project"},{id:"auth",title:"Authentication",prompt:"Run gcloud ADC login now? (y/N)"},{id:"sa",title:"Service account",prompt:"Service account email to record (optional)"},{id:"audience",title:"IAP",prompt:"IAP audience to record on a sample route (optional)"},{id:"port",title:"Ports",prompt:"Proxy listen port"},{id:"profile",title:"Profiles",prompt:"Default profile name (optional)"},{id:"write",title:"Validation",prompt:"Write starter configuration"}]});import{existsSync as Hd,mkdirSync as Fd,statSync as N_,writeFileSync as H_}from"fs";import{createInterface as j_}from"readline/promises";import{stdin as B_,stdout as U_}from"process";import{basename as Da,dirname as Nd,join as tn,resolve as W_}from"path";function jd(e,t){let r=e===""?process.cwd():e;if(t==="")return{repo:r,cfgPath:tn(r,Nt,lr)};let n=W_(t);if(!(Hd(n)?N_(n).isDirectory():!/\.ya?ml$/i.test(n))){let i=Nd(n);return{repo:Da(i)===Nt?Nd(i):i,cfgPath:n}}if(Da(n)===Nt)return{repo:Nd(n),cfgPath:tn(n,lr)};return{repo:n,cfgPath:tn(n,Nt,lr)}}function Ch(e,t=Da(e),r="",n="",o=!1){let i=tn(e,Nt,lr);if(!o&&Hd(i))throw _(Y,`configuration already exists at ${i}; not overwriting`);return Ia(e,{...Sn(e,r),name:t,profile:n}),i}async function Ph(e,t,r="",n=!1){let{repo:o,cfgPath:i}=jd(t,r);if(!n&&Hd(i)){Ht(`Found existing configuration at ${i}; nothing written.`),Ht("Pass --force to overwrite it, or edit it directly.");return}let s=j_({input:B_,output:U_}),a=async(h,v)=>{let b=v!==""?` [${v}]`:"",g=(await s.question(`${h}${b}: `)).trim();return g===""?v:g},c=0,l=()=>(c+=1,c);if(Ht(`devctl setup \u2014 ${wt.length} steps`),Ht(""),r==="")await a(`${l()}. Repository root`,o);let d=Sn(o,""),u=await a(`${l()}. ${wt[1]?.prompt}`,Da(o));d.name=u;let f=await e.detectGoogle(""),p=await a(`${l()}. ${wt[2]?.prompt}`,f.projectID);if(d.project=p,Ht(`${l()}. ${wt[3]?.title}`),!f.adcAvailable){if(Ht(" ADC is not available. Run: gcloud auth application-default login"),(await a(" Run login now? (y/N)","n")).toLowerCase()==="y")try{await e.loginGoogle()}catch(v){Ht(` login failed: ${v instanceof Error?v.message:String(v)}`)}}else if(Ht(" \u2713 Application Default Credentials available"),f.userEmail!=="")Ht(` user: ${f.userEmail}`);d.sa=await a(`${l()}. ${wt[4]?.prompt}`,""),d.audience=await a(`${l()}. ${wt[5]?.prompt}`,"");let m=await a(`${l()}. ${wt[6]?.prompt}`,String(Ni));d.proxyPort=Aa(m),d.profile=await a(`${l()}. ${wt[7]?.prompt}`,""),s.close(),Ia(o,d),Ht(""),Ht(`Wrote starter configuration to ${i}`),Ht(`${l()}. Validation`);try{let h=e.load(o,""),v=await e.runDoctor.execute(h);Ht(e.formatDoctor(v))}catch(h){Ht(` configuration is not valid yet: ${h instanceof Error?h.message:String(h)}`),Ht(" edit .devctl/config.yaml and run `devctl config validate`")}}function Ia(e,t){let r=tn(e,".devctl");Fd(tn(r,"services"),{recursive:!0}),Fd(tn(r,"profiles"),{recursive:!0}),Fd(tn(r,"proxy"),{recursive:!0}),H_(tn(r,"config.yaml"),kh(t))}function Ht(e){process.stdout.write(`${e}
|
|
51
|
+
`)}var Ma=I(()=>{ht();La();te()});function xg(){return/\$\{(?:env\.)?([A-Za-z_][A-Za-z0-9_]*)\}/g}function tu(e){return[...e.matchAll(xg())].map((t)=>t[1]??"")}function wg(e,t){let r=[];return{value:e.replace(xg(),(o,i)=>{let s=t[i];if(s===void 0||s==="")return r.push(i),"";return s}),missing:r}}function Ba(e){let t=e.trim();if(t===""||tu(t).length===0)return;return t}function Sg(e){return/^\$\{(?:env\.)?[A-Za-z_][A-Za-z0-9_]*\}$/.test(e.trim())}function Pn(e){return Object.entries(e.provenance).sort(([t],[r])=>t.localeCompare(r)).flatMap(([t,r])=>{let n=r[r.length-1];if(!n)return[];return[{path:t,value:tE(e,t),...n,shadowed:r.slice(0,-1)}]})}function tE(e,t){let r=e;for(let n of t.split(".")){if(typeof r!=="object"||r===null)return;r=r[n]}return r}function qa(e){if(en(e))return dr({kind:ro,serviceAccount:e.service_account});if(yh(e)&&(e.type!==""||e.mode!==""))return dr({kind:Ka});let t=(e.type||e.mode).toLowerCase();if(t!=="")return dr({kind:t,providerConfig:e.config??{}});return dr({kind:Go})}function no(e){if(e.type.toLowerCase()==="none")return dr({kind:Go});let t=e.identity.type.toLowerCase(),r=e.identity.service_account||e.service_account;if(t==="service"||t==="service_account"||e.type==="service_account")return dr({kind:ro,serviceAccount:r});if(e.type.toLowerCase()==="iap"&&t==="")return dr({kind:Go});if(e.type===""&&t==="")return dr({kind:Go});return dr({kind:Ka})}function Lg(e){return e.kind===Ka||e.kind===ro}function Ko(e){if(e.tokenKey!=="")return e.tokenKey;if(e.kind===ro)return`sa:${e.serviceAccount}`;if(e.email!=="")return`user:${e.email}`;return"user"}function dr(e={}){return{kind:Go,email:"",serviceAccount:"",project:"",projectSource:"",adcAvailable:!1,providerConfig:{},tokenKey:"",...e}}function iu(e){let t=new Map,r=(n,o)=>{if(n!=="")t.set(n,(t.get(n)??!1)||o)};for(let n of Object.values(e.services))if(en(n.identity)&&n.identity.service_account!=="")r(n.identity.service_account,!0);for(let n of e.proxy.routes){let o=n.auth.type.toLowerCase(),i=n.auth.identity.type.toLowerCase(),s=n.auth.identity.service_account||n.auth.service_account;if(i==="service"||i==="service_account"||o==="service_account")r(s,o!=="none")}return[...t.entries()].map(([n,o])=>({email:n,active:o})).sort((n,o)=>n.email.localeCompare(o.email))}function Hr(e){return iu(e).filter((t)=>t.active).map((t)=>t.email)}function Dg(e,t,r){let n=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=qa(i.identity);if(en(i.identity)&&i.identity.service_account===""){n.push({name:o,message:"service account identity is not configured"});continue}if((Lg(s)||i.capabilities.some((c)=>c==="google_api"||c==="iap"||c==="service_identity"))&&!r)n.push({name:o,message:"ADC unavailable"})}return n}function Gi(e){if(Hr(e).length>0)return!0;if(e.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap"))return!0;return Object.values(e.services).some((t)=>{if(Lg(qa(t.identity)))return!0;return t.capabilities.some((r)=>r==="google_api"||r==="iap"||r==="service_identity")})}function su(){return{name:"user",accepts:(e)=>!en(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return dr({kind:Go});return{...await t(),kind:Ka}}}}function au(){return{name:"service_account",accepts:(e)=>en(e),resolve:async(e,t)=>{if(e.service_account==="")throw _(va,"service account identity is not configured");return{...await t(),kind:ro,serviceAccount:e.service_account}}}}async function Ig(e,t,r){if(r)for(let n=r.length-1;n>=0;n-=1){let o=r[n];if(o?.accepts(e))return o.resolve(e,t)}if(en(e))return au().resolve(e,t);return su().resolve(e,t)}var Ka="user",ro="service_account",Go="none";var jr=I(()=>{ht();te()});function er(){return"devctl 0.4.1"}var jt="0.4.1",Yi=1;function Qg(e){return e}function ur(e){return e}function Jo(e){if(e.state===je&&e.health===Bt)return pr;if(e.state===je&&e.health===Oe)return Br;return e.state}function Qi(e){return{name:e,state:Et,health:Tt,pid:0,ports:{},restarts:0,last_error:"",identity:"",startTime:void 0,cpuPercent:void 0,memoryKB:void 0,profile:"",env_source:"daemon",orphaned:!1}}function tv(e,t){let r={},n={};for(let a of Object.keys(t))r[a]=0;for(let a of Object.keys(t)){let c=e.services[a]?.dependencies??[];for(let l of c){let d=zt(l);if(!t[d])continue;n[d]=[...n[d]??[],a],r[a]=(r[a]??0)+1}}let o=[],i=Object.keys(t).length,s={};while(i>0){let a=Object.entries(r).filter(([c,l])=>l===0&&!s[c]).map(([c])=>c).sort();if(a.length===0)throw _(dh,"dependency cycle detected while planning startup");o.push(a);for(let c of a){s[c]=!0,i-=1;for(let l of n[c]??[])r[l]=(r[l]??0)-1}}return o}function xu(e,t){if(!e.services[t])throw _(Ho,`unknown service "${t}"`)}function nc(e,t,r){let n={},o=(a)=>{if(n[a])return;xu(e,a),n[a]=!0;for(let c of e.services[a]?.dependencies??[])o(zt(c))};for(let a of t)o(a);let i=tv(e,n),s=[];return i.forEach((a,c)=>{for(let l of a)s.push({name:l,wave:c+1,dependencies:[...e.services[l]?.dependencies??[]]})}),{profile:r,steps:s,waves:i,blockers:[]}}function co(e,t){let r={};for(let[i,s]of Object.entries(e.services))for(let a of s.dependencies){let c=zt(a);r[c]=[...r[c]??[],i]}let n={},o=(i)=>{if(n[i])return;xu(e,i),n[i]=!0;for(let s of r[i]??[])o(s)};for(let i of t)o(i);return Object.keys(n)}function rv(e,t){let r=[...tv(e,t)].reverse().map((o)=>[...o].sort()),n=[];return r.forEach((o,i)=>{for(let s of o)n.push({name:s,wave:i+1,dependencies:[...e.services[s]?.dependencies??[]]})}),{steps:n,waves:r,profile:"",blockers:[]}}function oc(e,t){let r=Object.fromEntries(co(e,t).map((n)=>[n,!0]));return rv(e,r)}function ic(e,t){let r={};for(let n of t)xu(e,n),r[n]=!0;return rv(e,r)}function nv(e){let t="";if(e.profile!=="")t+=`Profile: ${e.profile}
|
|
52
52
|
|
|
53
53
|
`;t+=`Plan:
|
|
54
54
|
|
|
@@ -56,7 +56,7 @@ 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 sc(e){return`${e.join(", ")} changed and only take effect after a full \`devctl down && devctl start\`, not a reload`}function
|
|
59
|
+
`}return t}function sc(e){return`${e.join(", ")} changed and only take effect after a full \`devctl down && devctl start\`, not a reload`}function nT(e,t,r){let n={};if(t!==""){let o=e.profiles[t];if(!o)throw _("configuration",`unknown profile "${t}"`);Object.assign(n,o.environment)}if(r.length>0)return{services:Zg(e,r),env:n};if(t==="")return{services:Object.keys(e.services),env:n};return{services:Zg(e,e.profiles[t]?.services??[]),env:n}}function oT(e){let t=Object.keys(e.profiles).sort();return ur(t[0]??"")}function lo(e,t){if(t==="")return{};return{...e.profiles[ur(t)]?.environment??{}}}function ac(e,t){let r=t.services??[],n=t.profile??ur("");if(r.length===0&&n===""){if(n=t.activeProfile||oT(e),n==="")throw _(Y,"no profile or services given; pass service names or add a profile")}let o=nT(e,n,r);return{services:o.services,env:o.env,profile:n}}function Zg(e,t){let r={},n=[];for(let o of t){if(r[o])continue;if(!e.services[o])throw _("service_not_found",`unknown service "${o}"`);r[o]=!0,n.push(o)}return n}var ev="UNKNOWN",sn="STARTING",je="RUNNING",pr="HEALTHY",Br="UNHEALTHY",Rr="STOPPING",Et="STOPPED",dt="FAILED",fr="RESTARTING",Tt="UNKNOWN",Bt="HEALTHY",Oe="UNHEALTHY";var At=I(()=>{ht();te()});function Av(e){if(e.newer)return`update available ${e.current} \u2192 ${e.latest} ${e.hint}`;if(e.latest!=="")return`${e.current} up to date`;return`${e.current} (latest unavailable)`}function Lv(e){let t=Ev(e.scriptPath),r=Ev(e.execPath);if(!e.standalone&&t.includes("/@amr-m-abdelgawad/devctl/")){if(t.includes("/_npx/"))return{kind:"npx",hint:"npx @amr-m-abdelgawad/devctl@latest"};let n=IT();return{kind:"npm",command:n,hint:n.join(" ")}}if(e.standalone&&r.toLowerCase().includes("/cellar/devctl/"))return{kind:"homebrew",command:["brew","reinstall","--formula","https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/homebrew/devctl.rb"],hint:"brew reinstall --formula https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/homebrew/devctl.rb"};if(e.standalone)return{kind:"release",hint:"re-download the matching GitHub Release binary \u2014 see docs/installation.md"};if(t.endsWith("/src/bin.ts"))return{kind:"source",hint:"git pull && cd app && bun install"};return{kind:"unknown",hint:`npm install --global ${"@amr-m-abdelgawad/devctl"}@latest # alternatives: Homebrew or the unsigned GitHub Release binaries \u2014 see docs/installation.md`}}function Dv(e,t){let r=Tv(e),n=Tv(t);for(let o=0;o<3;o+=1){let i=(r[o]??0)-(n[o]??0);if(i!==0)return i>0?1:-1}return 0}function IT(){return[process.platform==="win32"?"npm.cmd":"npm","install","--global","@amr-m-abdelgawad/devctl@latest"]}function Ev(e){return e.replaceAll("\\","/")}function Tv(e){return e.replace(/^v/,"").split(".").slice(0,3).map((t)=>{let r=Number.parseInt(t.replace(/[^0-9].*$/,""),10);return Number.isFinite(r)?r:0})}var mc="restart the daemon with `devctl down` then start again";function Rn(e){if(!Number.isInteger(e))return Qu;return Math.min(bA,Math.max(xA,e))}function Ty(e,t){if(t.length!==1||t<"0"||t>"9")return e;if(e.length>=wA)return t;return`${e}${t}`}function Ay(e){return e.slice(0,-1)}function Ly(e,t){if(e==="")return Rn(t);let r=Number(e);if(!Number.isInteger(r))return Rn(t);return Rn(r)}function fo(e){let t=Number.parseInt(ar(e).slice(0,yA),16),r=Number.isFinite(t)?t%Ey:0;return Qu+r}function Dy(e,t){return t===fo(e)}var Qu=18700,Ey=600,Jj,yA=8,bA=65535,xA=1024,wA=5;var Zu=I(()=>{Xs();Jj=Qu+Ey-1});class mr{nameMarkers;patterns;constructor(e,t){this.nameMarkers=[...jy,...e],this.patterns=t.flatMap((r)=>{try{return[new RegExp(r)]}catch{return[]}})}update(e,t){this.nameMarkers=[...jy,...e],this.patterns=t.flatMap((r)=>{try{return[new RegExp(r)]}catch{return[]}})}isSecretName(e){let t=e.toUpperCase();return this.nameMarkers.some((r)=>t.includes(r.toUpperCase()))}redactValue(e,t){if(t==="")return t;if(this.isSecretName(e))return"********";if(this.patterns.some((r)=>r.test(t)))return"********";return t}redactMap(e){let t={};for(let[r,n]of Object.entries(e))t[r]=this.redactValue(r,n);return t}redactText(e){if(e==="")return e;let t=e;for(let r of this.patterns)t=t.replace(r,"********");return DA(t)}}function DA(e){let t=e.toLowerCase(),r="authorization: bearer ",n=t.indexOf("authorization: bearer ");if(n>=0)return By(e,n+22);let o=t.indexOf("bearer ");if(o<0)return e;return By(e,o+7)}function By(e,t){let r=t;while(r<e.length&&!IA(e[r]??""))r+=1;return e.slice(0,t)+"********"+e.slice(r)}function IA(e){return e===" "||e==="\t"||e===`
|
|
60
60
|
`||e==="\r"}var jy;var ni=I(()=>{jy=["PASSWORD","SECRET","TOKEN","PRIVATE_KEY","CLIENT_SECRET","API_KEY","CREDENTIAL","ACCESS_KEY","AUTH_KEY"]});var Tn;var sp=I(()=>{Tn={procedure:`---
|
|
61
61
|
name: devctl-onboard
|
|
62
62
|
description: Survey a repository \u2014 its runnable services, docker-compose, Terraform, Kubernetes manifests, .env files, task runners \u2014 and author a validated .devctl configuration for it. Use when asked to onboard a repo to devctl, add or fix .devctl/config.yaml, generate devctl services/profiles/proxy routes, or diagnose why a devctl config fails to load.
|
|
@@ -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` |\n| `logs` | `max_memory_events` `persistence` |\n| `logs.persistence` | `enabled` `directory` `retention_days` `max_session_logs` |\n| `auth` | `refresh_threshold_seconds` |\n| `shutdown` | `stop_services_on_exit` `grace_seconds` |\n| `ui` | `theme` `keymap` |\n| `secrets` | `extra_markers` `extra_patterns` |\n| `doctor` | `tools` (each `{ name, command }`) |\n| `plugins[]` | `path` |\n| `environment` | `sources` `secrets` |\n\nThere is no `depends_on`, `build`, `replicas`, or `env_file`. Container fields\nbelong under `service.container`, not directly on the service.\n\n---\n\n## Required, at minimum\n\n- `version: 1`. Anything else: *unsupported config version N; no migration is\n available.*\n- **At least one service.** An empty or missing `services` map fails with *at\n least one service must be defined* \u2014 so a proxy-only or profiles-only config\n is not valid.\n- Every host service needs a non-empty `command`; a container service may rely on its image command.\n\n---\n\n## Ports\n\n- **Globally unique across all services.** Two services on `8000` gives\n *duplicate port 8000 used by api and worker*. This is the single most common\n failure when onboarding a repo whose services each assumed they owned the\n default port.\n- Range 1\u201365535.\n- Forms accepted: a named map (`ports: { http: 8000 }`, preferred), a list, a\n bare number, or `auto`. `auto` is exempt from the uniqueness check \u2014\n devctl allocates at start.\n- Name the port `http` when it serves HTTP.\n- Prefer the explicit `${services.X.ports.http}` reference form. The short\n `${services.X.port}` form is only unambiguous for a single-port service: once\n the service is running it prefers the assigned port named `http`, but when it\n resolves against the configuration it takes the **first port declared**,\n whatever its name. Two forms of the same reference disagreeing on a\n multi-port service is a genuinely confusing bug to chase.\n\n## References\n\n`${services.<name>.ports.<portname>}` and `${services.<name>.port}` are the\nsupported forms. Anything else \u2014 `${env.FOO}`, `${project.name}` \u2014 throws.\n\n- The referenced service must exist and the named port must be defined, or\n validation fails with *unresolvable reference*.\n- References resolve inside service `environment` values, `defaults`, profile\n environments and dotenv values, before the process starts.\n- Use them for every cross-service URL. Hard-coded ports silently break when a\n port changes or is switched to `auto`.\n\n## Dependencies\n\n- Must name a service that exists.\n- A service may not depend on itself.\n- Cycles are rejected: *dependency cycle: a \u2192 b \u2192 a*.\n- Direction: `dependencies` means \"start these first\". `start x` walks **up**\n and starts x's dependencies; `stop x` walks **down** and stops x's\n *dependents*, never its dependencies.\n- A string dependency uses `service_started`. Use `{ service: db, condition:\n service_healthy }` to wait for the dependency's configured health check.\n\n## Commands and `shell`\n\nA command whose argv contains any of `|` `||` `&&` `;` `>` `>>` `<` `&` is\nrejected unless `shell: true`:\n\n```yaml\n# rejected\ncommand: \"npm run build && npm start\"\n\n# correct\ncommand: \"npm run build && npm start\"\nshell: true\n\n# better \u2014 no shell needed\ncommand: [npm, start]\n```\n\nPrefer argv lists. A string command is split on whitespace, which mangles\narguments containing spaces.\n\n## `working_dir`\n\nRelative to the **repository root** \u2014 the directory containing `.devctl` \u2014 not\nthe process cwd, and not the `.devctl` directory itself.\n\n---\n\n## Health\n\n| `type` | Also requires |\n|---|---|\n| `http` | `url` \u2014 omit it and you get *health.url is required for http health checks* |\n| `tcp` | `address`, **or** at least one port defined on the service |\n| `command` | `health.command` (non-empty) |\n| `process` or omitted | nothing; only checks the pid is alive |\n\nAny other `type` is rejected \u2014 *health.type must be http, tcp, process, or\ncommand* \u2014 **unless** `plugins` is non-empty. That exemption is not approval:\nplugins load after validation, so the supervisor re-checks the type at boot and\nat reload. A custom type no plugin provides fails as a *reload rejection*, not\na config error, which is much harder to diagnose. Only use a custom type when\nyou can point at the plugin that registers it.\n\nDefaults when omitted: interval 2s, timeout 2s, start period 0s, unhealthy\nthreshold 3, healthy reset threshold 10. Failures during the start period do\nnot mark the service unhealthy or consume restart budget.\n\n## Identity\n\n- `type: user` (or omitted) needs nothing else.\n- `type: service_account` (or `service`) **requires** `service_account`, and it\n must contain `@` \u2014 *service_account must be an email*.\n- `mode` is an accepted alias of `type`.\n- Any other type behaves like a custom health type: allowed only with\n `plugins` set, re-checked at boot.\n\nNever fabricate an SA email. If Terraform interpolates it beyond what you can\nresolve, leave a clearly-marked placeholder and tell the user.\n\n## Restart\n\n`policy` must be `never`, `on_failure`, or `always`. `enabled: true` with no\npolicy behaves as `on_failure`.\n\n`max_retries` is a real budget: it resets on a manual stop/start/restart and\nafter a sustained healthy run, but an automatic health-triggered restart\npreserves it across its own cycle.\n\n## Capabilities\n\nDocumentation for doctor, not behaviour. Only these are accepted:\n`google` `google_api` `iap` `network` `service_identity` `local_http`.\nAnything else is rejected.\n\n---\n\n## Proxy\n\n- `proxy.listen.port` is **required** when `proxy.enabled: true`.\n- `proxy.listen.host` must be loopback (`127.0.0.1`, `localhost`, `::1`, or `127.0.0.0/8`). `0.0.0.0` and `::` are rejected.\n- `proxy.token_endpoint.host` must be loopback; `0.0.0.0` and `::` are rejected.\n- Every route needs a `name` and either `upstream.url` or `upstream.service`.\n A service reference must name a real service and an existing port (default\n port name is `http`). `service.expose` and `proxy.gateway` synthesize\n service-reference routes; they do nothing unless `proxy.enabled` is true.\n A hand-written route of the same name wins.\n- **Route names must be unique** \u2014 including names generated from per-service\n `proxy` fragments.\n- `auth.type: iap` requires **both** `audience` and `auth.identity.type`. A\n missing identity type on an IAP route is a configuration error, not a\n default.\n- Optional `client_id` plus `client_secret` mints the user IAP ID token with\n that OAuth client instead of ADC's default client. Put `${NAME}` or\n `${env.NAME}` in `client_secret` so the value is read from the environment\n at mint time (this is not service-env interpolation). These fields are\n invalid on non-IAP routes and on IAP routes whose identity is a service\n account.\n- An identity of `service` / `service_account` requires an SA email, from\n either `auth.identity.service_account` or the route's `auth.service_account`.\n- `auth.type: none` means no auth at all \u2014 any identity left on such a route is\n ignored entirely, and will not be probed at start or by doctor.\n\n### Per-service route fragments\n\nA service's `proxy` key is one route or a list. At load they append into the\n**same** global `proxy.routes` list, named `<service>` or `<service>-<n>`:\n\n```yaml\nservices:\n api:\n command: [python3, main.py]\n proxy:\n - match: { path: /api }\n upstream: { url: \"http://127.0.0.1:8000\" }\n```\n\nThat generates a route named `api`. So a service named `api` **and** a global\nroute named `api` collide \u2014 *duplicate route name api*. Watch for this when\nmixing both styles.\n\nMatching is host + optional path prefix. There is no path rewriting or\nstripping: the matched path is forwarded to the upstream as-is.\n\n---\n\n## Modular layout\n\nWhen the main file is `.devctl/config.yaml`, these merge in automatically:\n\n```\n.devctl/services/<name>.yaml \u2192 services.<name> (FILENAME is the key)\n.devctl/profiles/<name>.yaml \u2192 profiles.<name>\n.devctl/proxy/routes.yaml \u2192 proxy.routes (see the wrapper below)\n```\n\nA service or profile file contains that object's body **only** \u2014 no `services:`\nwrapper, no `version:`. Getting this wrong reads oddly: a `services:` wrapper\ninside `.devctl/services/api.yaml` reports\n`unknown fields: services.api.services`.\n\n`proxy/routes.yaml` is the exception: it accepts either a full `proxy:` wrapper\nor a bare top-level `routes:` list. Both append to the same global route list.\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 wrapper form, also lets you set proxy.listen here\nproxy:\n routes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 bare form, equivalent for routes alone\nroutes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\nPick one. Both keys in the same file append **both** lists, which trips the\nduplicate-route-name check.\n\nOverlays merge after the repo config, later wins:\nbuilt-in defaults \u2192 repo `.devctl` \u2192 `~/.devctl/config.local.yaml` \u2192\n`.devctl/config.local.yaml` \u2014 the repo-local overlay wins over the home one.\nOverlays are presence-aware:\n`false`, `0` and\nempty collections in an overlay do override, so `proxy.enabled: false` in a\nlocal overlay genuinely turns the proxy off.\n\n---\n\n## Environment\n\nMerge order \u2014 later sources win:\n\n```\nprocess \u2192 profile \u2192 dotenv \u2192 generated \u2192 keychain \u2192 secret_manager \u2192 defaults \u2192 vars \u2192 runtime\n```\n\n`process`, `defaults`, `vars` and `runtime` always run. Listing\n`environment.sources` **adds** optional sources (`profile`, `dotenv`,\n`generated`, `keychain`, `secret_manager`) to that always-on set \u2014 it does not\nreplace it, and it does not reorder anything.\n\n- `dotenv` reads repo root then `working_dir`: `.env`, `.env.development`,\n `.env.local`, `.env.<profile>`.\n- `keychain` and `secret_manager` throw when listed and the fetch fails, so\n only list them when the repo genuinely uses them.\n- `environment.required` on a service fails the start if those keys are still\n empty after the whole merge \u2014 the right place to encode \"this cannot run\n without X\".\n\nRuntime values devctl injects: `SERVICE_PORT`, `SERVICE_HOST`,\n`DEVCTL_PROXY_URL`, `DEVCTL_SERVICE_NAME`, `DEVCTL_ENVIRONMENT`,\n`DEVCTL_TOKEN_URL`, `DEVCTL_INTERNAL_TOKEN`. Do not define these yourself.\n\n---\n\n## Templates\n\n```yaml\ntemplates:\n python-http:\n health: { type: http, interval_seconds: 2, timeout_seconds: 1 }\n logs: { stdout: true, stderr: true }\n restart: { policy: on_failure, max_retries: 2, backoff_seconds: 1 }\n\nservices:\n api:\n extends: python-http\n command: [python3, main.py]\n health: { url: \"http://127.0.0.1:8000/health\" } # merged field-by-field\n```\n\n- `extends` must name a template that exists.\n- Nested sections merge **field by field**, so a service can override one\n health field without restating the rest.\n- A template is not a service and is never started.\n\n---\n\n## Reading validation output\n\nEvery message names its path. Fix the path it names.\n\n| Message | Meaning |\n|---|---|\n| `at least one service must be defined` | empty/missing `services` |\n| `duplicate port N used by X and Y` | two services pinned the same port |\n| `services.X.command is required` | missing or empty command |\n| `services.X.command contains shell metacharacters` | add `shell: true` or use argv |\n| `services.X.dependencies: unknown service \"Y\"` | typo, or Y lives in a file you did not create |\n| `dependency cycle: a \u2192 b \u2192 a` | remove an edge |\n| `services.X.health.url is required for http health checks` | add `url`, or change the type |\n| `services.X.identity.service_account must be an email` | placeholder left unresolved |\n| `services.X.environment.K: unresolvable reference ${\u2026}` | referenced service or port name does not exist |\n| `services.X.capabilities: unknown capability \"c\"` | only the six listed above are accepted |\n| `profiles.P references unknown service \"S\"` | profile lists a service that is not defined |\n| `proxy.routes[i]: duplicate route name N` | often a per-service fragment colliding with a global route |\n| `proxy.routes[i].auth.audience is required when auth.type is iap` | IAP needs both audience and identity.type |\n| `proxy.routes[i].auth.client_id is only valid when auth.type is iap` | `client_id` / secret only apply to IAP user routes |\n| `proxy.routes[i].auth.client_id is required when client_secret is set` | secret without client_id |\n| `proxy.routes[i].auth.client_secret is required when client_id is set` | client_id needs a secret |\n| `proxy.routes[i].auth.client_id is only valid with identity.type user` | SA IAP uses generateIdToken, not a user OAuth client |\n| `proxy.listen.port is required when proxy.enabled is true` | pin a port |\n| `unsupported config version N (expected 1)` | `version:` must be `1` |\n| `unknown fields: services.a.depends_on` | not in the allowlists at the top of this file \u2014 usually a compose or k8s spelling |\n\nEvery message above is the validator's exact wording. `devctl config validate`\nprints one per line and exits 2; a clean run prints `configuration is valid`\nand exits 0.\n",discovery:`# Discovery: repository signal \u2192 devctl service
|
|
370
|
+
`,authoring:"# Authoring rules the loader enforces\n\nThe JSON Schema (`schema/devctl.config.schema.json`) describes the shape.\nThis file describes what the **loader and validator actually reject** \u2014 the\nrules that turn a schema-plausible config into one that fails at\n`devctl config validate`. Check anything you are unsure of here before writing.\n\n---\n\n## Unknown fields are rejected everywhere\n\nNot warned about \u2014 rejected, with the offending path named. These are the\ncomplete allowlists.\n\n**Top level:** `version` `project` `google` `profiles` `templates` `services`\n`tasks` `proxy` `logs` `auth` `shutdown` `ui` `secrets` `doctor` `plugins`\n`environment`\n\n**Service** (and `templates.<name>`, same shape): `extends` `description`\n`command` `shell` `working_dir` `dependencies` `ports` `environment` `health`\n`identity` `logs` `restart` `startup` `capabilities` `proxy` `expose` `container` `watch` `hooks`\n\n| Section | Allowed keys |\n|---|---|\n| `project` | `name` |\n| `google` | `project_id` `region` |\n| `profiles.<name>` | `services` `environment` |\n| `service.health` | `type` `url` `address` `command` `interval_seconds` `timeout_seconds` `start_period_seconds` `unhealthy_threshold` `healthy_reset_threshold` |\n| `service.hooks` | `pre_start` `post_start` |\n| `service.container` | `image` `runtime` `ports` `env` `volumes` |\n| `service.watch` | `enabled` `paths` `debounce_ms` `ignore` |\n| `tasks.<name>` | `command` `shell` `working_dir` `dependencies` `environment` |\n| `service.identity` | `type` `mode` `service_account` |\n| `service.restart` | `enabled` `policy` `max_retries` `backoff_seconds` |\n| `service.startup` | `wait_for_healthy` `timeout_seconds` |\n| `service.logs` | `stdout` `stderr` |\n| `service.environment` | `required` `defaults` + arbitrary `KEY: value` pairs |\n| `service.expose` | `enabled` `host` `port` (or the `true` shorthand) |\n| `proxy` | `enabled` `gateway` `credentials` `listen` `token_endpoint` `routes` |\n| `proxy.listen` | `host` `port` |\n| `proxy.token_endpoint` | `enabled` `host` `port` |\n| `route` | `name` `match` `upstream` `auth` |\n| `route.match` | `host` `path` |\n| `route.upstream` | `url` `service` `port` |\n| `route.auth` | `type` `identity` `audience` `service_account` `client_id` `client_secret` `credentials` `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
|
|
@@ -1192,7 +1192,7 @@ flowchart LR
|
|
|
1192
1192
|
- [Services](services.md)
|
|
1193
1193
|
- [Admin setup](admin-setup.md)
|
|
1194
1194
|
- [TUI](tui.md)
|
|
1195
|
-
`},{path:"docs/environment.md",title:"Environment",body:"# Environment\n\nEach service process gets a merged environment. Later sources override earlier ones.\n\nDefault source order (`ENV_SOURCE_ORDER` / `environment.sources`):\n\n```mermaid\nflowchart LR\n process --> profile --> dotenv --> generated --> keychain --> secret_manager --> defaults --> vars --> runtime\n```\n\n`process`, `defaults`, `vars`, and `runtime` always run for host services. Container services deliberately omit `process` so the caller's whole shell is not stored in inspectable container metadata. If you set `environment.sources`, the listed optional sources (`profile`, `dotenv`, `generated`, `keychain`, `secret_manager`) are added to the always-on set.\n\n| Source | What it loads |\n|--------|----------------|\n| `process` | The env of whichever CLI/TUI client most recently started or restarted this service (forwarded over the RPC as `client_env`), falling back to the supervisor's own environment if no client has done so yet \u2014 see below |\n| `profile` | `profiles.<name>.environment` |\n| `dotenv` | Repo-root then service working-dir: `.env`, `.env.development`, `.env.local`, `.env.<profile>` |\n| `generated` | Built-in hook that always returns `{}`. A plugin may register `environmentSources` if you need generated values |\n| `keychain` | Named secrets from `environment.secrets` / the credential store |\n| `secret_manager` | Values that look like `projects/*/secrets/*` via the Google REST API |\n| `defaults` | `services.<name>.environment.defaults` |\n| `vars` | Explicit `services.<name>.environment` keys |\n| `runtime` | Values `devctl` injects at start |\n\n`keychain` and `secret_manager` throw only when that source is listed and fetch fails.\n\n### `process` and the daemon-replacement limitation\n\nThe daemon remembers each service's `client_env` only in memory, per service, never on disk. A crash/health-triggered auto-restart or an MCP-initiated `start`/`restart` reuses the last one a real client supplied; a service that has never been started/restarted by a real client this daemon's lifetime \u2014 e.g. one adopted from a prior session by `recoverSession()` \u2014 has none, and falls back to the daemon's own (possibly stale) environment.\n\nThis memory does not survive the daemon process itself being replaced (upgrade, crash, `devctl down` then a fresh start): a new daemon starts with no client history at all, so anything it restarts before a client issues a fresh `start`/`restart` runs on whatever environment that new daemon process itself inherited at spawn. If a service depends on env that changed since the daemon last started, restart it explicitly (`devctl restart <service>` or the TUI) rather than relying on an automatic restart to pick it up.\n\n## Runtime-generated variables\n\nInjected when applicable:\n\n- `SERVICE_PORT`, `SERVICE_HOST`\n- `DEVCTL_PROXY_URL`\n- `DEVCTL_SERVICE_NAME`\n- `DEVCTL_ENVIRONMENT`\n- `DEVCTL_TOKEN_URL` and `DEVCTL_INTERNAL_TOKEN` for host services (never a raw access token); containers omit both because container loopback cannot reach the host loopback endpoint\n\nReferences such as `${services.identity.ports.http}` resolve before process start, including inside profile and dotenv values. `${env.NAME}` is rejected there. IAP route `auth.client_secret` is the exception: `${NAME}` and `${env.NAME}` are expanded from the process environment when the token is minted, not at config load.\n\n`environment.required` on a service fails start if those keys are still empty after the merge.\n\n## TUI / CLI flag precedence\n\n```mermaid\nflowchart LR\n cli[\"CLI flags\"] --> env[\"DEVCTL_* env\"]\n env --> tuiEnv[\"DEVCTL_TUI_CONFIG\"]\n tuiEnv --> tuiJson[\"repo / user tui.json\"]\n tuiJson --> yaml[\"repo .devctl\"]\n yaml --> defaults[\"defaults\"]\n```\n\n## File plugins\n\n`plugins[].path` can register additional named environment sources. Unknown configured source names fail after plugins load instead of being silently skipped. See [Plugins](plugins.md) for the SDK contract, failure behavior, and examples. The built-in `generated` source stays empty unless a plugin registers an environment source with that name.\n\n## Related\n\n- [Services](services.md)\n- [Configuration](configuration.md)\n- [Security](security.md)\n"},{path:"docs/iap.md",title:"Identity-Aware Proxy",body:`# Identity-Aware Proxy
|
|
1195
|
+
`},{path:"docs/environment.md",title:"Environment",body:"# Environment\n\nEach service process gets a merged environment. Later sources override earlier ones.\n\nDefault source order (`ENV_SOURCE_ORDER` / `environment.sources`):\n\n```mermaid\nflowchart LR\n process --> profile --> dotenv --> generated --> keychain --> secret_manager --> defaults --> vars --> runtime\n```\n\n`process`, `defaults`, `vars`, and `runtime` always run for host services. Container services deliberately omit `process` so the caller's whole shell is not stored in inspectable container metadata. If you set `environment.sources`, the listed optional sources (`profile`, `dotenv`, `generated`, `keychain`, `secret_manager`) are added to the always-on set.\n\n| Source | What it loads |\n|--------|----------------|\n| `process` | The env of whichever CLI/TUI client most recently started or restarted this service (forwarded over the RPC as `client_env`), falling back to the supervisor's own environment if no client has done so yet \u2014 see below |\n| `profile` | `profiles.<name>.environment` |\n| `dotenv` | Repo-root then service working-dir: `.env`, `.env.development`, `.env.local`, `.env.<profile>` |\n| `generated` | Built-in hook that always returns `{}`. A plugin may register `environmentSources` if you need generated values |\n| `keychain` | Named secrets from `environment.secrets` / the credential store |\n| `secret_manager` | Values that look like `projects/*/secrets/*` via the Google REST API |\n| `defaults` | `services.<name>.environment.defaults` |\n| `vars` | Explicit `services.<name>.environment` keys |\n| `runtime` | Values `devctl` injects at start |\n\n`keychain` and `secret_manager` throw only when that source is listed and fetch fails.\n\n### `process` and the daemon-replacement limitation\n\nThe daemon remembers each service's `client_env` only in memory, per service, never on disk. A crash/health-triggered auto-restart or an MCP-initiated `start`/`restart` reuses the last one a real client supplied; a service that has never been started/restarted by a real client this daemon's lifetime \u2014 e.g. one adopted from a prior session by `recoverSession()` \u2014 has none, and falls back to the daemon's own (possibly stale) environment.\n\nThis memory does not survive the daemon process itself being replaced (upgrade, crash, `devctl down` then a fresh start): a new daemon starts with no client history at all, so anything it restarts before a client issues a fresh `start`/`restart` runs on whatever environment that new daemon process itself inherited at spawn. If a service depends on env that changed since the daemon last started, restart it explicitly (`devctl restart <service>` or the TUI) rather than relying on an automatic restart to pick it up.\n\n## Runtime-generated variables\n\nInjected when applicable:\n\n- `SERVICE_PORT`, `SERVICE_HOST`\n- `DEVCTL_PROXY_URL`\n- `DEVCTL_SERVICE_NAME`\n- `DEVCTL_ENVIRONMENT`\n- `DEVCTL_USER_EMAIL` \u2014 the developer's own detected Google identity (gcloud/ADC), so a service can key on who is running it without a hardcoded, team-unfriendly value. Omitted when no identity is detected.\n- `DEVCTL_TOKEN_URL` and `DEVCTL_INTERNAL_TOKEN` for host services (never a raw access token); containers omit both because container loopback cannot reach the host loopback endpoint\n\nReferences such as `${services.identity.ports.http}` resolve before process start, including inside profile and dotenv values. `${identity.user}` resolves to the running developer's detected email \u2014 use it to map that identity onto a service's own variable in shared config, e.g. `LOCAL_USER_EMAIL: ${identity.user}` (empty when no identity is detected). `${env.NAME}` is rejected there. IAP route `auth.client_secret` is the exception: `${NAME}` and `${env.NAME}` are expanded from the process environment when the token is minted, not at config load.\n\n`environment.required` on a service fails start if those keys are still empty after the merge.\n\n## TUI / CLI flag precedence\n\n```mermaid\nflowchart LR\n cli[\"CLI flags\"] --> env[\"DEVCTL_* env\"]\n env --> tuiEnv[\"DEVCTL_TUI_CONFIG\"]\n tuiEnv --> tuiJson[\"repo / user tui.json\"]\n tuiJson --> yaml[\"repo .devctl\"]\n yaml --> defaults[\"defaults\"]\n```\n\n## File plugins\n\n`plugins[].path` can register additional named environment sources. Unknown configured source names fail after plugins load instead of being silently skipped. See [Plugins](plugins.md) for the SDK contract, failure behavior, and examples. The built-in `generated` source stays empty unless a plugin registers an environment source with that name.\n\n## Related\n\n- [Services](services.md)\n- [Configuration](configuration.md)\n- [Security](security.md)\n"},{path:"docs/iap.md",title:"Identity-Aware Proxy",body:`# Identity-Aware Proxy
|
|
1196
1196
|
|
|
1197
1197
|
IAP routes must set an audience (IAP OAuth client ID or IAP resource name) **and** an identity. A missing \`identity.type\` is a configuration error, not a silent default to the current user.
|
|
1198
1198
|
|
|
@@ -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### 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## 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
|
|
|
@@ -2052,7 +2052,7 @@ services:
|
|
|
2052
2052
|
|
|
2053
2053
|
Working directories resolve from the repository root (the directory that contains \`.devctl\`), not the process cwd.
|
|
2054
2054
|
|
|
2055
|
-
\`\${services.<name>.ports.<port>}\` interpolates another service's port; \`\${services.<name>.url}\` and \`\${services.<name>.host}\` give a stable base address that routes through the proxy when the target is exposed (see [Proxy \u2192 Expose](proxy.md)). \`expose: true\` publishes the service through the proxy at \`<service>.local\` when \`proxy.enabled\` is true; \`proxy.gateway: true\` does the same for every HTTP service at once. Neither flag creates a route if the proxy is off.
|
|
2055
|
+
\`\${services.<name>.ports.<port>}\` interpolates another service's port; \`\${services.<name>.url}\` and \`\${services.<name>.host}\` give a stable base address that routes through the proxy when the target is exposed (see [Proxy \u2192 Expose](proxy.md)). \`\${identity.user}\` resolves to the running developer's detected Google email \u2014 handy in shared config as \`LOCAL_USER_EMAIL: \${identity.user}\` (each developer gets their own, nothing hardcoded); it is also injected automatically as \`DEVCTL_USER_EMAIL\`. \`expose: true\` publishes the service through the proxy at \`<service>.local\` when \`proxy.enabled\` is true; \`proxy.gateway: true\` does the same for every HTTP service at once. Neither flag creates a route if the proxy is off.
|
|
2056
2056
|
|
|
2057
2057
|
String commands that contain \`|\`, \`||\`, \`&&\`, \`;\`, \`>\`, \`>>\`, \`<\`, or \`&\` fail validation unless \`shell: true\`.
|
|
2058
2058
|
|
|
@@ -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(`
|
|
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 UA(){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,...UA()]}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 WA(e){return e.toLowerCase().split(/[^a-z0-9_]+/).filter((t)=>t.length>=jA)}function GA(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+=$A;if(n.includes(a))i+=FA;let c=o.split(a).length-1;i+=Math.min(c,NA)}let s=t.join(" ");if(s.length>=BA&&o.includes(s))i+=HA;return i}function KA(e,t){let r=e.split(`
|
|
2208
2208
|
`),n=0,o=-1;for(let s=0;s<r.length;s++){let a=r.slice(s,s+Ky).join(`
|
|
2209
2209
|
`).toLowerCase(),c=0;for(let l of t)if(a.includes(l))c+=1;if(c>o)o=c,n=s}let i=r.slice(n,n+Ky).join(`
|
|
2210
|
-
`).trim();if(i.length<=Gy)return i;return`${i.slice(0,Gy).trimEnd()}\u2026`}function KA(e){if(e===void 0||Number.isNaN(e))return IA;return Math.min(MA,Math.max(1,Math.floor(e)))}function zy(e,t){let r=e.trim(),n=UA(r);if(n.length===0)throw Error("query is required");let o=KA(t),i=qy().map((s)=>({path:s.path,title:s.title,score:WA(s,n),snippet:GA(s.body,n)})).filter((s)=>s.score>0).sort((s,a)=>a.score-s.score||s.path.localeCompare(a.path)).slice(0,o);return{query:r,hits:i}}var IA=5,MA=10,OA=12,$A=8,FA=8,NA=15,Gy=400,Ky=5,HA=2,jA=4;var Yy=I(()=>{sp();Wy()});import{existsSync as qA,readFileSync as VA}from"fs";function An(e,t){return!(t??[]).includes(e)}function Jy(e){return _c.filter((t)=>An(t.name,e))}function Rc(e){return _c.some((t)=>t.name===e)}function ps(e){return new mr(e.secrets.extra_markers,e.secrets.extra_patterns)}function Xy(e){return Object.values(e.services).map((t)=>({name:t.name,state:t.state,health:t.health,ports:t.ports,pid:t.pid,last_error:t.last_error}))}function YA(e,t){let r=e.config(),n=r.services[t];if(!n)throw Error(`unknown service ${t}`);let i=e.status().services[t],s=ps(r);return{name:t,state:i?.state,health:i?.health,pid:i?.pid,last_error:i?.last_error,command:n.command.args,cwd:n.working_dir,ports:i?.ports??Object.fromEntries(n.ports.map((a)=>[a.name,a.value])),environment:s.redactMap({...n.environment.defaults,...n.environment.vars}),container:n.container?{...n.container,env:s.redactMap(n.container.env)}:void 0}}function Qy(e){return{session_id:e.session_id,repo_root:e.repo_root,profile:e.profile,setup_mode:e.setup_mode===!0?!0:void 0,identity:{user:e.identity.user,project:e.identity.project,adc:e.identity.adc,iap:e.identity.iap,service_accounts:e.identity.service_accounts,service_account_status:e.identity.service_account_status},proxy:{running:e.proxy.running,address:e.proxy.address,routes:e.proxy.routes},logs:e.logs,mcp:e.mcp?{running:e.mcp.running,address:e.mcp.address,port:e.mcp.port}:{running:!1}}}async function Zy(e,t){let r=typeof t.service==="string"?t.service:"",n=typeof t.since==="string"?t.since:"",o=typeof t.cursor==="string"?t.cursor:void 0,i=await e.logsPage({services:r===""?[]:[r],level:typeof t.level==="string"?t.level:"",search:typeof t.search==="string"?t.search:"",source:typeof t.source==="string"?t.source:"",since:n,until:typeof t.until==="string"?t.until:"",cursor:o,direction:o?"forward":void 0,limit:zA}),s=ps(e.config());return{events:i.events.map((a)=>({timestamp:a.timestamp,service:a.service,source:a.source,level:a.level,message:s.redactText(a.message),pid:a.pid})),truncated:i.hasPrev,has_more:i.hasNext,next_since:i.events[i.events.length-1]?.timestamp??n,next_cursor:i.nextCursor,session_changed:i.sessionChanged}}function JA(e){return Object.entries(e.profiles).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>({name:t,services:r.services}))}function oi(e){return e.trim()===""?void 0:e}function XA(e){let t={name:e.name,match:e.match,upstream:e.upstream.url!==""?e.upstream.url:oi(e.upstream.service??"")?`service:${e.upstream.service}${e.upstream.port?`/${e.upstream.port}`:""}`:"",auth:e.auth.type},r=oi(e.auth.identity.type),n=oi(e.auth.audience),o=oi(e.auth.identity.service_account||e.auth.service_account),i=oi(e.auth.client_id),s=Ba(e.auth.client_secret),a=oi(e.auth.credentials??"");if(r)t.identity=r;if(n)t.audience=n;if(o)t.service_account=o;if(i)t.client_id=i;if(s)t.client_secret=s;if(a)t.credentials=a;return t}function eb(e){let t=Object.entries(e.services).map(([r,n])=>({name:r,description:n.description,command:n.command.args,cwd:n.working_dir,ports:n.ports.map((o)=>({name:o.name,value:o.auto?0:o.value,auto:o.auto})),dependencies:n.dependencies,container:n.container?{image:n.container.image,runtime:n.container.runtime||"docker",ports:n.container.ports,volumes:n.container.volumes}:void 0}));return{project:e.project.name,config_path:e.configPath,repo_root:e.repoRoot,services:t,proxy:{enabled:e.proxy.enabled,gateway:e.proxy.gateway,credentials:e.proxy.credentials,listen:{host:e.proxy.listen.host,port:e.proxy.listen.port},routes:e.proxy.routes.map((r)=>XA(r))}}}function QA(e){let t=ps(e);return{entries:Pn(e).map((r)=>{let n=typeof r.value==="string"?r.value:JSON.stringify(r.value);return{...r,value:t.redactMap({[r.path]:n})[r.path]}})}}function Pc(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}async function tb(e,t,r){switch(t){case"list_services":return Xy(e.status());case"get_service":if(typeof r.name!=="string"||r.name==="")throw Error("name is required");return YA(e,r.name);case"get_status":return Qy(e.status());case"get_logs":return Zy(e,r);case"list_profiles":return JA(e.config());case"get_config":return eb(e.config());case"get_config_sources":return QA(e.config());case"run_doctor":return e.doctor();case"start_services":return e.start({services:Pc(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0});case"stop_services":return await e.stop(Pc(r.services)),{ok:!0};case"restart_services":return await e.restart(Pc(r.services),r.cascade===!0),{ok:!0};case"reload_config":return e.reload();case"run_task":{if(typeof r.name!=="string"||r.name==="")throw Error("name is required");let n=await e.runTask(r.name),o=ps(e.config());return{...n,stdout:o.redactText(n.stdout),stderr:o.redactText(n.stderr)}}case"start_proxy":return await e.startProxy(),{ok:!0};case"stop_proxy":return await e.stopProxy(),{ok:!0};case"exec_service":{if(typeof r.service!=="string"||r.service==="")throw Error("service is required");if(!e.exec)throw Error("exec is unavailable");let n=await e.exec(r.service,Pc(r.command),r.print_env===!0),o=ps(e.config());return{...n,stdout:o.redactText(n.stdout),stderr:o.redactText(n.stderr),environment:n.environment?o.redactMap(n.environment):void 0}}case"get_setup_guide":return ZA(r);case"search_docs":return zy(typeof r.query==="string"?r.query:"",typeof r.limit==="number"?r.limit:void 0);case"get_doc":return Vy(typeof r.path==="string"?r.path:"");case"validate_config":return eL(e,r);default:throw Error(`unknown tool ${t}`)}}function ZA(e){let t=typeof e.section==="string"?e.section:"procedure",r=t in Tn?t:"procedure";return{section:r,text:Tn[r],sections:Object.keys(Tn)}}function eL(e,t){let r=e.config();if(typeof t.text==="string"){let o=e.validateConfigText(t.text);return{valid:o.length===0,issues:o,source:"candidate",config_path:r.configPath}}if(!qA(r.configPath))return{valid:!1,issues:[`no configuration at ${r.configPath}`],source:"disk",config_path:r.configPath,setup_mode:!0};let n=e.validateConfigText(VA(r.configPath,"utf8"));return{valid:n.length===0,issues:n,source:"disk",config_path:r.configPath}}function rb(e){return ap.includes(e)}async function nb(e,t){switch(t){case"devctl://status":return Qy(e.status());case"devctl://services":return Xy(e.status());case"devctl://logs":return Zy(e,{});case"devctl://config":return eb(e.config());case"devctl://doctor":return e.doctor();default:throw Error(`unknown resource ${t}`)}}var zA=200,ap,cp,_c;var fs=I(()=>{ni();Yy();sp();ap=["devctl://status","devctl://services","devctl://logs","devctl://config","devctl://doctor"],cp=["inspect","logs","diagnostics","control","setup"],_c=[{name:"list_services",label:"List services",summary:"Name, state, health, ports, pid",category:"inspect",description:"List services with state, health, ports, pid, and last error",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_service",label:"Service detail",summary:"One service with command, cwd, ports",category:"inspect",description:"One service plus command, cwd, and ports. Environment values are redacted.",inputSchema:{type:"object",properties:{name:{type:"string"}},required:["name"],additionalProperties:!1}},{name:"get_status",label:"Status",summary:"Profile, identity, proxy, log counts",category:"inspect",description:"Profile, session, identity flags (no tokens), proxy, and log counts",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_logs",label:"Read logs",summary:"Filtered log pages, secrets redacted",category:"logs",description:"Recent log lines, optionally filtered. Capped at 200 events per page. Secrets are redacted. Pass cursor=next_cursor to page forward with no duplicate or same-millisecond-lost events; since/until are plain timestamp filters for a fresh query, not a follow cursor.",inputSchema:{type:"object",properties:{service:{type:"string"},level:{type:"string"},search:{type:"string"},source:{type:"string"},since:{type:"string",description:"Only events at or after this timestamp"},until:{type:"string",description:"Only events at or before this timestamp"},cursor:{type:"string",description:"Opaque cursor from a previous response's next_cursor; continues forward from exactly there"}},additionalProperties:!1}},{name:"list_profiles",label:"List profiles",summary:"Configured profiles and their members",category:"inspect",description:"Configured profiles and their member services",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_config",label:"Read config",summary:"Merged project summary, no secret values",category:"inspect",description:"Merged project summary: services, routes, and proxy listen paths. Route auth includes type, identity, audience, and optional IAP client_id. client_secret is returned only as a ${ENV} template, never a resolved secret.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_config_sources",label:"Config sources",summary:"Winning and shadowed config sources",category:"inspect",description:"List effective configuration values with their winning source/layer and any shadowed sources. Secret-like values are redacted.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"run_doctor",label:"Run doctor",summary:"Environment diagnostics",category:"diagnostics",description:"Run environment diagnostics",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"start_services",label:"Start services",summary:"Start named services or a profile",category:"control",mutates:!0,description:"Start named services, or a profile when names are omitted. Empty start uses profile, then the active session profile, then the first configured profile \u2014 never every service.",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}},profile:{type:"string",description:"Profile to start when services is omitted"}},additionalProperties:!1}},{name:"stop_services",label:"Stop services",summary:"Stop services and their dependents",category:"control",mutates:!0,description:"Stop named services, or all started services when names are omitted. Also stops every service that transitively depends on a named one (never a named service's own dependencies, which other running services may still need).",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}}},additionalProperties:!1}},{name:"restart_services",label:"Restart services",summary:"Restart services, optionally cascading",category:"control",mutates:!0,description:"Restart named services. By default this touches only the named services, not anything that depends on them. Pass cascade=true to also restart their transitive dependents (the same set stop_services would affect).",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}},cascade:{type:"boolean",description:"Also restart transitive dependents; default false restarts only the named services"}},additionalProperties:!1}},{name:"reload_config",label:"Reload config",summary:"Re-read .devctl configuration",category:"control",mutates:!0,description:"Reload .devctl configuration",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"run_task",label:"Run task",summary:"Run a named one-off task",category:"control",mutates:!0,description:"Run a named task from configuration. Output is also written to the log ring as task:<name>.",inputSchema:{type:"object",properties:{name:{type:"string"}},required:["name"],additionalProperties:!1}},{name:"start_proxy",label:"Start proxy",summary:"Start the local reverse proxy",category:"control",mutates:!0,description:"Start the local reverse proxy. Same as CLI `devctl proxy start` and TUI start on the proxy screen.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"stop_proxy",label:"Stop proxy",summary:"Stop the local reverse proxy",category:"control",mutates:!0,description:"Stop the local reverse proxy and leave it suppressed until start_proxy (or CLI/TUI start) runs again.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"exec_service",label:"Execute in service",summary:"Run a command in a resolved service context",category:"control",mutates:!0,description:"Run an arbitrary command with a service's fully resolved environment and working directory, whether or not it is running. Output and print_env values are redacted.",inputSchema:{type:"object",properties:{service:{type:"string"},command:{type:"array",items:{type:"string"}},print_env:{type:"boolean",description:"Return the resolved environment without executing a command"}},required:["service"],additionalProperties:!1}},{name:"get_setup_guide",label:"Setup guide",summary:"How to author a .devctl for this repo",category:"setup",description:"The devctl onboarding guide: how to survey a repository and author a .devctl configuration for it. Read section=procedure first; read section=authoring BEFORE writing any YAML (it carries the rules the loader rejects on, which the JSON Schema does not state); read section=discovery for mapping compose/package.json/pyproject/Terraform/k8s/.env to services. Write the files with your own editing tools \u2014 this server does not write them \u2014 then check your work with validate_config.",inputSchema:{type:"object",properties:{section:{type:"string",enum:["procedure","authoring","discovery"],description:"Which part of the guide to return; defaults to procedure"}},additionalProperties:!1}},{name:"search_docs",label:"Search docs",summary:"Search the embedded product documentation",category:"setup",description:"Search the compiled-in product documentation (docs/*.md plus the onboarding skill). Returns ranked pages with short snippets for discovery. To read a full page, pass a hit's `path` to get_doc. Use this for IAP, proxy, MCP, configuration, and similar topics; use get_setup_guide for the full onboarding procedure.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Keywords or a short phrase to search for"},limit:{type:"integer",description:"Maximum hits to return (default 5, max 10)"}},required:["query"],additionalProperties:!1}},{name:"get_doc",label:"Get doc",summary:"Read a full documentation page",category:"setup",description:"Return the complete text of one embedded documentation page. Pass the `path` from a search_docs hit (e.g. docs/proxy.md) \u2014 search_docs only returns short snippets, so use this to read the whole page. A basename like proxy.md also resolves when unambiguous.",inputSchema:{type:"object",properties:{path:{type:"string",description:"Doc path from a search_docs hit, e.g. docs/proxy.md"}},required:["path"],additionalProperties:!1}},{name:"validate_config",label:"Validate config",summary:"Check configuration or a draft for errors",category:"setup",description:"Validate devctl configuration and return the exact issues the loader would report. With no arguments, validates what is on disk. Pass text to validate a candidate config.yaml before writing it \u2014 the candidate is run through the real load pipeline (modular services/profiles, overlays, templates), so it works even when no configuration exists yet. This is the only way to validate over MCP; there is no CLI round-trip needed.",inputSchema:{type:"object",properties:{text:{type:"string",description:"Candidate config.yaml contents to validate instead of the file on disk"}},additionalProperties:!1}}]});function db(e){return{...e,runDoctor:(t,r,n)=>e.runDoctor.execute(t,r,n),startupPlan:(t,r,n)=>e.getStartupPlan.execute(t,r,n),shutdownPlan:(t,r)=>e.getShutdownPlan.execute(t,r),shutdownPlanExact:(t,r)=>ic(t,r),resolveStartRequest:(t,r)=>e.resolveStart.execute(t,{...r,profile:r.profile?ur(r.profile):void 0,activeProfile:r.activeProfile===void 0?void 0:ur(r.activeProfile)})}}var ub=I(()=>{At()});function Tc(e){let t=e.toLowerCase().trim(),r=fL[t];if(r)return r;return St.includes(t)?t:"devctl"}function mL(){if(process.env.DEVCTL_THEME_LIGHT==="1")return!0;if(process.env.DEVCTL_THEME_LIGHT==="0")return!1;if(process.platform==="darwin")try{let t=Bun.spawnSync(["defaults","read","-g","AppleInterfaceStyle"],{stdout:"pipe",stderr:"pipe"}),r=t.stdout.toString();if(t.exitCode===0&&r.toLowerCase().includes("dark"))return!1;if(t.exitCode!==0)return!0}catch{}let e=process.env.COLORFGBG??"";if(e.includes(";15")||e.endsWith(";7"))return!0;if(e.includes(";0")||e.endsWith(";8"))return!1;return!1}function pb(e){let t=Tc(e);if(t==="system"){if(mL())return{...dp["solarized-light"],name:"system"};return dp.system}return dp[t]}function hr(e){switch(e.toUpperCase()){case"HEALTHY":case"OK":return"\u2713";case"UNHEALTHY":case"WARNING":case"WARN":return"!";case"FAILED":case"ERROR":return"\u2717";case"STOPPED":case"UNKNOWN":return"\u25CB";case"RUNNING":case"STARTING":case"RESTARTING":return"\u25CF";default:return"\u25CB"}}function mo(e,t){return ys(e)>=vs?Ac:t}function ys(e){let t=e.startsWith("#")?e.slice(1):e,r=Number.parseInt(t,kL);if(Number.isNaN(r))return 0;let n=r>>CL&up,o=r>>PL&up,i=r&up;return(n*bL+o*xL+i*wL)/SL}function _L(e){let t=vL;for(let r=0;r<e.length;r+=1)t^=e.charCodeAt(r),t=Math.imul(t,yL);return t>>>0}function Lc(e){return ys(e.background)>=vs}function fp(e){if(!e||!/^#[0-9a-f]{6}$/i.test(e))return!1;return ys(e)<vs}function Dc(e,t){let r=RL[e];return Lc(t)?r.light:r.dark}function fb(e,t){return ys(Dc(e,t))>=vs?EL:TL}function Ze(e,t){if(e===""||e==="all")return t.muted;let r=ys(t.background)>=vs?gL:hL;return r[_L(e)%r.length]??t.info}function AL(e,t){switch(t.toUpperCase()){case"ERROR":case"FATAL":return e.error;case"WARN":case"WARNING":return e.warning;case"INFO":return e.text;case"DEBUG":case"TRACE":return e.muted;default:return e.text}}function mb(e,t,r){if(r==="search")return e.search;if(r==="string")return e.accent;if(r==="number")return e.info;if(r==="keyword")return e.error;return AL(e,t)}function et(e,t){switch(t.toUpperCase()){case"HEALTHY":case"OK":return e.success;case"UNHEALTHY":case"WARNING":case"WARN":return e.warning;case"FAILED":case"ERROR":return e.error;case"RUNNING":case"STARTING":case"RESTARTING":return e.primary;case"INFO":return e.info;case"FATAL":return e.error;case"DEBUG":case"TRACE":case"UNKNOWN":return e.muted;default:return e.muted}}var St,pp,dp,fL,hL,gL,vL=2166136261,yL=16777619,bL=299,xL=587,wL=114,SL=1000,vs=160,kL=16,up=255,CL=16,PL=8,Ac="#1A1A1A",RL,EL="#1A1A1A",TL="#F5F5F5";var tt=I(()=>{St=["devctl","ember","tokyonight","catppuccin","catppuccin-latte","nord","gruvbox","kanagawa","dracula","onedark","monokai","solarized-dark","solarized-light","rose-pine","everforest","github-dark","iceberg","ayu-dark","oxocarbon","night-owl","terminal","voltage","voltage-light","ironclad","ironclad-light","signal","signal-light","system"],pp={devctl:"forest + mint, website palette",ember:"warm orange",tokyonight:"Tokyo Night storm blues",catppuccin:"Catppuccin Mocha","catppuccin-latte":"Catppuccin Latte (light)",nord:"Arctic Nord frost",gruvbox:"Gruvbox dark hard",kanagawa:"Kanagawa wave / gold",dracula:"classic Dracula purple",onedark:"Atom / VS Code One Dark",monokai:"Monokai Sublime","solarized-dark":"Solarized dark","solarized-light":"Solarized light","rose-pine":"Ros\xE9 Pine moon",everforest:"Everforest dark medium","github-dark":"GitHub dimmed canvas",iceberg:"Iceberg navy","ayu-dark":"Ayu Mirage-adjacent dark",oxocarbon:"IBM Oxocarbon","night-owl":"Sarah Drasner Night Owl",terminal:"black + VGA ANSI chrome",voltage:"max contrast, electric neon (dark)","voltage-light":"max contrast, electric neon (light)",ironclad:"max contrast, steel & amber (dark)","ironclad-light":"max contrast, steel & amber (light)",signal:"max contrast, traffic-light semantics (dark)","signal-light":"max contrast, traffic-light semantics (light)",system:"neutral dark (system default)"},dp={tokyonight:{name:"tokyonight",primary:"#82AAFF",accent:"#C792EA",error:"#FF5370",warning:"#FFCB6B",search:"#FFCB6B",success:"#C3E88D",info:"#89DDFF",text:"#F4F7FF",muted:"#A8B2CC",background:"#0F111A",panel:"#1A1B2E",element:"#24283B",highlight:"#2E3350",inverse:"#0F111A",border:"#3D4466",borderActive:"#82AAFF"},catppuccin:{name:"catppuccin",primary:"#89B4FA",accent:"#F5C2E7",error:"#F38BA8",warning:"#F9E2AF",search:"#F9E2AF",success:"#A6E3A1",info:"#89DCEB",text:"#E4E8F7",muted:"#A6A9BE",background:"#11111B",panel:"#1E1E2E",element:"#313244",highlight:"#45475A",inverse:"#11111B",border:"#585B70",borderActive:"#89B4FA"},"catppuccin-latte":{name:"catppuccin-latte",primary:"#1E66F5",accent:"#EA76CB",error:"#D20F39",warning:"#DF8E1D",search:"#DF8E1D",success:"#40A02B",info:"#179299",text:"#4C4F69",muted:"#6C6F85",background:"#EFF1F5",panel:"#E6E9EF",element:"#DCE0E8",highlight:"#CCD0DA",inverse:"#EFF1F5",border:"#9CA0B0",borderActive:"#1E66F5"},nord:{name:"nord",primary:"#88C0D0",accent:"#B48EAD",error:"#BF616A",warning:"#EBCB8B",search:"#EBCB8B",success:"#A3BE8C",info:"#8FBCBB",text:"#F2F5FA",muted:"#A4AEBF",background:"#2E3440",panel:"#3B4252",element:"#434C5E",highlight:"#4C566A",inverse:"#2E3440",border:"#5E6A7D",borderActive:"#88C0D0"},gruvbox:{name:"gruvbox",primary:"#FE8019",accent:"#FABD2F",error:"#FB4934",warning:"#FE8019",search:"#FE8019",success:"#B8BB26",info:"#8EC07C",text:"#F2E5BC",muted:"#C4B394",background:"#1D2021",panel:"#282828",element:"#3C3836",highlight:"#504945",inverse:"#1D2021",border:"#665C54",borderActive:"#FE8019"},kanagawa:{name:"kanagawa",primary:"#7FB4CA",accent:"#E6C384",error:"#E82424",warning:"#FFA066",search:"#FFA066",success:"#98BB6C",info:"#7FB4CA",text:"#E8E4C9",muted:"#B4C0D6",background:"#16161D",panel:"#1F1F28",element:"#2A2A37",highlight:"#363646",inverse:"#16161D",border:"#54546D",borderActive:"#E6C384"},dracula:{name:"dracula",primary:"#BD93F9",accent:"#FF79C6",error:"#FF5555",warning:"#F1FA8C",search:"#F1FA8C",success:"#50FA7B",info:"#8BE9FD",text:"#F8F8F2",muted:"#8B9DC9",background:"#21222C",panel:"#282A36",element:"#343746",highlight:"#44475A",inverse:"#21222C",border:"#6272A4",borderActive:"#BD93F9"},onedark:{name:"onedark",primary:"#61AFEF",accent:"#C678DD",error:"#E06C75",warning:"#E5C07B",search:"#E5C07B",success:"#98C379",info:"#56B6C2",text:"#ABB2BF",muted:"#9DA5B4",background:"#21252B",panel:"#282C34",element:"#2C323C",highlight:"#3E4451",inverse:"#21252B",border:"#4B5263",borderActive:"#61AFEF"},monokai:{name:"monokai",primary:"#66D9EF",accent:"#AE81FF",error:"#F92672",warning:"#E6DB74",search:"#E6DB74",success:"#A6E22E",info:"#66D9EF",text:"#F8F8F2",muted:"#A39E80",background:"#1D1E19",panel:"#272822",element:"#3E3D32",highlight:"#49483E",inverse:"#1D1E19",border:"#75715E",borderActive:"#F92672"},"solarized-dark":{name:"solarized-dark",primary:"#268BD2",accent:"#6C71C4",error:"#DC322F",warning:"#B58900",search:"#B58900",success:"#859900",info:"#2AA198",text:"#93A1A1",muted:"#586E75",background:"#002B36",panel:"#073642",element:"#0A4A58",highlight:"#1A5A68",inverse:"#002B36",border:"#586E75",borderActive:"#268BD2"},"solarized-light":{name:"solarized-light",primary:"#268BD2",accent:"#D33682",error:"#DC322F",warning:"#CB4B16",search:"#CB4B16",success:"#859900",info:"#2AA198",text:"#586E75",muted:"#839496",background:"#FDF6E3",panel:"#EEE8D5",element:"#E4DCC8",highlight:"#D5CDB6",inverse:"#FDF6E3",border:"#93A1A1",borderActive:"#268BD2"},"rose-pine":{name:"rose-pine",primary:"#C4A7E7",accent:"#EBBCBA",error:"#EB6F92",warning:"#F6C177",search:"#F6C177",success:"#31748F",info:"#9CCFD8",text:"#E0DEF4",muted:"#908CAA",background:"#191724",panel:"#1F1D2E",element:"#26233A",highlight:"#403D52",inverse:"#191724",border:"#6E6A86",borderActive:"#C4A7E7"},everforest:{name:"everforest",primary:"#7FBBB3",accent:"#D699B6",error:"#E67E80",warning:"#DBBC7F",search:"#DBBC7F",success:"#A7C080",info:"#83C092",text:"#D3C6AA",muted:"#9DA9A0",background:"#232A2E",panel:"#2D353B",element:"#343F44",highlight:"#3D484D",inverse:"#232A2E",border:"#4F585E",borderActive:"#A7C080"},"github-dark":{name:"github-dark",primary:"#58A6FF",accent:"#A371F7",error:"#F85149",warning:"#D29922",search:"#D29922",success:"#3FB950",info:"#79C0FF",text:"#E6EDF3",muted:"#8B949E",background:"#0D1117",panel:"#161B22",element:"#21262D",highlight:"#30363D",inverse:"#0D1117",border:"#30363D",borderActive:"#58A6FF"},iceberg:{name:"iceberg",primary:"#84A0C6",accent:"#A093C7",error:"#E27878",warning:"#E2A478",search:"#E2A478",success:"#B4BE82",info:"#89B8C2",text:"#C6C8D1",muted:"#9AA0B8",background:"#161821",panel:"#1E2132",element:"#2A3150",highlight:"#3D425B",inverse:"#161821",border:"#444B71",borderActive:"#84A0C6"},"ayu-dark":{name:"ayu-dark",primary:"#59C2FF",accent:"#FFB454",error:"#D95757",warning:"#FF8F40",search:"#FF8F40",success:"#C2D94C",info:"#95E6CB",text:"#B3B1AD",muted:"#8B939C",background:"#0A0E14",panel:"#0D1017",element:"#131721",highlight:"#1B212C",inverse:"#0A0E14",border:"#2D3640",borderActive:"#FFB454"},oxocarbon:{name:"oxocarbon",primary:"#78A9FF",accent:"#EE5396",error:"#FA4D56",warning:"#F1C21B",search:"#F1C21B",success:"#42BE65",info:"#3DDBD9",text:"#F2F4F8",muted:"#A8A8A8",background:"#161616",panel:"#262626",element:"#393939",highlight:"#525252",inverse:"#161616",border:"#6F6F6F",borderActive:"#78A9FF"},"night-owl":{name:"night-owl",primary:"#82AAFF",accent:"#C792EA",error:"#EF5350",warning:"#ECC48D",search:"#ECC48D",success:"#ADDB67",info:"#7FDBCA",text:"#D6DEEB",muted:"#8FA4A4",background:"#011627",panel:"#0B2942",element:"#1D3B53",highlight:"#1E4B6B",inverse:"#011627",border:"#5F7E97",borderActive:"#82AAFF"},terminal:{name:"terminal",primary:"#55FFFF",accent:"#FF55FF",error:"#FF5555",warning:"#FFFF55",search:"#FFFF55",success:"#55FF55",info:"#5555FF",text:"#C0C0C0",muted:"#808080",background:"#000000",panel:"#000000",element:"#000000",highlight:"#404040",inverse:"#000000",border:"#555555",borderActive:"#55FFFF"},voltage:{name:"voltage",primary:"#00E5FF",accent:"#FF3EA5",error:"#FF1744",warning:"#FFEA00",search:"#FFEA00",success:"#00E676",info:"#40C4FF",text:"#FFFFFF",muted:"#C0C0C0",background:"#000000",panel:"#000000",element:"#0D0D0D",highlight:"#262626",inverse:"#000000",border:"#808080",borderActive:"#FFFFFF"},"voltage-light":{name:"voltage-light",primary:"#0057B7",accent:"#C2185B",error:"#D50000",warning:"#FF6D00",search:"#FF6D00",success:"#2E7D32",info:"#00838F",text:"#000000",muted:"#424242",background:"#FFFFFF",panel:"#FFFFFF",element:"#EDEDED",highlight:"#D6D6D6",inverse:"#FFFFFF",border:"#616161",borderActive:"#000000"},ironclad:{name:"ironclad",primary:"#4FC3F7",accent:"#FFD54F",error:"#FF5252",warning:"#FFB300",search:"#FFB300",success:"#69F0AE",info:"#64B5F6",text:"#FFFFFF",muted:"#BDBDBD",background:"#000000",panel:"#000000",element:"#101010",highlight:"#242424",inverse:"#000000",border:"#757575",borderActive:"#4FC3F7"},"ironclad-light":{name:"ironclad-light",primary:"#01579B",accent:"#E65100",error:"#B71C1C",warning:"#EF6C00",search:"#EF6C00",success:"#1B5E20",info:"#0277BD",text:"#000000",muted:"#4A4A4A",background:"#FFFFFF",panel:"#FFFFFF",element:"#EFEFEF",highlight:"#DADADA",inverse:"#FFFFFF",border:"#6E6E6E",borderActive:"#01579B"},signal:{name:"signal",primary:"#29B6F6",accent:"#AB47BC",error:"#FF0000",warning:"#FFC400",search:"#FFC400",success:"#00E400",info:"#00B8D4",text:"#FFFFFF",muted:"#B3B3B3",background:"#000000",panel:"#000000",element:"#0A0A0A",highlight:"#202020",inverse:"#000000",border:"#8A8A8A",borderActive:"#29B6F6"},"signal-light":{name:"signal-light",primary:"#01579B",accent:"#6A1B9A",error:"#C62828",warning:"#F9A825",search:"#F9A825",success:"#2E7D32",info:"#00796B",text:"#000000",muted:"#3D3D3D",background:"#FFFFFF",panel:"#FFFFFF",element:"#ECECEC",highlight:"#D2D2D2",inverse:"#FFFFFF",border:"#5C5C5C",borderActive:"#01579B"},system:{name:"system",primary:"#60A5FA",accent:"#C084FC",error:"#F87171",warning:"#FBBF24",search:"#FBBF24",success:"#34D399",info:"#22D3EE",text:"#F9FAFB",muted:"#C4CAD3",background:"#030712",panel:"#111827",element:"#1F2937",highlight:"#374151",inverse:"#030712",border:"#4B5563",borderActive:"#60A5FA"},devctl:{name:"devctl",primary:"#77DDBA",accent:"#9EE9CF",error:"#F28B91",warning:"#E8C586",search:"#E8C586",success:"#91D6A0",info:"#8EC8D8",text:"#DEEEE5",muted:"#A4B9AE",background:"#15201D",panel:"#202923",element:"#293B32",highlight:"#365448",inverse:"#15201D",border:"#526D60",borderActive:"#77DDBA"},ember:{name:"ember",primary:"#FF9F43",accent:"#2EE6FF",error:"#FF4D6D",warning:"#FFE566",search:"#FFE566",success:"#3DFF9A",info:"#2EE6FF",text:"#F7F9FF",muted:"#B0B8CC",background:"#07090F",panel:"#12161F",element:"#1A2130",highlight:"#243044",inverse:"#0B0D12",border:"#334155",borderActive:"#FF9F43"}},fL={opencode:"ember",light:"system",mocha:"catppuccin",latte:"catppuccin-latte","one-dark":"onedark","one dark":"onedark",solarized:"solarized-dark",rosepine:"rose-pine","rose pine":"rose-pine",github:"github-dark",ayu:"ayu-dark","night owl":"night-owl",ansi:"terminal",xterm:"terminal",console:"terminal",classic:"terminal",vga:"terminal",contrast:"voltage","contrast-light":"voltage-light",hc:"voltage","hc-light":"voltage-light",a11y:"voltage","a11y-light":"voltage-light"};hL=["#7AA2F7","#9ECE6A","#E0AF68","#BB9AF7","#2AC3DE","#FF9E64","#F7768E","#73DACA","#C0CAF5","#FF79C6","#7DCFFF","#C3E88D"],gL=["#2E5AAC","#3D7A1F","#9A6B12","#6B3FA0","#0E7A86","#B85C1A","#B4234A","#1A7A6D","#4A5080","#9A3480","#1565C0","#4A7C19"];RL={claude:{dark:"#E08A6A",light:"#B54328"},cursor:{dark:"#B8D4FF",light:"#1E4EBF"},kilo:{dark:"#F0C94A",light:"#8A6400"},codex:{dark:"#4ADE80",light:"#0B7A4B"}}});var Rr=I(()=>{ta()});function vb(e){return e!==void 0}function hb(e){let t=e/1000;if(Number.isInteger(t))return`${t}s`;return`${e}ms`}function gp(e,t,r){let n=e.indexOf(t),i=((n<0?0:n)+r+e.length)%e.length;return e[i]??e[0]}function yb(e,t){let r=[...St],n=r.includes(e)?e:hp;return gp(r,n,t)}function bb(e,t){let r=[...LL],n=r.reduce((o,i)=>Math.abs(i-e)<Math.abs(o-e)?i:o,r[0]);return gp(r,n,t)}function bs(e){let t=[...mp];return t.reduce((r,n)=>Math.abs(n-e)<Math.abs(r-e)?n:r,t[0])}function xs(e,t){return gp([...mp],bs(e),t)}function ws(e){let t=bs(e),r=gb[t]??gb[Lo];return{px:t,...r}}function ho(e){return e.label==="compact"}function Ss(e){return e.steps<=2}function xb(e){return mp.map((r,n)=>n<e?"\u2588":"\u2591").join(" ")}function Ic(e){return ws(e).label}function wb(e){let t=e.locked?"Applies this session only. DEVCTL_TUI_CONFIG overrides the user file.":`Saved to ${e.configPath}.`;return[{id:"theme",group:"Appearance",kind:"cycle",name:"Theme",value:e.themeName,hint:"\u2190 \u2192 save enter picker",detail:`Arrows write the theme to the user file. Enter opens the picker. ${t}`},{id:"font",group:"Appearance",kind:"cycle",name:"Display size",value:Ic(e.fontSize),hint:"\u2190 \u2192 save",detail:`Scales content padding and list-row height. Default keeps a thin rule between chrome and panes. Compact sits toolbars flush against borders. Header, nav, and status stay one line. Does not change the terminal font. ${t}`},{id:"mouse",group:"Input",kind:"toggle",name:"Mouse",value:e.mouse?"on":"off",hint:"space or enter toggle",detail:`Clicks on nav and lists. Restart the TUI after a change. ${t}`},{id:"leader",group:"Input",kind:"cycle",name:"Leader",value:hb(e.leaderMs),hint:"\u2190 \u2192 save",detail:`How long ${It("x")} waits for the next key. ${t}`},{id:"mcp",group:"MCP",kind:"page",name:"Settings page",value:e.mcpRunning?"running \u2192 /mcp":"off \u2192 /mcp",hint:"enter open the MCP page",detail:"Opens a dedicated MCP page (also /mcp or /agent). Start or stop the localhost server, change the port, and copy Claude, Cursor, Codex, and Kilo Code config."},{id:"file",group:"About",kind:"info",name:"File",value:e.configPath,hint:"read-only",detail:e.locked?`Using ${e.configPath} from DEVCTL_TUI_CONFIG.`:`User preferences live in ${e.configPath}.`},{id:"version",group:"About",kind:"info",name:"Version",value:jt,hint:"read-only",detail:er()},{id:"reset",group:"About",kind:"action",name:"Reset",value:"defaults",hint:"enter confirm reset",detail:`Restore theme ${hp}, display ${Ic(Lo)}, mouse on, leader ${hb(sd)}. Enter asks first. ${t}`}]}function Mc(){return{theme:hp,mouse:!0,leader_timeout:sd,font_size:Lo}}function Sb(e){return["Appearance","Input","MCP","About"].flatMap((r)=>{let n=e.filter((o)=>o.group===r);if(n.length===0)return[];return[{group:r,items:n}]})}function vp(e,t){if(e.length===0)return 0;return Math.min(Math.max(t,0),e.length-1)}function Ln(e,t){return e[vp(e,t)]}function yp(e,t){return e??t}var LL,mp,hp="devctl",gb;var Er=I(()=>{tt();Rr();LL=[1000,2000,3000],mp=[12,14,16,18,20,22];gb={12:{pad:0,rowH:1,gap:0,steps:1,label:"compact"},14:{pad:0,rowH:1,gap:0,steps:2,label:"default"},16:{pad:1,rowH:1,gap:1,steps:3,label:"comfortable"},18:{pad:1,rowH:2,gap:1,steps:4,label:"large"},20:{pad:2,rowH:2,gap:1,steps:5,label:"xl"},22:{pad:2,rowH:2,gap:2,steps:6,label:"xxl"}}});import{createContext as DL,useContext as IL}from"react";function ye(){return IL(bp)}var bp;var Mt=I(()=>{Er();Rr();bp=DL(ws(Lo))});function Re(e,t){if(t<=0)return"";if(e.length<=t)return e;if(t===1)return"\u2026";return`${e.slice(0,t-1)}\u2026`}function oe(e,t){if(t<=0)return"";if(e.length>=t)return Re(e,t);return e.padEnd(t)}function si(e,t=20){let r=Number.isFinite(e)?Math.min(1,Math.max(0,e)):0,n=Math.round(r*t);return"\u2588".repeat(n)+"\u2591".repeat(Math.max(0,t-n))}function ks(e){if(!Number.isFinite(e)||e<xp)return"< 1s";let t=Math.floor(e/kb),r=Math.floor(e%kb/Sp),n=Math.floor(e%Sp/wp),o=Math.floor(e%wp/xp);if(t>0)return`${t}d ${r}h`;if(r>0)return`${r}h ${n}m`;if(n>0)return`${n}m ${String(o).padStart(2,"0")}s`;return`${o}s`}function Cs(e){if(!Number.isFinite(e)||e<0)return"\u2014";let t=e/ML;if(t>=Cb)return`${(t/Cb).toFixed(1)}G`;if(t>=1)return`${Math.round(t)}M`;return`${Math.round(e)}K`}function Pb(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${e.toFixed(1)}%`}var xp=1000,wp,Sp,kb,ML=1024,Cb=1024;var Ot=I(()=>{wp=60*xp,Sp=60*wp,kb=24*Sp});function Cp(e){return e>=FL}function Pp(e){return e>=NL}function _p(e){return e>=HL}function Nc(e,t=0){return Math.max(1,e-Rb-t*2)}function Eb(e){let t=_b+Tr+$t;if(Cp(e))t+=Ur;if(Pp(e))t+=$c;if(_p(e))t+=sn;return Math.max(Fc,e-t)}function Hc(e,t,r){if(r)return e;let n=t.reduce((a,c)=>Math.max(a,c.length),0),o=Math.min(kp,Math.max(Fc,n+OL)),i=_b+Tr+$t+o+Rb+$L,s=Math.max(Oc,Math.floor(e*jL));return Math.min(s,Math.max(Oc,i))}function Tb(e){if(!e)return"";return Object.keys(e.profiles).sort()[0]??""}function jc(e){if(!e)return!0;let t=Object.values(e.services);if(t.length===0)return!0;return!t.some((r)=>vo(r))}function Rp(e,t){if(e.length>0)return e;return[...t]}function Bc(e,t){if(e.length>0)return[...e];return t===""?[]:[t]}function Uc(e){return e.command.args.join(" ")||"\u2014"}function Es(e,t){let r=go(t);if(r!=="")return r;if(e.ports.length===0)return"\u2014";return e.ports.map((n)=>`${n.name}:${n.auto?"auto":n.value}`).join(" ")}function Wc(e){let t=e.health.type||"none",r=e.health.url||e.health.address;return r===""?t:`${t} ${r}`}function Gc(e,t){if(t?.identity)return t.identity;let r=e.identity.type||"none",n=e.identity.service_account;return n===""?r:`${r} ${n}`}function Kc(e){let t=e.restart.policy||"none";if(e.restart.max_retries>0)return`${t} \xD7${e.restart.max_retries}`;return t}function Ab(e,t,r,n,o){let i={...e.environment.defaults,...e.environment.vars},a=BL(o??i,t,r,n);return[...new Set([...Object.keys(a),...e.environment.required])].map((l)=>({key:l,value:a[l]??"",required:e.environment.required.includes(l),fromConfig:Object.prototype.hasOwnProperty.call(i,l)})).sort((l,d)=>l.fromConfig===d.fromConfig?l.key.localeCompare(d.key):l.fromConfig?-1:1)}function Lb(e,t){if(!e||e.processes.length===0)return;if(t!==void 0&&t!==""&&e.session_id===t)return;return e}function go(e){if(!e)return"";if(e.ports.http!==void 0)return String(e.ports.http);let t=Object.values(e.ports)[0];return t!==void 0?String(t):""}function tr(e){if(!e)return"STOPPED";return Jo(e)}function BL(e,t,r,n){if(t)return e;return new mr(r,n).redactMap(e)}function Ts(e){return UL.has(e)}function vo(e){if(!e)return!1;return Ts(e.state)}var _b=8,Tr=12,Ur=10,$c=8,sn=8,Ps=9,_s=7,Rs=8,Fc=12,kp=40,$t=2,OL=2,Rb=2,$L=2,Oc=34,FL=48,NL=60,HL=72,jL=0.48,UL;var Lt=I(()=>{At();ni();UL=new Set(["RUNNING","STARTING","RESTARTING","HEALTHY","UNHEALTHY"])});function Yc(e){return e===ma?"\u2014":e}function Ep(e,t,r){let n=r?Ib:WL+Ib+Math.max(0,t)*2;return Math.max(1,e-n)}function VL(e){if(e.startsWith('"')||e.startsWith("'"))return"string";if(/^\d+$/.test(e))return"number";return"keyword"}function Jc(e){return e.replace(zL,"")}function Fb(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function Xc(e,t){let r=Math.max(1,t),n=Jc(e).replace(/\r\n/g,`
|
|
2210
|
+
`).trim();if(i.length<=Gy)return i;return`${i.slice(0,Gy).trimEnd()}\u2026`}function qA(e){if(e===void 0||Number.isNaN(e))return MA;return Math.min(OA,Math.max(1,Math.floor(e)))}function zy(e,t){let r=e.trim(),n=WA(r);if(n.length===0)throw Error("query is required");let o=qA(t),i=qy().map((s)=>({path:s.path,title:s.title,score:GA(s,n),snippet:KA(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 MA=5,OA=10,$A=12,FA=8,NA=8,HA=15,Gy=400,Ky=5,jA=2,BA=4;var Yy=I(()=>{sp();Wy()});import{existsSync as VA,readFileSync as zA}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 JA(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:YA}),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 XA(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 QA(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)=>QA(r))}}}function ZA(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 JA(e,r.name);case"get_status":return Qy(e.status());case"get_logs":return Zy(e,r);case"list_profiles":return XA(e.config());case"get_config":return eb(e.config());case"get_config_sources":return ZA(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 eL(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 tL(e,r);default:throw Error(`unknown tool ${t}`)}}function eL(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 tL(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(!VA(r.configPath))return{valid:!1,issues:[`no configuration at ${r.configPath}`],source:"disk",config_path:r.configPath,setup_mode:!0};let n=e.validateConfigText(zA(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 YA=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=mL[t];if(r)return r;return St.includes(t)?t:"devctl"}function hL(){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(hL())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,CL);if(Number.isNaN(r))return 0;let n=r>>PL&up,o=r>>_L&up,i=r&up;return(n*xL+o*wL+i*SL)/kL}function RL(e){let t=yL;for(let r=0;r<e.length;r+=1)t^=e.charCodeAt(r),t=Math.imul(t,bL);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=EL[e];return Lc(t)?r.light:r.dark}function fb(e,t){return ys(Dc(e,t))>=vs?TL:AL}function Ze(e,t){if(e===""||e==="all")return t.muted;let r=ys(t.background)>=vs?vL:gL;return r[RL(e)%r.length]??t.info}function LL(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 LL(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,mL,gL,vL,yL=2166136261,bL=16777619,xL=299,wL=587,SL=114,kL=1000,vs=160,CL=16,up=255,PL=16,_L=8,Ac="#1A1A1A",EL,TL="#1A1A1A",AL="#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"}},mL={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"};gL=["#7AA2F7","#9ECE6A","#E0AF68","#BB9AF7","#2AC3DE","#FF9E64","#F7768E","#73DACA","#C0CAF5","#FF79C6","#7DCFFF","#C3E88D"],vL=["#2E5AAC","#3D7A1F","#9A6B12","#6B3FA0","#0E7A86","#B85C1A","#B4234A","#1A7A6D","#4A5080","#9A3480","#1565C0","#4A7C19"];EL={claude:{dark:"#E08A6A",light:"#B54328"},cursor:{dark:"#B8D4FF",light:"#1E4EBF"},kilo:{dark:"#F0C94A",light:"#8A6400"},codex:{dark:"#4ADE80",light:"#0B7A4B"}}});var Er=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=[...DL],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 DL,mp,hp="devctl",gb;var Tr=I(()=>{tt();Er();DL=[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 IL,useContext as ML}from"react";function ye(){return ML(bp)}var bp;var Mt=I(()=>{Tr();Er();bp=IL(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/OL;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,OL=1024,Cb=1024;var Ot=I(()=>{wp=60*xp,Sp=60*wp,kb=24*Sp});function Cp(e){return e>=NL}function Pp(e){return e>=HL}function _p(e){return e>=jL}function Nc(e,t=0){return Math.max(1,e-Rb-t*2)}function Eb(e){let t=_b+Ar+$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+$L)),i=_b+Ar+$t+o+Rb+FL,s=Math.max(Oc,Math.floor(e*BL));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=UL(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 UL(e,t,r,n){if(t)return e;return new mr(r,n).redactMap(e)}function Ts(e){return WL.has(e)}function vo(e){if(!e)return!1;return Ts(e.state)}var _b=8,Ar=12,Ur=10,$c=8,an=8,Ps=9,_s=7,Rs=8,Fc=12,kp=40,$t=2,$L=2,Rb=2,FL=2,Oc=34,NL=48,HL=60,jL=72,BL=0.48,WL;var Lt=I(()=>{At();ni();WL=new Set(["RUNNING","STARTING","RESTARTING","HEALTHY","UNHEALTHY"])});function Yc(e){return e===ma?"\u2014":e}function Ep(e,t,r){let n=r?Ib:GL+Ib+Math.max(0,t)*2;return Math.max(1,e-n)}function zL(e){if(e.startsWith('"')||e.startsWith("'"))return"string";if(/^\d+$/.test(e))return"number";return"keyword"}function Jc(e){return e.replace(YL,"")}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,`
|
|
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+=sn;return{name:Math.max(Fc,e-a),health:t,cpu:r,mem:n,up:o,rst:i,pid:s}}function Hx(e){let t=Number.isFinite(e)&&e>0?e:0;return{ratio:Math.min(1,t),label:JD(t)}}function jx(e,t=lI){return`[${si(e.ratio,t)}] ${e.label.padStart(4)}`}function Gp(e,t){let r=e.reduce((l,d)=>Math.max(l,d.what.length),4),n=e.reduce((l,d)=>Math.max(l,d.reading.length),7),o=e.some((l)=>l.meter)?dI:0,i=pi*(o>0?3:2),s=Math.min(aI,Math.max(Bp,r)),a=Math.min(cI,Math.max(Up,n)),c=t-s-a-o-i;if(c<al&&a>Up)a=Math.max(Up,a-(al-c)),c=t-s-a-o-i;if(c<al&&s>Bp)s=Math.max(Bp,s-(al-c)),c=t-s-a-o-i;return{what:s,reading:a,meaning:Math.max(0,c),meter:o}}function Bx(e){return uI.has((e?.health.type??"").toLowerCase())}function Ux(e){if(e==="darwin")return"macOS";if(e==="win32")return"Windows";if(e==="linux")return"Linux";return e||"this computer"}function Wx(e,t){let r=Math.max(1,t),n=zD(e,r),o=n==="error"?"overloaded":n==="warning"?"busy":"not busy",i=n==="error"?`${r} cores are queued up \u2014 last minute ${jp(e)}`:n==="warning"?`${r} cores nearly full \u2014 last minute ${jp(e)}`:`${r} cores. Last minute ${jp(e)} (under ${r} is fine)`;return{what:"CPU work",reading:o,meaning:i,tone:n,meter:Hx(Mx(e,r))}}function Gx(e,t){let r=YD(e,t),n=`${Cs(e)} of ${Cs(t)}`,o=r==="error"?"Almost no RAM left for new work":r==="warning"?"RAM is getting tight":"RAM the computer can still give out",i=t>0?Math.max(0,(t-e)/t):0;return{what:"RAM leftover",reading:n,meaning:o,tone:r,meter:Hx(i)}}function Kx(e){let t=e?.state??"STOPPED";if(t==="FAILED")return"crashed";if(t==="STOPPING")return"stopping";if(t==="STARTING"||t==="RESTARTING")return"starting";if(Ts(t))return"up";return"off"}function qx(e){if(!e||!Ts(e.state))return"\u2014";if(e.health==="HEALTHY")return"ready";if(e.health==="UNHEALTHY")return"failing";if(e.state==="STARTING"||e.state==="RESTARTING")return"waiting";return"checking"}function Vx(e){if(e==="keychain")return"password store";if(e==="file")return"local file";return e||"unknown store"}function zx(e,t){let r=[{what:"Started",reading:String(e.live),meaning:e.live===0?"nothing is running yet":"process is up",tone:e.live>0?"success":"muted"}];if(t)r.push({what:"Ready",reading:String(e.healthy),meaning:e.live===0?"start services first":"passed their health check",tone:e.live>0&&e.healthy<e.live?"warning":e.healthy>0?"success":"muted"});if(e.starting>0)r.push({what:"Still starting",reading:String(e.starting),meaning:"not ready for traffic yet",tone:"warning"});if(e.stopping>0)r.push({what:"Stopping",reading:String(e.stopping),meaning:"shutting down",tone:"warning"});return r.push({what:"Crashed",reading:String(e.failed),meaning:e.failed>0?"open the service for the error":"none",tone:e.failed>0?"error":"muted"}),r.push({what:"Not started",reading:String(e.stopped),meaning:e.stopped>0?"off until you start them":"all are up",tone:"muted"}),r}var UD=0.85,WD=1.5,GD=0.2,KD=0.1,qD=5,Ls="\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588",XD=52,QD=64,ZD=72,eI=82,tI=90,rI=100,nI=2,oI=2,iI=2,sI=1,ll=8,Bp=8,aI=22,Up=6,cI=28,al=12,pi=2,lI=10,dI=18,uI;var dl=I(()=>{Ot();Lt();uI=new Set(["http","tcp","command"])});import{useEffect as pI,useRef as fI}from"react";import{jsxDEV as me,Fragment as hI}from"@opentui/react/jsx-dev-runtime";function mI(e,t){switch(t){case"primary":return{bg:e.primary,fg:mo(e.primary,e.inverse)};case"accent":return{bg:e.accent,fg:Lc(e)?Ac:mo(e.accent,e.inverse)};case"success":return{bg:e.success,fg:mo(e.success,e.inverse)};case"warning":return{bg:e.warning,fg:Lc(e)?Ac:mo(e.warning,e.inverse)};case"error":return{bg:e.error,fg:mo(e.error,e.inverse)};case"info":return{bg:e.info,fg:mo(e.info,e.inverse)};case"muted":return{bg:e.element,fg:e.text};case"ghost":return{bg:e.element,fg:e.muted};default:return{bg:e.element,fg:e.text}}}function Yx(e){let{palette:t,dir:r}=e;return me("box",{width:2,height:1,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,children:r==="left"?"\u2039":"\u203A"},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Ut(e){let{palette:t,children:r,backgroundColor:n,edge:o="bottom"}=e,i=ye(),s=e.ruled??!ho(i),a=n??t.element,c=me("box",{flexShrink:0,flexDirection:"column",backgroundColor:a,overflow:"hidden",children:r},void 0,!1,void 0,this);if(!s)return c;return me("box",{flexShrink:0,flexDirection:"column",overflow:"hidden",border:[...o==="both"?["top","bottom"]:[o]],borderStyle:"single",borderColor:t.border,children:c},void 0,!1,void 0,this)}function ul(e){let{palette:t,items:r,active:n,width:o,onPick:i,emphasis:s="ghost"}=e,a=r.map((u)=>ci(u.label)),c=Qb(a,n,Math.max(1,o)),l=c.start>0,d=c.end>=0&&c.end<r.length-1;return me("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[l?me(Yx,{palette:t,dir:"left"},void 0,!1,void 0,this):null,r.map((u,f)=>{if(f<c.start||f>c.end)return null;let p=f===n,m=s==="fill"&&p;return me(Be,{palette:t,label:u.label,tone:m?"primary":"ghost",bg:m?void 0:p?t.highlight:t.element,fg:m?void 0:p?t.primary:u.color??t.muted,onMouseDown:()=>i(f)},u.id,!1,void 0,this)}),d?me(Yx,{palette:t,dir:"right"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function Be(e){let{palette:t,label:r,tone:n="idle",fg:o,bg:i,onMouseDown:s}=e,a=mI(t,n);return me("box",{height:1,paddingLeft:1,paddingRight:1,backgroundColor:i??a.bg,overflow:"hidden",flexShrink:0,onMouseDown:s,children:me("text",{fg:o??a.fg,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function it(e){let{palette:t,hints:r}=e;return me("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:r.map((n)=>me("box",{height:1,paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:me("text",{wrapMode:"none",children:[me("span",{fg:t.primary,children:n.key},void 0,!1,void 0,this),me("span",{fg:t.muted,children:` ${n.label}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},`${n.key}-${n.label}`,!1,void 0,this))},void 0,!1,void 0,this)}function ut(e){let{palette:t,items:r,hints:n,ruled:o=!0}=e,i=me("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[r.map((s,a)=>me(Be,{palette:t,label:s.text,tone:s.tone,onMouseDown:s.onMouseDown},`${s.text}-${a}`,!1,void 0,this)),n&&n.length>0?me(hI,{children:[me("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),me(it,{palette:t,hints:n},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this);if(!o)return i;return me(Ut,{palette:t,backgroundColor:t.element,children:i},void 0,!1,void 0,this)}function pt(e){let{palette:t,title:r,termW:n,termH:o,preferW:i,preferH:s,anchor:a="center",borderColor:c,bottomTitle:l,gap:d,children:u}=e,f=ho(ye()),p=ol(n,o,i,s,a,!f);return me("box",{position:"absolute",left:p.left,top:p.top,width:p.width,height:p.height,border:!0,borderStyle:Kp,borderColor:c??t.borderActive,backgroundColor:t.panel,padding:f?0:1,title:r,titleColor:t.primary,bottomTitle:l,gap:f?0:d,flexDirection:"column",overflow:"hidden",children:u},void 0,!1,void 0,this)}function Ue(e){return{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:e.panel},viewportOptions:{backgroundColor:e.panel},contentOptions:{backgroundColor:e.panel},scrollbarOptions:{trackOptions:{foregroundColor:e.primary,backgroundColor:e.element}}}}function rt(e,t){if(!e)return;e.scrollBy({x:0,y:t})}function fi(e,t){let r=fI(null);return pI(()=>{let n=r.current;if(!n||e<0)return;let o=requestAnimationFrame(()=>{n.scrollChildIntoView(`${t}-${e}`)});return()=>cancelAnimationFrame(o)},[t,e]),r}function kt(e){let t=ye(),r=Ss(t),n=e.scroll?me("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(e.palette),children:me("box",{flexDirection:"column",overflow:"hidden",children:e.children},void 0,!1,void 0,this)},void 0,!1,void 0,this):e.children;return me("box",{flexGrow:1,border:!0,borderStyle:Kp,borderColor:e.borderColor??e.palette.border,title:e.title,titleColor:e.palette.primary,paddingLeft:t.pad,paddingRight:t.pad,paddingTop:r?0:t.pad,paddingBottom:r?0:t.pad,backgroundColor:e.palette.panel,flexDirection:"column",overflow:"hidden",children:n},void 0,!1,void 0,this)}function So(e){let{palette:t,label:r,value:n,tone:o="text"}=e,i=ye(),s=o==="success"?t.success:o==="warning"?t.warning:o==="error"?t.error:o==="muted"?t.muted:t.text;return me("box",{height:i.rowH,flexDirection:"row",overflow:"hidden",children:[me("box",{width:14,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),me("box",{flexGrow:1,overflow:"hidden",children:me("text",{fg:s,wrapMode:"none",children:n},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function pl(e){let{palette:t,title:r,body:n,hint:o}=e;return me("box",{paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",border:!0,borderStyle:Kp,borderColor:t.borderActive,titleColor:t.primary,children:[me("text",{fg:t.primary,children:r},void 0,!1,void 0,this),me("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),o?me("text",{fg:t.muted,wrapMode:"word",children:o},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Kp="rounded";var ke=I(()=>{Mt();vr();In();Er();tt()});import{jsxDEV as $e,Fragment as vI}from"@opentui/react/jsx-dev-runtime";function Xx(e){let{palette:t,cfg:r,snap:n,google:o,profile:i,reveal:s,width:a}=e,c=cl(n,r?Object.keys(r.services):void 0),l=n?.proxy.running===!0,d=o?.adcAvailable===!0,u=a<nl,f=Re(r?.project.name||"local",u?18:22),p=Re(i||n?.profile||"no profile",16),m=ix({width:a,running:c.running,total:c.total,proxyOn:l,proxyAddress:n?.proxy.address??"",mcpOn:n?.mcp?.running===!0,adc:d,reveal:s}).filter((b)=>!b.hide&&b.label!=="").map((b)=>$e(Be,{palette:t,label:b.label,tone:b.tone},b.label,!1,void 0,this)),h=Ss(ye()),v=$e("box",{height:1,flexGrow:1,overflow:"hidden",backgroundColor:t.panel,paddingLeft:1,children:$e("text",{wrapMode:"none",children:[$e("span",{fg:t.primary,children:er()},void 0,!1,void 0,this),$e("span",{fg:t.muted,children:` ${f}`},void 0,!1,void 0,this),$e("span",{fg:t.accent,children:` ${p}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this);return $e(Ut,{palette:t,backgroundColor:t.panel,ruled:!h,children:$e("box",{height:u?2:1,flexDirection:"column",backgroundColor:t.panel,overflow:"hidden",children:[$e("box",{height:1,flexDirection:"row",overflow:"hidden",children:[v,u?null:m]},void 0,!0,void 0,this),u?$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.panel,children:m},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function Qx(e){let{palette:t,screen:r,width:n,onSelect:o}=e,i=n>=Jx+gI,s=i?Math.max(1,n-Jx):n,a=bo.map((l)=>({id:l.id,label:Xb(l.label,s)})),c=Zb(r);return $e(Ut,{palette:t,backgroundColor:t.element,children:$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[$e(ul,{palette:t,items:a,active:c,width:s,onPick:(l)=>{let d=bo[l];if(d)o(d.id)}},void 0,!1,void 0,this),i?$e(vI,{children:[$e("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),$e(it,{palette:t,hints:[Gr]},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function Zx(e){let{palette:t,screen:r,overlay:n,status:o,paused:i,errorOnly:s,width:a,copyKey:c}=e,l=Ss(ye()),d=Math.max(18,Math.floor(a*0.42)),u=sx(Ax(r,n,c),d),f=ax(o);return $e(Ut,{palette:t,backgroundColor:t.panel,edge:"top",ruled:!l,children:$e("box",{height:1,backgroundColor:t.panel,overflow:"hidden",children:$e(ut,{ruled:!1,palette:t,items:[{text:r,tone:"ghost"},{text:i?"PAUSED":"LIVE",tone:i?"warning":"success"},...s?[{text:"ERROR+",tone:"error"}]:[],...o===""?[]:[{text:Re(o,Math.max(16,a-d-28)),tone:f}]],hints:u},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function ze(e){return $e("box",{flexGrow:1,padding:1,overflow:"hidden",children:$e(pl,{palette:e.palette,title:e.title,body:e.body,hint:e.hint},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function fl(e){return $e("box",{flexGrow:1,padding:1,overflow:"hidden",children:$e("text",{fg:e.palette.text,children:e.label},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function ew(e){return $e("box",{flexGrow:1,padding:1,flexDirection:"column",overflow:"hidden",children:[$e("text",{fg:e.palette.error,children:e.title},void 0,!1,void 0,this),$e("text",{fg:e.palette.text,wrapMode:"word",children:e.body},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Jx,gI=8;var Yt=I(()=>{Mt();vr();wo();Ot();In();dl();ke();Er();Jx=Gr.key.length+Gr.label.length+3});function yI(){if(process.platform==="darwin")return[["pbcopy"]];if(process.platform==="win32")return[["clip"]];return[["wl-copy"],["xclip","-selection","clipboard"],["xsel","--clipboard","--input"]]}function bI(){return yI().filter((e)=>{let t=e[0];return typeof t==="string"&&Bun.which(t)!==null})}function xI(e){return`\x1B]52;c;${Buffer.from(e,"utf8").toString("base64")}\x07`}function tw(){if(process.platform==="linux")return"install wl-clipboard (Wayland) or xclip (X11)";return"no clipboard helper found"}async function ml(e){let t=bI(),r;for(let n of t)try{await SI(n,e);return}catch(o){r=o instanceof Error?o:Error(String(o))}if(process.platform!=="win32")try{await wI(e);return}catch(n){r=n instanceof Error?n:Error(String(n))}throw Te(lt,"clipboard unavailable",r?`${tw()} (${r.message})`:tw())}async function wI(e){let t=xI(e);try{await Bun.write("/dev/tty",t)}catch{if(!process.stderr.write(t))throw Error("terminal clipboard write failed")}}async function SI(e,t){let r=Bun.spawn(e,{stdin:"pipe",stdout:"ignore",stderr:"ignore"});r.stdin.write(t),await r.stdin.end();let n=await r.exited;if(n!==0)throw Error(`${e[0]} exited ${n}`)}var qp=I(()=>{te()});function Vp(e){return e==="slash"||e==="config-edit"||e==="setup-wizard"}function rw(e,t){return mn(e,t.keybinds.leader??Ao("x"))}function nw(e,t){return mn(e,t.keybinds.command_list??Ao("p"))}function an(e,t){return mn(e,t.keybinds.command??"/")||e.name==="/"}function ow(e,t){return mn(e,t.keybinds.help??"?")||e.sequence==="?"}function iw(e,t){return mn(e,t.keybinds.search??"f")}function sw(e){return(e.name??"").toLowerCase()==="q"&&!e.ctrl&&!e.meta&&!e.super&&!e.alt&&!e.option}function aw(e){let t=e.name??"";return t==="R"||e.shift===!0&&t.toLowerCase()==="r"}function cw(e){return Or(e)&&(e.name??"").toLowerCase()==="l"}function lw(e,t){return mn(e,t.keybinds.copy??Zr())}function dw(e,t,r=kI){return t>0&&e-t<=r}function Mn(e,t,r,n){return mn(e,t.keybinds[r]??n)}function Ds(e){let t=(e.name??"").toLowerCase();return t==="pagedown"||Or(e)&&t==="d"}function Is(e){let t=(e.name??"").toLowerCase();return t==="pageup"||Or(e)&&t==="u"}var kI=2000;var hl=I(()=>{Rr()});function mi(e){return`http://127.0.0.1:${e}/mcp`}function zp(e){if(e==="")return{};return{Authorization:`Bearer ${e}`}}function Yp(e,t){return JSON.stringify({mcpServers:{devctl:{type:"http",url:e,headers:zp(t)}}},null,2)}function Jp(e,t){return JSON.stringify({mcpServers:{devctl:{url:e,headers:zp(t)}}},null,2)}function Xp(e,t){return JSON.stringify({mcp:{devctl:{type:"remote",url:e,headers:zp(t),enabled:!0}}},null,2)}function Qp(e,t){let r=t===""?"":`
|
|
2213
|
+
`),o=[];for(let i of n)if(i.length<=r)o.push(i);else o.push(...JL(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=` ${KL}${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=Lr){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=Lr){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 JL(e,t){let r=[],n=e;while(n.length>t){let o=n.slice(0,t),i=XL(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 XL(e){let t=e.lastIndexOf(" "),r=e.lastIndexOf("\t"),n=e.lastIndexOf(","),o=Math.max(t,r,n);if(o<Math.floor(e.length*qL))return-1;return e[o]===","?o+1:o}function QL(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function ZL(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(QL(r),"gi")}function eD(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(VL.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:zL(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=ZL(t,r);return l?eD(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 tD.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 rD(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},...rD(t,n)]}function nD(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},...nD(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,Lr=200,GL=2,Ib=1,KL="\u25B8",$b=2,qL=0.4,VL,YL,tD,Wb;var gr=I(()=>{ga();Ot();Lt();VL=/("[^"]*"|'[^']*'|\b(?:ERROR|FATAL|WARN(?:ING)?|FAIL(?:ED)?)\b|\b\d{3,5}\b)/gi;YL=/\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;tD=new Set(["auth","devctl","proxy"]),Wb=["devctl","mcp","auth"]});function ci(e){return e.length+iD}function Xb(e,t){if(t<aD)return Re(e,3);if(t<sD)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 oD;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 oD=9,iD=2,rl=2,sD=80,aD=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?fD:0;return r+n+uD+n+pD+n}function di(e){return Math.max(lD,e-dD*2)}function ix(e){let t=e.width<cD,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 mD(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=mD(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(gD.test(e))return"error";if(e.includes("session only"))return"warning";if(hD.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,cD=60,lD=8,dD=6,rx=1,uD=1,pD=1,fD=1,iW,hD,gD;var vr=I(()=>{Ot();gr();In();iW=ox(nl-1);hD=/^(started|stopped|restarted|refreshed|cleared|copied|exported|jumped|proxy started|proxy stopped|secrets|re-running|theme |mouse |leader |display |restored)/i,gD=/\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:LD(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 LD(e,t){if(t==="")return 1;let r=e.name.toLowerCase(),n=e.aliases.map((o)=>o.toLowerCase());return ID(r,n,t)||MD(r,n,t)||OD(r,n,t)||$D(r,n,t)||DD(e.suggest,t)||FD(e.desc.toLowerCase(),t)}function DD(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 ID(e,t,r){if(e===r)return vD;return t.includes(r)?yD:0}function MD(e,t,r){if(e.startsWith(r))return bD;if(t.some((i)=>i.startsWith(r)))return xD;let n=e.split("-");return n.length>1&&n.some((i)=>i.startsWith(r))?wD:0}function OD(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 $D(e,t,r){if(r.length<_D)return 0;let n=ux(e,r);if(n>0)return SD+Math.min(n,AD);return t.some((o)=>ux(o,r)>0)?kD: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 FD(e,t){if(t.length<RD)return 0;if(e.split(/[^a-z0-9+]+/).filter((n)=>n.length>=ED).some((n)=>n.startsWith(t)))return CD;return t.length>=TD&&e.includes(t)?PD: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 vD=100,yD=90,bD=80,xD=70,wD=60,px=50,fx=45,SD=30,kD=28,CD=20,PD=10,mx=2,_D=3,RD=4,ED=4,TD=4,AD=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 ND+r+HD}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 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 xx(e,t){return{...e,hint:t.token,desc:t.desc,usage:void 0,suggest:void 0}}function BD(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 jD(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 BD(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 UD(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 UD(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,ND=3,HD=2,Op=22;var wo=I(()=>{Er();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 zD(e,t,r){if(!Number.isFinite(e))return"success";if(e>r)return"error";if(e>t)return"warning";return"success"}function YD(e,t){return zD(Mx(e,t),WD,GD)}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 JD(e,t){let r=t>0?e/t:1;if(r<qD)return"error";if(r<KD)return"warning";return"success"}function jp(e){if(!Number.isFinite(e)||e<0)return"\u2014";return e.toFixed(2)}function XD(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=VD){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-oI-t*2-iI-sI-aI)}function Nx(e){let t=e>=QD,r=e>=ZD,n=e>=eI,o=e>=tI,i=e>=rI,s=e>=nI,a=Ar+$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:XD(t)}}function jx(e,t=dI){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)?uI:0,i=pi*(o>0?3:2),s=Math.min(cI,Math.max(Bp,r)),a=Math.min(lI,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 pI.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=YD(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=JD(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 WD=0.85,GD=1.5,KD=0.2,qD=0.1,VD=5,Ls="\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588",QD=52,ZD=64,eI=72,tI=82,rI=90,nI=100,oI=2,iI=2,sI=2,aI=1,ll=8,Bp=8,cI=22,Up=6,lI=28,al=12,pi=2,dI=10,uI=18,pI;var dl=I(()=>{Ot();Lt();pI=new Set(["http","tcp","command"])});import{useEffect as fI,useRef as mI}from"react";import{jsxDEV as me,Fragment as gI}from"@opentui/react/jsx-dev-runtime";function hI(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=hI(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(gI,{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=mI(null);return fI(()=>{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();Tr();tt()});import{jsxDEV as $e,Fragment as yI}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+vI,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(yI,{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,vI=8;var Yt=I(()=>{Mt();vr();wo();Ot();In();dl();ke();Tr();Jx=Gr.key.length+Gr.label.length+3});function bI(){if(process.platform==="darwin")return[["pbcopy"]];if(process.platform==="win32")return[["clip"]];return[["wl-copy"],["xclip","-selection","clipboard"],["xsel","--clipboard","--input"]]}function xI(){return bI().filter((e)=>{let t=e[0];return typeof t==="string"&&Bun.which(t)!==null})}function wI(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=xI(),r;for(let n of t)try{await kI(n,e);return}catch(o){r=o instanceof Error?o:Error(String(o))}if(process.platform!=="win32")try{await SI(e);return}catch(n){r=n instanceof Error?n:Error(String(n))}throw Te(lt,"clipboard unavailable",r?`${tw()} (${r.message})`:tw())}async function SI(e){let t=wI(e);try{await Bun.write("/dev/tty",t)}catch{if(!process.stderr.write(t))throw Error("terminal clipboard write failed")}}async function kI(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 $r(e)&&(e.name??"").toLowerCase()==="l"}function lw(e,t){return hn(e,t.keybinds.copy??Zr())}function dw(e,t,r=CI){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"||$r(e)&&t==="d"}function Is(e){let t=(e.name??"").toLowerCase();return t==="pageup"||$r(e)&&t==="u"}var CI=2000;var hl=I(()=>{Er()});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===""?"":`
|
|
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(an(t,n))return hi(e),!0;if(C==="escape")return i(),!0;if(r==="log-details"&&C==="return"){let B=e.logDetail?.request_id?.trim()??"";if(B!=="")e.setLogSearch(B),e.setScreen("logs"),e.closeOverlay(),e.setStatus(`tracing ${B}`);return!0}let j=r==="log-details"?d.current:u.current;if(C==="down"||C==="j")return rt(j,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(j,-n.scroll_speed),!0;if(Ds(t))return rt(j,di(o)),!0;if(Is(t))return rt(j,-di(o)),!0;return!0}if(r==="route-details"){if(an(t,n))return hi(e),!0;if(C==="escape"||C==="return")return i(),!0;if(C==="down"||C==="j")return rt(f.current,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(f.current,-n.scroll_speed),!0;return!0}if(r==="plan"){if(an(t,n))return hi(e),!0;if(C==="escape"||C==="return"&&!l)return i(),!0;if(C==="down"||C==="j")return rt(p.current,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(p.current,-n.scroll_speed),!0;return!0}if(r==="help"){if(an(t,n))return hi(e),!0;if(C==="escape")return i(),!0;if(C==="down"||C==="j")return rt(m.current,1),!0;if(C==="up"||C==="k")return rt(m.current,-1),!0;if(C==="pagedown"||Ds(t))return rt(m.current,pf),!0;if(C==="pageup"||Is(t))return rt(m.current,-pf),!0;return!0}if(r==="themes"){if(an(t,n))return h(),hi(e),!0;if(C==="escape")return h(),i(),!0;if(C==="down"||C==="j")return v((j)=>{let B=Math.min(j+1,St.length-1),z=St[B];if(z)b(z);return B}),!0;if(C==="up"||C==="k")return v((j)=>{let B=Math.max(0,j-1),z=St[B];if(z)b(z);return B}),!0;if(C==="return"){let j=St[g%St.length];if(j)_(j)}return!0}if(r==="leader"){if(an(t,n)){if(R.current)clearTimeout(R.current);return hi(e),!0}if(R.current)clearTimeout(R.current);S("none");let j=gx(C),B=j?Wr(j):void 0;if(B)y(B,[]);return!0}if(r==="config-edit"){if(C==="escape")return x(""),i(),!0;if(Or(t)&&C==="s")return w(),!0;return!0}if(Vp(r)){if(C==="escape")return i(),!0;if(r==="slash"&&C==="down")return k((j)=>Math.min(Math.max(D.length-1,0),j+1)),!0;if(r==="slash"&&C==="up")return k((j)=>Math.max(0,j-1)),!0;if(r==="slash"&&C==="tab"){if(t.shift)return k((B)=>Math.max(0,B-1)),!0;let j=ui(D,F);if(j)O(Rx(j));return!0}if(r==="slash"&&C==="return")return Z(),!0;return!0}return!0}var ww=I(()=>{vr();wo();hl();Rr();ke();ff();tt()});var yl=I(()=>{Zu()});function Sw(e,t){if(t.length===0)return[];let r=new Set(t);return co(e,t).filter((n)=>!r.has(n))}function kw(e,t,r){let n=r!==""&&e.profiles[r]?r:"";if(t.length>0)return{services:t,profile:n};if(n!=="")return{services:[...e.profiles[r]?.services??[]],profile:n};return{services:Object.keys(e.services),profile:""}}function bl(e){return e.waves.flat().join(" \u2192 ")}function Cw(e){let t=bl(e);if(t==="")return"Started selected services";return`Started ${t}`}function Pw(e){let t=bl(e);if(t==="")return"Stopped running services";return`Stopped ${t}`}function _w(e,t,r,n="start"){let o=r?.services[e],i=tr(o);if(o?.last_error&&n!=="stop")return o.last_error;if(n==="stop"){if(i==="STOPPED")return"stopped";if(i==="STOPPING")return"stopping now";if(i==="FAILED")return"already failed";return"queued"}if(i==="HEALTHY")return"ready";if(i==="STARTING")return"starting now";if(i==="RUNNING")return"up, waiting for health";if(i==="UNHEALTHY")return"unhealthy \u2014 retrying health check";if(i==="STOPPING")return"stopping first";if(i==="FAILED")return"failed \u2014 later waves will not start";let a=(t.steps.find((c)=>c.name===e)?.dependencies??[]).filter((c)=>{let l=tr(r?.services[zt(c)]);return gn(c)==="service_healthy"?l!=="HEALTHY":l==="STOPPED"||l==="FAILED"}).map(zt);if(a.length>0)return`waits for ${a.join(", ")}`;return"queued"}function Fs(e,t,r="start"){if(e.length===0)return"completed";let n=e.map((i)=>t?.services[i]);if(n.some((i)=>i?.state==="FAILED"))return"failed";if(r==="stop"){if(n.every((i)=>!i||i.state==="STOPPED"||i.state==="UNKNOWN"))return"completed";if(n.some((i)=>i?.state==="STOPPING"))return"active";return"queued"}let o=(i)=>Boolean(i&&(i.state==="HEALTHY"||i.health==="HEALTHY"||i.state==="RUNNING"&&i.health!=="UNHEALTHY"));if(n.every(o))return"completed";if(n.some((i)=>i?.state==="UNHEALTHY"||i?.health==="UNHEALTHY"))return"unhealthy";if(n.some((i)=>i?.state==="STARTING"||i?.state==="RUNNING"||i?.state==="RESTARTING"))return"active";return"queued"}function Rw(e,t,r="start"){let o=e.waves.flat().length,i=0,s=0,a=0,c=0;for(let m=0;m<e.waves.length;m++){let h=e.waves[m]??[],v=Fs(h,t,r);if(v==="active"||v==="unhealthy"||v==="failed")c=m;else if(v==="completed"&&c===m&&m<e.waves.length-1)c=m+1;for(let b of h){let g=t?.services[b];if(r==="stop"?!g||g.state==="STOPPED"||g.state==="UNKNOWN":Boolean(g&&(g.state==="HEALTHY"||g.health==="HEALTHY"||g.state==="RUNNING"&&g.health!=="UNHEALTHY")))i++;else if(g?.state==="FAILED")s++;else if(g?.state==="STARTING"||g?.state==="RUNNING"||g?.state==="UNHEALTHY"||g?.health==="UNHEALTHY"||g?.state==="STOPPING"||g?.state==="RESTARTING")a++}}let l=o>0?Math.round(i/o*100):100,d=16,u=Math.round(l/100*d),f="\u2588".repeat(u)+"\u2591".repeat(Math.max(0,d-u)),p=i===o&&o>0;return{total:o,ready:i,active:a,failed:s,percent:l,progressBar:f,currentWaveIndex:c,totalWaves:e.waves.length,isComplete:p}}function Ew(e,t,r){let n=r==="completed"?"\u2713 Completed":r==="failed"?"\u2717 Failed":r==="unhealthy"?"\u26A0 Unhealthy":r==="active"?"\u23F3 In Progress":"\u25CB Queued";return`${e==="stop"?t===0?"Wave 1 (Stop First)":`Wave ${t+1}`:t===0?"Wave 1 (Start First)":`Wave ${t+1}`} \xB7 ${n}`}function Tw(e,t,r,n){let o=n?` \xB7 Profile ${n}`:"";if(r)return`${e==="stop"?"Shutdown":"Startup"} Failed (${r})`;if(t)return`${e==="stop"?"Stopping Services":e==="restart"?"Restarting Pipeline":"Starting Pipeline"}${o}`;return`${e==="stop"?"Shutdown Complete":"Startup Complete"}${o}`}function Aw(e,t){if(e)return{primary:"Working\u2026 esc hide this panel (keeps running in background)",secondary:"Services are transitioning. You can dismiss anytime without stopping them."};if(t)return{primary:"enter or esc back to dashboard",secondary:"Execution stopped. Check logs or open Doctor to resolve errors."};return{primary:"enter or esc back to dashboard",secondary:"All waves finished. Click here or press enter to continue."}}var xl=I(()=>{ht();At();Lt()});function Lw(e,t){let r=(t.name??"").toLowerCase();if(e.screen==="mcp"&&e.listCursor===1&&r.length===1&&r>="0"&&r<="9")return e.setMcpPortDraft((n)=>Ty(n,r)),!0;return!1}function Dw(e,t){let r=(t.name??"").toLowerCase(),{screen:n,overlay:o,tui:i,cfg:s,controller:a,profile:c,listCursor:l,names:d,listCount:u,height:f,checked:p,detailName:m,bootErrorMissing:h,bootError:v,logSearchFocused:b,logSources:g,logService:_,logShowTimestamps:R,logShowMeta:S,logWrap:y,showSystemLogs:x,logPinned:w,dashboardLogCursor:k,applyLogCursor:D,applyDashboardLogCursor:O,jumpToLatestLogs:F,toggleSystemLogs:Z,clearLogs:W,setLogService:C,setLogShowTimestamps:j,setLogShowMeta:B,setPaused:z,setErrorOnly:de,setLogWrap:Ee,beginStart:Ne,beginStop:ne,beginRestart:H,applyMcpPortDraft:ae,toggleMcp:Q,toggleMcpTool:nt,copyFocusedMcpSnippet:ft,persistMcpPort:ie,restartMcpOnPort:Le,setMcpPortDraft:Me,settingRows:Ke,persistPrefs:gt,cycleSetting:Xe,toggleMouse:Ct,setDoctorTick:ge,refreshAuth:vt,configScrollRef:at,detailScrollRef:yt,handleEnter:rr,setScreen:Qe,setChecked:Xt,setStatus:qe,setSelected:mt,setProfile:br,refresh:Kt,toggleChecked:xr,createStarterConfig:nr,startWizard:qt,setConfirmKind:or,setConfirmDetail:ir,setOverlay:sr,openConfigBuffer:Vt,runCommand:ce}=e,K=(n==="logs"||n==="dashboard")&&!b;if(K&&(r==="left"||r==="["||n==="logs"&&r==="h")){C(Dp(g,_,-1));return}if(K&&(r==="right"||r==="]"||n==="logs"&&r==="l")){C(Dp(g,_,1));return}if((n==="dashboard"||n==="setup")&&r==="o"){Qe("profiles");return}if((n==="dashboard"||n==="services")&&(r==="*"||t.shift&&r==="8")){Xt([...d]),qe(`Selected ${d.length} services`);return}if((n==="dashboard"||n==="services")&&(r==="-"||r==="_"||r==="minus")){Xt([]),qe("Selection cleared");return}if((n==="dashboard"||n==="services")&&r==="n"){Ne(Bc(p,d[l]??""),c);return}if((n==="dashboard"||n==="services")&&r==="x"){ne(Bc(p,d[l]??""));return}if((n==="dashboard"||n==="services"||n==="detail")&&aw(t)){let A=Bc(p,d[l]??m);if(s&&Sw(s,A).length>0){or("restart-cascade"),ir({services:A}),sr("confirm");return}H(A,c);return}if((n==="dashboard"||n==="services")&&r==="r"){Kt(),qe("Refreshed");return}if(n==="auth"&&r==="r"){vt();return}if(n==="logs"&&r==="t"){let A=!R;j(A),gt({log_timestamps:A},A?"timestamps on":"timestamps off");return}if(n==="logs"&&r==="m"){let A=!S;B(A),gt({log_metadata:A},A?"metadata on":"metadata off");return}if(r==="down"||r==="j"){if(n==="mcp"&&l===1)ae();if(n==="config"){if(u>0)mt((A)=>Math.min(Math.max(u-1,0),A+1));rt(at.current,i.scroll_speed);return}if(n==="detail"){rt(yt.current,i.scroll_speed);return}if(n==="logs"){D(l+1);return}if(n==="dashboard"&&k>=0){O(k+1);return}mt((A)=>Math.min(Math.max(u-1,0),A+1));return}if(r==="up"||r==="k"){if(n==="mcp"&&l===1)ae();if(n==="config"){if(u>0)mt((A)=>Math.max(0,Math.min(A,Math.max(u-1,0))-1));rt(at.current,-i.scroll_speed);return}if(n==="detail"){rt(yt.current,-i.scroll_speed);return}if(n==="logs"){D(l-1);return}if(n==="dashboard"&&k>=0){O(k-1);return}mt((A)=>Math.max(0,Math.min(A,Math.max(u-1,0))-1));return}if(Ds(t)){let A=di(f);if(n==="config"){rt(at.current,A);return}if(n==="detail"){rt(yt.current,A);return}if(n==="logs"){D(l+A);return}if(n==="dashboard"&&(w||k>=0)){O(Math.max(0,k)+A);return}mt((ve)=>Math.min(Math.max(u-1,0),ve+A));return}if(Is(t)){let A=di(f);if(n==="config"){rt(at.current,-A);return}if(n==="detail"){rt(yt.current,-A);return}if(n==="logs"){D(l-A);return}if(n==="dashboard"&&(w||k>=0)){O(Math.max(0,k)-A);return}mt((ve)=>Math.max(0,ve-A));return}if(n==="proxy"&&r==="n"){a?.proxyStart().then(async()=>{qe("Proxy started"),await Kt()});return}if(n==="proxy"&&r==="x"){a?.proxyStop().then(async()=>{qe("Proxy stopped"),await Kt()});return}if(n==="detail"&&m!==""){if(r==="n"){Ne([m],c);return}if(r==="x"){ne([m]);return}if(r==="o"){Qe("config");return}if(r==="l"){C(m),Qe("logs");return}}if(n==="mcp"&&l===1&&(r==="left"||r==="h")){let A=Rn(ae()-1);ie(A),Le(A);return}if(n==="mcp"&&l===1&&(r==="right"||r==="l")){let A=Rn(ae()+1);ie(A),Le(A);return}if(n==="mcp"&&l===1&&(r==="backspace"||r==="delete")){Me((A)=>Ay(A));return}if(n==="settings"&&(r==="left"||r==="h")){Xe(-1,l);return}if(n==="settings"&&(r==="right"||r==="l")){Xe(1,l);return}if(r==="space"){if(n==="profiles"){let J=Object.keys(s?.profiles??{}).sort()[l];if(J)br(J),qe(`Profile ${J}`);return}if(n==="mcp"){if(l===0){Q();return}let ve=Os(l);if(ve){nt(ve);return}ft(l);return}if(n==="logs")return;if(n==="settings"){if(Ln(Ke,l)?.id==="mouse")Ct();return}let A=d[l];if(A)xr(A);return}if(r==="return"){if(n==="setup"&&!a){if(!h){qe(v||"Existing configuration is invalid \u2014 fix it and restart devctl.");return}qt();return}if(n==="setup"&&a){Ne([],c);return}rr();return}if(Mn(t,i,"services","s")&&o==="none"&&n!=="logs"){Qe("services");return}if(Mn(t,i,"logs","l")&&o==="none"&&n!=="detail"&&n!=="logs"&&n!=="settings"){Qe("logs");return}if(Mn(t,i,"auth","a")&&o==="none"){Qe("auth");return}if(Mn(t,i,"proxy","p")&&o==="none"&&n!=="logs"){Qe("proxy");return}if(Mn(t,i,"doctor","d")&&o==="none"&&n!=="doctor"){Qe("doctor");return}if(Mn(t,i,"config","c")&&o==="none"){Qe("config");return}if(Mn(t,i,"setup","u")&&o==="none"){Qe("setup");return}if(n==="config"&&r==="e"){ce(Wr("/edit")??{name:"edit",aliases:[],desc:"",leader:"",group:"ui"},[]);return}if(n==="config"&&r==="v"){Vt();return}if(n==="doctor"&&r==="r"){ge((A)=>A+1),qe("Re-running doctor");return}if(n==="logs"&&r==="p"){z((A)=>!A);return}if((n==="logs"||n==="dashboard")&&r==="g"){F();return}if((n==="logs"||n==="dashboard")&&r==="w"){let A=Qc(y);Ee(A),qe(`Log ${el(A)}`);return}if((n==="logs"||n==="dashboard")&&r==="e"){de((A)=>!A);return}if((n==="logs"||n==="dashboard")&&r==="i"){Z(),qe(x?"Hiding internal auth/mcp/devctl/proxy logs":"Showing internal logs");return}if((n==="logs"||n==="dashboard")&&cw(t))W()}var Iw=I(()=>{yl();vr();gr();xl();Lt();hl();ke();$s();Er()});import{useKeyboard as WI}from"@opentui/react";import{useCallback as mf}from"react";function Mw({tui:e,controller:t,cfg:r,snap:n,ui:o,logView:i,lifecycleActions:s,mcp:a,preferences:c,diagnostics:l,refs:d}){let{screen:u,onQuit:f,closeOverlay:p,confirmKind:m,confirmDetail:h,portTarget:v,profile:b,listCursor:g,names:_,runCommand:R,copySelection:S,setOverlay:y,setConfirmKind:x,setConfirmDetail:w,setPortTarget:k,setLogDetail:D,setProfile:O,setStatus:F,setSlashIndex:Z,setQuery:W,setSlashPicker:C,setScreen:j,freePort:B,openDetail:z}=o,{logSlice:de,logSliceB:Ee,logSearchFocused:Ne,logsFullscreen:ne,setLogSearchFocused:H,setLogsFullscreen:ae,splitLogs:Q,splitFocus:nt,toggleSplitLogs:ft,cycleSplitFocus:ie,setActiveLogService:Le,logService:Me,logServiceB:Ke}=i,{beginStart:gt,beginRestart:Xe,planBusy:Ct}=s,{applyMcpPortDraft:ge,toggleMcp:vt,toggleMcpTool:at,copyFocusedMcpSnippet:yt}=a,{settingRows:rr,activateSetting:Qe,applyFont:Xt,applyReset:qe,fontSize:mt,revertThemePreview:br,setThemeName:Kt,leaderMs:xr}=c,{doctor:nr}=l,{interruptArmedAt:qt,leaderTimer:or}=d,ir=mf(()=>{if(r?.shutdown.stop_services_on_exit===void 0){x("quit"),y("confirm");return}f(r.shutdown.stop_services_on_exit===!1)},[r,f,x,y]),sr=mf((ce)=>{if(m==="quit"){f(!1);return}if(m==="reload"){p();let K=n?.restart_required??[];if(K.length>0)Xe(K,b);return}if(m==="restart-cascade"){let K=h?.services??[];p(),Xe(K,b,ce==="cascade");return}if(m==="reset-prefs"){p(),qe();return}if(m==="free-port"){let K=v;if(p(),!K)return;B(K).then(()=>{F(`Stopped ${K.command} (pid ${K.pid}) on port ${K.port}`),l.setDoctorTick((A)=>A+1)}).catch((A)=>{F(L(A))});return}p(),gt([],b)},[qe,Xe,gt,p,h,m,l,B,f,v,b,F,n]),Vt=mf(()=>{if(u==="logs"){let K=(Q&&nt===1?Ee:de)[g];if(K)D(K),y("log-details");return}if(u==="profiles"){let A=Object.keys(r?.profiles??{}).sort()[g];if(A)O(A),x("start-profile"),y("confirm");return}if(u==="doctor"){let K=nr?.checks[g];if(K?.action?.kind==="free-port"){let A=K.action.holder;k(A),w({port:A.port,pid:A.pid,process:A.command}),x("free-port"),y("confirm")}return}if(u==="mcp"){if(g===0){vt();return}if(g===1){ge();return}let K=Os(g);if(K){at(K);return}yt(g);return}if(u==="settings"){let K=Ln(rr,g);if(K)Qe(K);return}if(u==="config"){let K=Object.keys(r?.tasks??{}).sort()[g],A=Wr("run");if(K&&A)R(A,[K]);return}if(u==="dashboard"&&jc(n)){gt([],b);return}let ce=_[g];if(ce&&(u==="dashboard"||u==="services"))z(ce)},[Qe,ge,gt,r,yt,nr,g,de,Ee,_,z,b,R,u,w,x,D,y,k,O,rr,n,nt,Q,vt,at]);WI((ce)=>{let K=(ce.name??"").toLowerCase();if(lw(ce,e)){S();return}if(Or(ce)&&(K==="="||K==="+"||K==="plus")){Xt(xs(mt,1));return}if(Or(ce)&&(K==="-"||K==="_"||K==="minus")){Xt(xs(mt,-1));return}if(Or(ce)&&K==="0"){Xt(Mc().font_size);return}if(u==="logs"&&!Ne&&(ce.sequence==="\\"||K==="\\")){ft(),F(Q?"Single log pane":"Split log panes");return}if(u==="logs"&&!Ne&&(ce.sequence==="|"||K==="|")){ie();return}if(xw({...o,tui:e,confirmAction:sr,planBusy:Ct,leaderTimer:or,logDetailsScrollRef:d.logDetailsScrollRef,scrollTextScrollRef:d.scrollTextScrollRef,routeDetailsScrollRef:d.routeDetailsScrollRef,planScrollRef:d.planScrollRef,helpScrollRef:d.helpScrollRef,revertThemePreview:br,setThemeName:Kt},ce))return;if(Ne){if(K==="escape")H(!1);return}if(rw(ce,e)){y("leader"),or.current=setTimeout(()=>y("none"),xr);return}if(nw(ce,e)||an(ce,e)){W(""),Z(0),C("commands"),y("slash");return}if(ow(ce,e)){y("help");return}if(iw(ce,e)||u==="logs"&&K==="f"&&!ce.ctrl&&!ce.meta){j("logs"),H(!0);return}if(K==="escape"||sw(ce)){if(K==="escape"){if(ne){ae(!1);return}if(u==="detail"){j("services");return}if(u==="setup"&&t){j("dashboard");return}}let J=Date.now();if(dw(J,qt.current)){qt.current=0,ir();return}qt.current=J,F(K==="q"?"q again to quit":"Esc again to quit");return}if(K==="z"&&(u==="logs"||u==="dashboard")&&!ce.ctrl&&!ce.meta){j("logs"),ae((J)=>u==="logs"?!J:!0);return}if(K==="tab"){j(ce.shift?Cx(u):kx(u));return}let A={...o,tui:e,controller:t,cfg:r,snap:n,...i,...s,...a,...c,logService:Q&&nt===1?Ke:Me,setLogService:Le,setDoctorTick:l.setDoctorTick,refreshAuth:l.refreshAuth,configScrollRef:d.configScrollRef,detailScrollRef:d.detailScrollRef,handleEnter:Vt};if(Lw(A,ce))return;let ve=tx(K);if(ve){j(ve);return}Dw(A,ce)})}var Ow=I(()=>{te();wo();In();Lt();hl();$s();Er();Rr();ww();Iw()});import{parse as GI,stringify as KI}from"yaml";function wl(e,t="imported"){let r=GI(e);if(!r||typeof r!=="object"||Array.isArray(r))throw Error("compose file is not a mapping");let n=r,o=gi(n.services)?n.services:{},i=[];if(n.networks!==void 0)i.push({service:"(root)",field:"networks",reason:$w.networks??"not modeled"});let s={},a=[];for(let[l,d]of Object.entries(o)){if(!gi(d))continue;let u=qI(l,d,i);if(s[l]=u.body,!u.container)a.push(l)}return{yaml:KI({version:1,project:{name:t},services:s,profiles:{default:{services:a}}}),dropped:i,services:Object.keys(s),profileServices:a}}function Sl(e){let t=[e.yaml.trimEnd(),"","Dropped fields:"];if(e.dropped.length===0)t.push("(none)");else{t.push("service\tfield\treason");for(let r of e.dropped)t.push(`${r.service} ${r.field} ${r.reason}`)}if(e.profileServices.length<e.services.length)t.push(""),t.push("Container services were omitted from profile default so first start does not require Docker.");return`${t.join(`
|
|
2216
|
+
`}function PI(e){return`codex mcp add --url ${e}`}function Ms(e,t){return[{kind:"claude",title:"Claude",path:".mcp.json",language:"json",text:Yp(e,t),hint:"claude mcp add-json"},{kind:"cursor",title:"Cursor",path:".cursor/mcp.json",language:"json",text:Jp(e,t)},{kind:"kilo",title:"Kilo Code",path:"kilo.jsonc",language:"json",text:Xp(e,t)},{kind:"codex",title:"Codex",path:"~/.codex/config.toml",language:"toml",text:Qp(e,t),hint:PI(e)}]}function uw(e,t){let r=[`URL ${e}`,""];for(let n of Ms(e,t))r.push(`# ${n.title} ${n.path}${n.hint?` (${n.hint})`:""}`),r.push(n.text.trimEnd()),r.push("");return r.join(`
|
|
2217
|
+
`)}import{jsxDEV as q}from"@opentui/react/jsx-dev-runtime";function rf(){return cp.flatMap((e)=>_c.filter((t)=>t.category===e))}function vl(){return gl+rf().length}function mw(){return vl()+fw}function Os(e){if(e<gl||e>=vl())return;return rf()[e-gl]}function nf(e){let t=e-vl();return t>=0&&t<fw?t:void 0}function hw(e){let{palette:t,snap:r,port:n,portDraft:o="",selected:i,onPick:s,onToggle:a,onCopy:c,onToggleTool:l}=e,d=ye(),u=r?.mcp?.running===!0,f=r?.mcp?.port??n,p=r?.mcp?.address??mi(f),m=r?.mcp?.token??"",h=Ms(p,m),v=r?.mcp?.disabled_tools??[],b=rf(),g=b.filter((S)=>!An(S.name,v)).length,R=h[nf(i)??-1],E=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:_I[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(AI,{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(TI,{palette:t,snippet:S,active:i===x,rowH:d.rowH,onPick:()=>s(x),onCopy:()=>c(S)},S.kind,!1,void 0,this)}),q(LI,{palette:t,snippet:E,selected:Boolean(R),brand:E?Dc(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:EI(i,u)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function EI(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:RI,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 TI(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 AI(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 LI(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,_I,of=14,RI=14;var $s=I(()=>{Mt();ke();tt();fs();_I={inspect:"Inspect",logs:"Logs",diagnostics:"Diagnostics",control:"Control",setup:"Setup"}});import{jsxDEV as Fe,Fragment as vw}from"@opentui/react/jsx-dev-runtime";function HI(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+MI}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+(FI-1)*e.gap;let a=Math.max(t,r),c=Math.max(o,i);return s+a+n+c+sf+(NI-1)*e.gap}function jI(e){return yw(e)-uf-e.pad*2}function BI(e){return e-uf*2<II}function bw(e){let{palette:t,termW:r,termH:n,copyKey:o=Zr(),scrollRef:i}=e,s=ho(ye()),a=BI(r),c=s?0:1,l=s?0:1,d=HI(o),u={stacked:a,logCount:d.length,pad:c,gap:l},f=yw(u),p=jI(u);return Fe(pt,{palette:t,title:"help",bottomTitle:`${er()} \xB7 j/k scroll \xB7 esc close`,termW:r,termH:n,preferW:DI,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:$I,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=UI(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(WI,{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 UI(e){return e.reduce((r,n)=>Math.max(r,n.key.length),0)+OI}function WI(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 DI=78,II=56,MI=2,uf=2,sf=1,pf=8,OI=2,$I=2,FI=6,NI=4,af,cf,lf,df;var ff=I(()=>{Mt();ke();Tr();Er();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:R,leaderTimer:E,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)R(j)}return!0}if(r==="leader"){if(cn(t,n)){if(E.current)clearTimeout(E.current);return hi(e),!0}if(E.current)clearTimeout(E.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($r(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();Er();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:R,logShowTimestamps:E,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,R,-1));return}if(K&&(r==="right"||r==="]"||n==="logs"&&r==="l")){C(Dp(g,R,1));return}if((n==="dashboard"||n==="setup")&&r==="o"){Qe("profiles");return}if((n==="dashboard"||n==="services")&&(r==="*"||t.shift&&r==="8")){Xt([...d]),qe(`Selected ${d.length} services`);return}if((n==="dashboard"||n==="services")&&(r==="-"||r==="_"||r==="minus")){Xt([]),qe("Selection cleared");return}if((n==="dashboard"||n==="services")&&r==="n"){Ne(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=!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)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();Tr()});import{useKeyboard as GI}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:R,runCommand:E,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)E(A,[K]);return}if(u==="dashboard"&&jc(n)){gt([],b);return}let ce=R[g];if(ce&&(u==="dashboard"||u==="services"))z(ce)},[Qe,ge,gt,r,yt,nr,g,de,Ee,R,z,b,E,u,w,x,D,y,k,O,rr,n,nt,Q,vt,at]);GI((ce)=>{let K=(ce.name??"").toLowerCase();if(lw(ce,e)){S();return}if($r(ce)&&(K==="="||K==="+"||K==="plus")){Xt(xs(mt,1));return}if($r(ce)&&(K==="-"||K==="_"||K==="minus")){Xt(xs(mt,-1));return}if($r(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();Tr();Er();ww();Iw()});import{parse as KI,stringify as qI}from"yaml";function wl(e,t="imported"){let r=KI(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=VI(l,d,i);if(s[l]=u.body,!u.container)a.push(l)}return{yaml:qI({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(`
|
|
2218
2218
|
`)}
|
|
2219
|
-
`}function
|
|
2219
|
+
`}function VI(e,t,r){for(let f of Object.keys(t)){let p=$w[f];if(p)r.push({service:e,field:f,reason:p})}let n={},o=Fw(t.command);if(o)n.command=o;let i=typeof t.working_dir==="string"?t.working_dir:typeof t.workingDir==="string"?t.workingDir:"";if(i!=="")n.working_dir=i;let s=zI(t.depends_on);if(s.length>0)n.dependencies=s;let a=YI(t.environment);if(Object.keys(a).length>0)n.environment={defaults:a};let c=JI(t.ports);if(c.host.length>0)n.ports=c.host;let l=QI(t.healthcheck);if(l)n.health=l;let d=typeof t.image==="string"?t.image:"",u=d!=="";if(u)n.container={image:d,...c.container&&Object.keys(c.container).length>0?{ports:c.container}:{},...Object.keys(a).length>0?{env:a}:{}};return{body:n,container:u}}function Fw(e){if(typeof e==="string"&&e.trim()!=="")return["sh","-c",e];if(Array.isArray(e)&&e.every((t)=>typeof t==="string"))return e;return}function zI(e){if(Array.isArray(e))return e.filter((t)=>typeof t==="string");if(gi(e))return Object.keys(e);return[]}function YI(e){let t={};if(Array.isArray(e)){for(let r of e){if(typeof r!=="string")continue;let n=r.indexOf("=");if(n===-1)t[r]="";else t[r.slice(0,n)]=r.slice(n+1)}return t}if(gi(e))for(let[r,n]of Object.entries(e))t[r]=n===void 0||n===null?"":String(n);return t}function JI(e){let t=[],r={};if(!Array.isArray(e))return{host:t,container:r};let n=0;for(let o of e){let i=XI(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 XI(e){if(typeof e==="number"&&Number.isFinite(e))return{host:e,container:e};if(typeof e==="string"){let r=e.replace(/\/(tcp|udp)$/i,"").split(":"),n=Number(r[r.length-1]),o=r.length>1?Number(r[r.length-2]):n;if(!Number.isFinite(n)||!Number.isFinite(o))return;return{host:o,container:n}}if(gi(e)){let t=Number(e.published??e.target),r=Number(e.target??e.published);if(!Number.isFinite(t)||!Number.isFinite(r))return;return{host:t,container:r}}return}function QI(e){if(!gi(e)||e.disable===!0)return;let t=Fw(e.test);if(!t||t[0]==="CMD"&&t.length===1||t[0]==="NONE")return;let r=t[0]==="CMD"||t[0]==="CMD-SHELL"?t.slice(1):t;if(r.length===0)return;return{type:"command",command:r}}function gi(e){return Boolean(e)&&typeof e==="object"&&!Array.isArray(e)}var $w;var hf=I(()=>{$w={build:"not modeled; use a local command or pre-built image",networks:"not modeled",deploy:"not modeled",replicas:"not modeled",env_file:"per-service env_file is dropped; set environment.sources at the project level",volumes:"not modeled on import",labels:"not modeled",restart:"use service.restart in authoring",expose:"use ports",privileged:"not modeled",user:"not modeled",entrypoint:"not modeled; command is mapped when present",hostname:"not modeled",extra_hosts:"not modeled",cap_add:"not modeled",cap_drop:"not modeled",mem_limit:"not modeled",cpus:"not modeled",scale:"not modeled"}});function Nw(e){let t=Object.keys(e.services).length,r=Object.keys(e.profiles).length,n=e.proxy.routes.length,o=Object.keys(e.tasks).length,i=[{text:e.project.name||"unnamed",tone:"primary"},{text:`schema ${e.version}`,tone:"muted"},{text:`${t} service${t===1?"":"s"}`,tone:t>0?"info":"idle"},{text:`${r} profile${r===1?"":"s"}`,tone:r>0?"info":"idle"},{text:e.proxy.enabled?"proxy on":"proxy off",tone:e.proxy.enabled?"success":"idle"}];if(n>0)i.push({text:`${n} route${n===1?"":"s"}`,tone:"info"});if(o>0)i.push({text:`${o} task${o===1?"":"s"}`,tone:"info"});return i}function Hw(e,t){return[{label:"path",value:e.configPath||"\u2014"},{label:"repo",value:e.repoRoot||"\u2014"},{label:"devctl",value:t},{label:"schema",value:String(e.version)}]}function jw(e){return[{label:"project",value:e.google.project_id||"(unset)",tone:e.google.project_id?"text":"muted"},{label:"region",value:e.google.region||"(unset)",tone:e.google.region?"text":"muted"}]}function Bw(e){let t=ZI(e);return[{label:"exit",value:t,tone:t==="ask on quit"?"warning":"text"},{label:"grace",value:`${e.shutdown.grace_seconds}s`},{label:"refresh",value:`${bh(e.auth)}s`},{label:"env",value:e.environment.sources.join(", ")||"\u2014",tone:e.environment.sources.length>0?"text":"muted"},{label:"theme",value:e.ui.theme||"system"}]}function Uw(e){let t=e.logs.persistence;return[{label:"memory",value:`${e.logs.max_memory_events} events`},{label:"persist",value:t.enabled?t.directory:"off",tone:t.enabled?"text":"muted"},{label:"keep",value:t.enabled?`${t.retention_days}d \xB7 ${t.max_session_logs||"\u221E"} sessions`:"\u2014"}]}function Ww(e){return[{label:"listen",value:xn(e.proxy.listen),tone:e.proxy.enabled?"text":"muted"},{label:"token",value:rM(e),tone:e.proxy.token_endpoint.enabled?"text":"muted"}]}function Gw(e){return Object.keys(e.services).sort().map((t)=>{let r=e.services[t]??yn();return{name:t,extends:r.extends||"\u2014",command:Uc(r),ports:Es(r),identity:Gc(r),health:Wc(r),depends:r.dependencies.map(Ra).join(", ")||"\u2014",restart:Kc(r)}})}function Kw(e){return e.proxy.routes.map((t)=>({name:t.name||t.match.host||"(unnamed)",match:nM(t),upstream:t.upstream.url||"\u2014",auth:t.auth.type||"none",identity:oM(t)}))}function qw(e){return Object.keys(e.profiles).sort().map((t)=>{let r=e.profiles[t],n=Object.keys(r?.environment??{}).sort();return{name:t,services:r?.services.join(", ")||"\u2014",env:n.length>0?n.join(", "):"\u2014"}})}function Vw(e){return Object.keys(e.templates).sort().map((t)=>({name:t,summary:iM(e.templates[t])}))}function zw(e){return Object.keys(e.tasks).sort().map((t)=>{let r=e.tasks[t],n=r?.command.args.join(" ")||"\u2014",o=r?.dependencies.join(", ");return{name:t,summary:o?`${n} depends ${o}`:n}})}function Yw(e,t){let r=Pn(e);if(r.length===0)return"No provenance recorded for this configuration.";let n=new mr(e.secrets.extra_markers,e.secrets.extra_patterns);return r.map((o)=>{let i=typeof o.value==="string"?o.value:JSON.stringify(o.value),s=t?i:n.redactMap({[o.path]:i})[o.path],a=o.shadowed.length===0?"":`
|
|
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 Jw(e){let t=e.doctor.tools.map((i)=>i.name||i.command).filter((i)=>i!==""),r=e.plugins.map((i)=>i.path).filter((i)=>i!==""),n=Object.keys(e.ui.keymap),o=[...e.secrets.extra_markers,...e.secrets.extra_patterns];return[{label:"doctor",value:t.join(", ")||"\u2014",tone:t.length>0?"text":"muted"},{label:"plugins",value:r.join(", ")||"\u2014",tone:r.length>0?"text":"muted"},{label:"keymap",value:n.length>0?`${n.length} override${n.length===1?"":"s"}`:"defaults",tone:n.length>0?"text":"muted"},{label:"secrets",value:o.length>0?o.join(", "):"defaults",tone:o.length>0?"text":"muted"}]}function
|
|
2224
|
+
`)}function Jw(e){let t=e.doctor.tools.map((i)=>i.name||i.command).filter((i)=>i!==""),r=e.plugins.map((i)=>i.path).filter((i)=>i!==""),n=Object.keys(e.ui.keymap),o=[...e.secrets.extra_markers,...e.secrets.extra_patterns];return[{label:"doctor",value:t.join(", ")||"\u2014",tone:t.length>0?"text":"muted"},{label:"plugins",value:r.join(", ")||"\u2014",tone:r.length>0?"text":"muted"},{label:"keymap",value:n.length>0?`${n.length} override${n.length===1?"":"s"}`:"defaults",tone:n.length>0?"text":"muted"},{label:"secrets",value:o.length>0?o.join(", "):"defaults",tone:o.length>0?"text":"muted"}]}function ZI(e){if(e.shutdown.stop_services_on_exit===void 0)return"ask on quit";return e.shutdown.stop_services_on_exit?"stop services":"detach"}function Xw(e){let t=e.reduce((r,n)=>Math.max(r,n.name.length),0);return Math.min(tM,Math.max(eM,t))}function rM(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 nM(e){let t=e.match.host||"*",r=e.match.path;if(r==="")return t;return`${t}${r.startsWith("/")?r:`/${r}`}`}function oM(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 iM(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 eM=8,tM=20,Qw=80,vi=10;var gf=I(()=>{ht();ni();Lt()});async function kl(e,t){e.suspend();try{return await t()}finally{e.resume(),e.intermediateRender()}}import{useCallback as sM,useRef as aM}from"react";function Zw({setOverlay:e,setQuery:t,setSlashPicker:r,checked:n,setConfirmKind:o,setStatus:i,persistTheme:s,setPaletteIndex:a,themeName:c,setScreen:l,setSelected:d,screen:u,renderer:f,cfg:p,setScrollText:m,reveal:h,controller:v,refresh:b,profile:g,setReveal:R,resolveEnvironment:E,openDetail:S,copySelection:y,lastExportPath:x,openConfigBuffer:w,onDown:k,workspace:D,logView:O,diagnostics:F,lifecycleActions:Z}){let{loginGoogle:W,detectGoogle:C,logoutGoogle:j,bootstrapLogPath:B,fileExists:z,readTextFile:de,writeTextFile:Ee,validateConfigText:Ne,resolveExportPath:ne,writeLogExport:H,exportsDir:ae,openInFileManager:Q,listSessions:nt,loadSessionEvents:ft,checkUpdate:ie,applyUpdate:Le,formatUpdateStatus:Me}=D,{setLogService:Ke,setLogsFullscreen:gt,setPaused:Xe,setErrorOnly:Ct,toggleSystemLogs:ge,clearLogs:vt,logWrap:at,setLogWrap:yt,logServices:rr,errorOnly:Qe,logLevel:Xt,logSearch:qe,logRegex:mt,logSource:br,filteredLogs:Kt,setLogRegex:xr,setLogSince:nr,setLogUntil:qt,setLogs:or,setLogSearch:ir,toggleSplitLogs:sr}=O,{refreshAuth:Vt,setGoogle:ce}=F,{beginStart:K,beginStop:A,beginRestart:ve}=Z,J=aM(!1);return{runCommand:sM(async(ct,Ce)=>{if(J.current)return;if(J.current=!0,ct.name!=="start"&&ct.name!=="stop"&&ct.name!=="restart")e("none");t("");let Po=Rp(Ce,n);try{switch(ct.name){case"exit":o("quit"),e("confirm");return;case"down":{let P=Ce.includes("--keep-services")||Ce.includes("-k");k(P);return}case"help":e("help");return;case"version":i(er()),ie().then((P)=>i(Me(P))).catch((P)=>i(L(P)));return;case"update":i("checking for update\u2026"),ie().then(async(P)=>{if(!P.newer||!P.command){i(Me(P));return}i(`installing ${P.current} \u2192 ${P.latest} via ${P.kind}\u2026`);let ue=await Le(P.command);if(ue.code!==0){i(`update failed (${ue.code}): ${(ue.stderr||ue.stdout).trim()||P.hint}`);return}i(`updated to ${P.latest}; ${mc}`)}).catch((P)=>i(L(P)));return;case"themes":if(Ce[0]){s(Ce[0]);return}a(Math.max(0,St.indexOf(c))),t(""),e("themes");return;case"settings":l("settings"),d(0);return;case"dashboard":case"services":l(ct.name);return;case"logs":if(Ce[0])Ke(Ce[0]);l("logs");return;case"fullscreen":l("logs"),gt((P)=>u==="logs"?!P:!0);return;case"split":l("logs"),sr(),i("Split log panes");return;case"trace":{let P=Ce.join(" ").trim();if(P===""){i("usage: /trace <request-or-trace-id>");return}ir(P),l("logs"),i(`tracing ${P}`);return}case"auth":{let P=(Ce[0]??"").toLowerCase();if(P==="refresh"){l("auth"),await Vt();return}if(P==="login"){l("auth"),i("starting gcloud ADC login\u2026");try{await kl(f,()=>W()),ce(await C(p?.google.project_id??"")),await Vt(),i("ADC login complete")}catch(ue){i(L(ue))}return}if(P==="logout"){l("auth"),i("revoking ADC\u2026");try{await j(),ce(await C(p?.google.project_id??"")),await Vt(),i("ADC revoked")}catch(ue){i(L(ue))}return}l("auth");return}case"credentials":case"proxy":case"mcp":case"config":case"profiles":case"setup":l(ct.name);return;case"import":{let P=Ce[0]??"",ue=Ce.includes("--write"),Yr=Ce.find((se)=>se!=="compose"&&se!=="--write")??"compose.yaml";if(P!=="compose"){i("usage: /import compose [path] [--write]");return}if(!z(Yr)){i(`compose file not found: ${Yr}`);return}let Jr=p?.repoRoot||process.cwd(),wr=p?.configPath||`${Jr}/.devctl/config.yaml`;try{let se=wl(de(Yr),Jr.split("/").pop()??"imported"),Bn=Ne(Jr,wr,se.yaml),Un=`${Sl(se)}${Bn.length>0?`
|
|
2225
2225
|
validation:
|
|
2226
|
-
${Bn.map((
|
|
2226
|
+
${Bn.map((Ir)=>` ${Ir}`).join(`
|
|
2227
2227
|
`)}
|
|
2228
2228
|
`:""}`;if(ue){if(Bn.length>0){m({title:"compose import",body:Un}),e("scroll-text"),i("import is invalid; not written");return}if(z(wr)){i(`configuration already exists at ${wr}; not overwriting`);return}Ee(wr,se.yaml),i(`wrote ${wr}; /reload to apply`);return}m({title:"compose import (dry-run)",body:`${Un}
|
|
2229
|
-
Enter is preview only. /import compose ${Yr} --write to save.`}),e("scroll-text")}catch(se){i(L(se))}return}case"diff":if(!p){i("no configuration loaded");return}m({title:"config sources",body:Yw(p,h)}),e("scroll-text");return;case"daemon":{let P=p?.repoRoot;if(!P){i("no repository");return}let ue=B(P);if(!z(ue)){i("no daemon bootstrap log yet for this repository");return}m({title:"daemon bootstrap",body:de(ue)}),e("scroll-text");return}case"reload":if(!v)return;v.reload().then((P)=>{if(i(P.restart_required.length===0?"Configuration reloaded":`Reload requires restart: ${P.restart_required.join(", ")}`),P.restart_required.length>0)o("reload"),e("confirm");b()});return;case"doctor":l("doctor");return;case"stats":l("stats");return;case"start":await K(Po,g);return;case"stop":await A(Po);return;case"restart":{let P=vx(Ce),ue=Rp(P.services,n);await ve(ue,g,P.cascade);return}case"run":{let P=Ce[0]??"";if(!P){r("tasks"),t(""),e("slash"),i(Object.keys(p?.tasks??{}).length===0?"no tasks configured":"pick a task");return}if(!v){i("no daemon attached");return}try{let ue=await v.runTask(P);Ke(`task:${P}`),l("logs"),i(`task ${P} exited ${ue.code}`)}catch(ue){throw Ke(`task:${P}`),l("logs"),ue}return}case"exec":{let P=yx(Ce);if(!P.service){r("services"),t(""),e("slash"),i(Object.keys(p?.services??{}).length===0?"no services configured":"pick a service");return}if(!v){i("no daemon attached");return}if(P.printEnv){if(!p?.services[P.service]){i(`unknown service ${P.service}`);return}if(P.reveal)
|
|
2230
|
-
`)),n("Config buffer not saved");return}if(i(e.configPath,v),p(""),r("none"),!s){n(`Wrote ${e.configPath}`);return}s.reload().then((g)=>{if(n(g.restart_required.length===0?"Configuration saved and reloaded":`Saved; restart required: ${g.restart_required.join(", ")}`),g.restart_required.length>0)a("reload"),r("confirm");c()})},[e,d,s,c]);return{configEditRef:l,configEditText:d,configEditError:f,setConfigEditError:p,openConfigBuffer:m,saveConfigBuffer:h}}var oS=I(()=>{te()});import{useEffect as lM}from"react";function iS({controller:e,cfg:t,paused:r,logSince:n,refresh:o,setLogs:i,setCfg:s,setConfigReloadError:a,setStatus:c}){return lM(()=>{if(!e)return;let l,d=!1,u=[],f=t&&t.logs.max_memory_events>0?t.logs.max_memory_events:50000,p=()=>{if(l=void 0,u.length>0){let h=u.splice(0,u.length);i((v)=>Gb(v,h,n,f))}if(d)d=!1,o()},m=e.onEvent((h)=>{if(h.type===Io&&h.payload&&typeof h.payload==="object"&&"event"in h.payload){let v=h.payload.event;if(!r)u.push(v);if(!l)l=setTimeout(p,30);return}if(h.type===Ti){a(dx(h));return}if(h.type===da)a(void 0),e.configSnapshot().then(s).catch((v)=>c(L(v)));if(d=!0,!l)l=setTimeout(p,30)});return()=>{if(m(),l)clearTimeout(l)}},[e,n,r,o]),{}}var sS=I(()=>{te();vr();gr()});import{useCallback as dM,useEffect as aS,useRef as cS,useState as yi}from"react";function lS({controller:e,workspace:t,cfg:r,snap:n,screen:o,configReloadError:i,setSnap:s,setStatus:a}){let{detectGoogle:c,runDoctor:l,loadPath:d}=t,[u,f]=yi(),[p,m]=yi(),[h,v]=yi(!1),[b,g]=yi(""),[_,R]=yi(0),[S,y]=yi({active:"Preparing diagnostics",checks:[]}),x=cS(void 0),w=cS(0),k=dM(async()=>{if(!e){a("Supervisor is not running");return}a("Probing configured identities\u2026");try{let D=await e.refreshAuth();s((W)=>W?{...W,identity:D}:W);let O=Object.values(D.service_account_status),F=O.filter((W)=>W==="available").length,Z=O.filter((W)=>W==="unavailable").length;a(O.length===0?"Identity refreshed \u2014 no service accounts configured":`Identity refreshed \u2014 ${F} available${Z>0?`, ${Z} unavailable`:""}`)}catch(D){a(L(D))}},[e]);return aS(()=>{c(r?.google.project_id??"").then(f).catch((D)=>{a(L(D))})},[r?.google.project_id,n?.identity.project]),aS(()=>{if(o!=="doctor"||!r)return;let D=x.current,O=n?.session_id??"";if(D?.cfg===r&&D.tick===_&&D.sessionID===O&&D.reloadError===i)return;x.current={cfg:r,tick:_,sessionID:O,reloadError:i};let F=++w.current;v(!0),g(""),y({active:"Preparing diagnostics",checks:[]});let Z;if(r.configPath!==""){Z="";try{d(r.repoRoot,r.configPath)}catch(W){Z=L(W)}}l(r,(W)=>{if(F===w.current)y(W)},{services:n?.services,proxyRunning:n?.proxy.running,repositoryConfigError:Z}).then((W)=>{if(F!==w.current)return;m(W)}).catch((W)=>{if(F!==w.current)return;g(L(W))}).finally(()=>{if(F===w.current)v(!1)})},[o,r,_,n?.session_id,i]),{google:u,setGoogle:f,doctor:p,doctorLoading:h,doctorError:b,doctorProgress:S,setDoctorTick:R,refreshAuth:k}}var dS=I(()=>{te()});import{useCallback as vf,useState as Cl}from"react";function uS({controller:e,workspace:t,cfg:r,snap:n,refresh:o,setStatus:i,setOverlay:s}){let{resolveStartRequest:a,startupPlan:c,shutdownPlan:l,shutdownPlanExact:d}=t,[u,f]=Cl(),[p,m]=Cl([]),[h,v]=Cl(!1),[b,g]=Cl("start"),_=vf(async(y,x)=>{if(!e||!r){i("no configuration loaded");return}try{let w=y.length>0,k=a(r,{services:y,profile:w?void 0:x}),D=c(r,k.services,k.profile);g("start"),m(D.waves.flat().filter((Z)=>vo(n?.services[Z]))),f(D),s("plan"),v(!0);let O=k.services.filter((Z)=>!vo(n?.services[Z])),F=await e.start({services:O.length>0?O:k.services,profile:w?void 0:k.profile});await o(),v(!1),i(Cw(F))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),R=vf(async(y)=>{if(!e||!r){i("no configuration loaded");return}let x=y.length>0?y:Object.entries(n?.services??{}).filter(([,w])=>w.state!=="STOPPED"&&w.state!=="UNKNOWN").map(([w])=>w);if(x.length===0){i("nothing to stop");return}try{let w=l(r,x);g("stop"),m([]),f(w),s("plan"),v(!0),await e.stop(y),await o(),v(!1),i(Pw(w))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),S=vf(async(y,x,w=!1)=>{if(!e||!r){i("no configuration loaded");return}try{let k=kw(r,y,x),D=w?l(r,k.services):d(r,k.services);g("restart"),m([]),f(D),s("plan"),v(!0),await e.restart(k.services,w),await o(),v(!1);let O=bl(D);i(O===""?"Restarted selected services":`Restarted ${O}`)}catch(k){v(!1),await o(),i(L(k))}},[r,e,o]);return{plan:u,planInitiallyRunning:p,planBusy:h,lifecycle:b,beginStart:_,beginStop:R,beginRestart:S}}var pS=I(()=>{te();xl();Lt()});import{useCallback as Lr,useEffect as Kr,useMemo as Fn,useRef as uM,useState as be}from"react";function mS(e,t,r,n,o){let i=Ap(e,t.sliceLen,t.windowStart,t.windowNewer);if(i.startDelta!==0){r(Math.max(0,t.windowStart+i.startDelta)),n(!0),o(i.selected);return}let s=Math.max(t.sliceLen-1,0),a=t.sliceLen>0&&(i.selected<s||t.windowNewer>0);if(a&&!t.pinned)r(Zc(t.filteredLen));n(a),o(i.selected)}function hS({controller:e,tui:t,names:r,screen:n,refresh:o,setStatus:i}){let[s,a]=be([]),[c,l]=be(!1),[d,u]=be(!1),[f,p]=be(!0),[m,h]=be(""),[v,b]=be(!1),[g,_]=be(0),[R,S]=be(""),[y,x]=be(""),[w,k]=be(""),[D,O]=be(""),[F,Z]=be(!1),[W,C]=be(0),[j,B]=be(Ar-1),[z,de]=be(!1),[Ee,Ne]=be(0),ne=fS,[H,ae]=be(t.log_timestamps!==!1),[Q,nt]=be(t.log_metadata!==!1),[ft,ie]=be(()=>[...Wb]),[Le]=be(""),[Me]=be(""),[Ke,gt]=be(!1),[Xe,Ct]=be("focus"),[ge,vt]=be(!1),[at,yt]=be(Ar-1),[rr,Qe]=be(!1),[Xt,qe]=be(-1),[mt,br]=be(),[Kt,xr]=be(""),[nr,qt]=be(!1),[or,ir]=be(!1),sr=uM([]),[Vt,ce]=be(0),K=Fn(()=>tl(r,s,ft),[r,s,ft]);Kr(()=>{ie((le)=>{let bt=new Set(le),Pt=!1;for(let Xr of s)if(Xr.service!==""&&!r.includes(Xr.service)&&!bt.has(Xr.service))bt.add(Xr.service),Pt=!0;return Pt?[...bt].sort():le})},[s,r]);let A=Fn(()=>yo(s,{service:w,services:ne,errorOnly:d,search:m,regex:Ke,source:Me,since:R,until:y,systemLogs:f}),[d,Ke,m,w,ne,R,Me,y,s,f]),ve=Fn(()=>Tp(A,ge,Vt),[A,ge,Vt]),J=ve.items,Dt=Fn(()=>yo(s,{service:D,services:ne,errorOnly:d,search:m,regex:Ke,source:Me,since:R,until:y,systemLogs:f}),[d,Ke,m,D,ne,R,Me,y,s,f]),ct=Fn(()=>Tp(Dt,z,Ee),[Dt,z,Ee]),Ce=ct.items;Kr(()=>{qe(-1),vt(!1),yt(Math.max(0,Math.min(Ar,A.length)-1))},[d,m,w,f]),Kr(()=>{de(!1),B(Math.max(0,Math.min(Ar,Dt.length)-1))},[d,m,D,f]),Kr(()=>{if(n!=="logs")Qe(!1)},[n]),Kr(()=>{if(n!=="logs"||ge)return;yt(Math.max(0,Math.min(Ar,A.length)-1))},[A.length,ge,n]),Kr(()=>{if(n!=="logs"||z)return;B(Math.max(0,Math.min(Ar,Dt.length)-1))},[Dt.length,z,n]);let Po=Lr(()=>{if(!ge)ce(Zc(A.length)),vt(!0)},[A.length,ge]),P=Lr(()=>{if(!z)Ne(Zc(Dt.length)),de(!0)},[Dt.length,z]),ue=Fn(()=>({sliceLen:J.length,windowStart:ve.start,windowNewer:ve.newer,filteredLen:A.length,pinned:ge}),[A.length,ge,J.length,ve.newer,ve.start]),Yr=Fn(()=>({sliceLen:Ce.length,windowStart:ct.start,windowNewer:ct.newer,filteredLen:Dt.length,pinned:z}),[Dt.length,z,Ce.length,ct.newer,ct.start]),Jr=Lr((le)=>mS(le,ue,ce,vt,yt),[ue]),wr=Lr((le)=>mS(le,Yr,Ne,de,B),[Yr]),se=Lr((le)=>{if(F&&W===1){wr(le);return}Jr(le)},[Jr,wr,W,F]),Bn=Lr((le)=>{let bt=J.length,Pt=Ap(le,bt,ve.start,ve.newer);if(Pt.startDelta!==0)ce(Math.max(0,ve.start+Pt.startDelta)),vt(!0);qe(Pt.selected)},[J.length,ve.newer,ve.start]),Un=Lr(()=>{if(vt(!1),de(!1),qe(-1),n==="logs")yt(Math.max(0,Math.min(Ar,A.length)-1)),B(Math.max(0,Math.min(Ar,Dt.length)-1));_((bt)=>bt+1);let le=Math.max(ve.newer,ct.newer);i(le>0?`Jumped to latest (+${le} new)`:"Jumped to latest logs")},[A.length,Dt.length,ve.newer,ct.newer,n]),Dr=Fn(()=>({services:F?fS:w!==""?[w]:ne,level:d?"ERROR":Le,search:m,regex:Ke,source:Me,since:R,until:y}),[d,Le,Ke,m,w,ne,R,Me,y,F]),dn=Lr(async()=>{if(!e)return;try{br(await e.logsStats(Dr))}catch(le){i(L(le))}},[e,Dr]),Ws=Lr(async()=>{if(!e||c)return;try{let le=await e.logsPage(Dr);a((bt)=>Kb(bt,le.events)),xr(le.prevCursor),qt(le.hasPrev),await dn()}catch(le){i(L(le))}},[e,Dr,c,dn]),Ci=Lr(()=>{a([]),S(new Date().toISOString()),vt(!1),de(!1),i("Cleared on-screen log buffer")},[]),Pi=Lr(()=>{p((le)=>!le)},[]);return Kr(()=>{o().then(()=>Ws())},[o,Ws]),Kr(()=>{sr.current=s},[s]),Kr(()=>{let le=Lp(ge,ve.start,nr),bt=F&&Lp(z,ct.start,nr);if(!e||or||!le&&!bt)return;ir(!0),e.logsPage({...Dr,cursor:Kt,direction:"backward"}).then((Pt)=>{if(Pt.sessionChanged){i("Daemon session changed \u2014 older log history is no longer available"),qt(!1);return}let Xr=sr.current,Gs=qb(Xr,Pt.events);a(Gs);let _o=Gs.length-Xr.length;if(_o>0){if(ge)ce((Sr)=>Sr+_o);if(z)Ne((Sr)=>Sr+_o)}xr(Pt.prevCursor),qt(Pt.hasPrev)}).catch((Pt)=>i(L(Pt))).finally(()=>ir(!1))},[e,Dr,or,nr,ge,z,Kt,ve.start,ct.start,F]),Kr(()=>{if(!e||c||n!=="logs")return;let le=setInterval(()=>{dn()},pM);return()=>clearInterval(le)},[e,c,dn,n]),{logs:s,setLogs:a,paused:c,setPaused:l,errorOnly:d,setErrorOnly:u,showSystemLogs:f,logSearch:m,setLogSearch:h,logSearchFocused:v,setLogSearchFocused:b,logFollow:g,logSince:R,setLogSince:S,setLogUntil:x,logService:w,setLogService:k,logServiceB:D,setLogServiceB:O,splitLogs:F,splitFocus:W,setSplitFocus:C,toggleSplitLogs:()=>{if(F){C(0),Z(!1);return}if(D===""){let le=r.find((bt)=>bt!==w);if(le!==void 0)O(le)}Z(!0)},cycleSplitFocus:()=>C((le)=>le===0?1:0),setActiveLogService:(le)=>{let bt=(Pt)=>typeof le==="function"?le(Pt):le;if(F&&W===1){O(bt);return}k(bt)},logSliceB:Ce,logWindowB:ct,logSelectedB:j,logPinnedB:z,logServices:ne,logShowTimestamps:H,setLogShowTimestamps:ae,logShowMeta:Q,setLogShowMeta:nt,logLevel:Le,logSource:Me,logRegex:Ke,setLogRegex:gt,logWrap:Xe,setLogWrap:Ct,logPinned:ge,logSelected:at,logsFullscreen:rr,setLogsFullscreen:Qe,dashboardLogCursor:Xt,setDashboardLogCursor:qe,logFacets:mt,logSources:K,filteredLogs:A,filteredLogsB:Dt,logWindow:ve,logSlice:J,pinLogView:Po,pinLogViewB:P,applyLogCursor:se,applyLogCursorA:Jr,applyLogCursorB:wr,applyDashboardLogCursor:Bn,jumpToLatestLogs:Un,clearLogs:Ci,toggleSystemLogs:Pi}}var fS,pM=2000;var gS=I(()=>{te();gr();fS=[]});import{useCallback as ko,useState as vS}from"react";function yS({tui:e,controller:t,cfg:r,persistPrefs:n,snap:o,refresh:i,setStatus:s}){let[a,c]=vS(""),[l,d]=vS(()=>e.mcp_port??fo(t?.cfg.repoRoot??process.cwd())),u=ko((g)=>{let _=Rn(g);d(_);let R=r?.repoRoot??process.cwd();if(Dy(R,_)){n({mcp_port:null},`MCP port ${_} (default)`);return}n({mcp_port:_},`MCP port ${_}`)},[r,n]),f=ko(()=>{if(a==="")return l;let g=Ly(a,l);return c(""),u(g),g},[l,a,u]),p=ko(async(g)=>{if(!t||o?.mcp?.running!==!0)return;await t.mcpStop(),await t.mcpStart({port:g}),await i()},[t,i,o?.mcp?.running]),m=ko(async(g)=>{if(!t){s("Supervisor is not running");return}let _=o?.mcp?.disabled_tools??[],R=An(g.name,_),S=R?[..._,g.name]:_.filter((y)=>y!==g.name);try{let y=await t.mcpSetTools(S);n({mcp_disabled_tools:y},`${g.label} ${R?"disabled":"enabled"}`),await i()}catch(y){s(L(y))}},[t,n,i,o]),h=ko(async()=>{if(!t){s("Supervisor is not running");return}try{if(o?.mcp?.running)await t.mcpStop(),n({mcp_enabled:!1},"MCP off"),s("MCP stopped");else await t.mcpStart({port:l}),n({mcp_enabled:!0},"MCP on"),s("MCP started");await i()}catch(g){s(L(g))}},[t,l,n,i,o?.mcp?.running]),v=ko((g)=>{ml(g.text).then(()=>{s(`Copied ${g.title} snippet`)}).catch((_)=>{s(L(_))})},[]),b=ko((g)=>{let _=nf(g);if(_===void 0)return!1;let R=o?.mcp?.address??mi(o?.mcp?.port??l),S=Ms(R,o?.mcp?.token??"")[_];if(!S)return!1;return v(S),!0},[v,l,o?.mcp?.address,o?.mcp?.port,o?.mcp?.token]);return{mcpPortDraft:a,setMcpPortDraft:c,mcpPort:l,persistMcpPort:u,applyMcpPortDraft:f,restartMcpOnPort:p,toggleMcpTool:m,toggleMcp:h,copyMcpSnippet:v,copyFocusedMcpSnippet:b}}var bS=I(()=>{te();yl();fs();qp();$s()});import{RGBA as fM}from"@opentui/core";import{useCallback as cn,useEffect as mM,useMemo as xS,useRef as Pl,useState as _l}from"react";function wS({tui:e,controller:t,resolveTuiOverridePath:r,terminalBackground:n,userTuiConfigPath:o,snap:i,setStatus:s,saveTuiPreferences:a,setPaletteIndex:c,setOverlay:l,setConfirmKind:d,setScreen:u,setSelected:f,screen:p}){let[m,h]=_l(e.theme||t?.cfg.ui.theme||"devctl"),v=Pl(m),[b,g]=_l(e.mouse),_=Pl(e.mouse),[R,S]=_l(e.leader_timeout),y=Pl(e.leader_timeout),[x,w]=_l(()=>bs(e.font_size)),k=Pl(x),D=vb(r()),O=xS(()=>{let H=pb(m);if(Tc(m)!=="terminal"||!fp(n))return H;let ae=n??H.background;return{...H,background:ae,panel:ae,element:ae,inverse:ae}},[n,m]),F=Tc(m)==="terminal"&&fp(n)?fM.defaultBackground(n??void 0):O.background,Z=xS(()=>wb({themeName:m,fontSize:x,mouse:b,leaderMs:R,locked:D,configPath:D?e.path||yp(r(),o()):yp(r(),o()),mcpRunning:i?.mcp?.running===!0}),[x,R,b,D,i?.mcp?.running,m,e.path]),W=cn((H,ae)=>{if(D){s(`${ae} session only`);return}let Q=a(H);s(`${ae} saved ${Q}`)},[D]),C=cn((H)=>{h(H),v.current=H,W({theme:H},`theme ${H}`)},[W]),j=cn(()=>{let H=!b;g(H),_.current=H,W({mouse:H},`mouse ${H?"on":"off"} restart TUI to apply clicks`)},[b,W]),B=cn((H)=>{S(H),y.current=H,W({leader_timeout:H},`leader ${H}ms`)},[W]),z=cn((H)=>{let ae=bs(H);w(ae),k.current=ae,W({font_size:ae},`display ${Ic(ae)}`)},[W]),de=cn(()=>{let H=Mc();h(H.theme),v.current=H.theme,g(H.mouse),_.current=H.mouse,S(H.leader_timeout),y.current=H.leader_timeout,w(H.font_size),k.current=H.font_size,W(H,"restored default preferences")},[W]),Ee=cn((H)=>{if(H.id==="theme"){c(Math.max(0,St.indexOf(m))),l("themes");return}if(H.id==="mouse"){j();return}if(H.id==="leader"){B(R);return}if(H.id==="font"){z(x);return}if(H.id==="reset"){d("reset-prefs"),l("confirm");return}if(H.id==="mcp"){u("mcp"),f(0);return}s(H.detail)},[z,B,x,R,m,j]),Ne=cn((H,ae)=>{let Q=Ln(Z,ae);if(!Q)return;if(Q.id==="theme"){C(yb(m,H));return}if(Q.id==="leader"){B(bb(R,H));return}if(Q.id==="font"){z(xs(x,H));return}if(Q.id==="mouse")j()},[z,B,x,R,C,Z,m,j]),ne=cn(()=>{h(v.current)},[]);return mM(()=>{if(p==="settings")return;h(v.current),w(k.current),S(y.current),g(_.current)},[p]),{themeName:m,setThemeName:h,leaderMs:R,fontSize:x,prefsLocked:D,palette:O,rootBackground:F,settingRows:Z,persistPrefs:W,persistTheme:C,toggleMouse:j,applyFont:z,applyReset:de,activateSetting:Ee,cycleSetting:Ne,revertThemePreview:ne}}var SS=I(()=>{Er();tt()});import{jsxDEV as Ns}from"@opentui/react/jsx-dev-runtime";function kS(e){let{palette:t,termW:r,termH:n,step:o,answers:i,repo:s,draft:a,authStatus:c,onDraft:l,onSubmit:d}=e,u=wt[o]??wt[0],f=wt.length,p=hM(u.id,i,s);return Ns(pt,{palette:t,title:`setup ${o+1}/${f} ${u.title}`,bottomTitle:"enter next \xB7 esc cancel",termW:r,termH:n,preferW:64,preferH:12,borderColor:t.primary,gap:1,children:[Ns("text",{fg:t.muted,wrapMode:"word",children:p},void 0,!1,void 0,this),u.id==="auth"||u.id==="write"||u.id==="repo"?Ns("text",{fg:t.text,wrapMode:"word",children:u.id==="auth"?c:u.id==="repo"?s:"Write .devctl/config.yaml and attach the daemon."},void 0,!1,void 0,this):Ns("input",{focused:!0,value:a,placeholder:u.prompt,onInput:l,onSubmit:d,backgroundColor:t.element,focusedBackgroundColor:t.element,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this),Ns(it,{palette:t,hints:[{key:"enter",label:u.id==="write"?"write and attach":"next"},{key:"esc",label:"cancel"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function yf(e,t,r){if(e==="name")return t.name;if(e==="project")return t.project;if(e==="sa")return t.sa;if(e==="audience")return t.audience;if(e==="port")return String(t.proxyPort);if(e==="profile")return t.profile;if(e==="repo")return r;return""}function CS(e,t,r,n){let o={...t};if(e==="name")o.name=r.trim()||wn(n).name;else if(e==="project")o.project=r.trim();else if(e==="sa")o.sa=r.trim();else if(e==="audience")o.audience=r.trim();else if(e==="port")o.proxyPort=Aa(r,t.proxyPort);else if(e==="profile")o.profile=r.trim();return o}function hM(e,t,r){if(e==="repo")return`Using ${r}. The TUI always writes under this checkout.`;if(e==="auth")return"ADC login needs a real TTY. Enter continues; if ADC is missing, login runs gcloud on the terminal.";if(e==="write")return`Project ${t.name||"(unnamed)"}. Enter writes the starter file and attaches the daemon.`;return wt.find((n)=>n.id===e)?.prompt??""}var bf=I(()=>{La();ke()});import{useCallback as xf,useState as Rl}from"react";function PS({workspace:e,renderer:t,setOverlay:r,setStatus:n,setScreen:o,onAttached:i,setController:s,setCfg:a,setBootError:c}){let l=process.cwd(),[d,u]=Rl(0),[f,p]=Rl(()=>wn(l)),[m,h]=Rl(""),[v,b]=Rl("Checking Application Default Credentials\u2026"),g=xf(()=>{let S=wn(l);u(0),p(S),h(yf("repo",S,l)),r("setup-wizard"),e.detectGoogle("").then((y)=>{p((x)=>({...x,project:y.projectID||x.project})),b(y.adcAvailable?`ADC available${y.userEmail?` (${y.userEmail})`:""}. Enter continues.`:"ADC is not available. Enter runs gcloud ADC login on this TTY, then continues.")})},[l,r,e]),_=xf(async(S)=>{try{Ia(l,S);let y=await e.openTui(l,"");i?.(y),s(y),a(y.cfg),c(void 0),r("none"),o("dashboard"),n("Wrote .devctl/config.yaml and attached the daemon")}catch(y){n(L(y))}},[i,l,c,a,s,r,o,n,e]),R=xf(async()=>{let S=wt[d];if(!S)return;let y=CS(S.id,f,m,l);if(S.id==="auth"){if(!(await e.detectGoogle(y.project)).adcAvailable)try{await kl(t,()=>e.loginGoogle())}catch(D){n(L(D))}}if(p(y),S.id==="write"){await _(y);return}let x=Math.min(d+1,wt.length-1),w=wt[x];u(x),h(w?yf(w.id,y,l):"")},[f,m,_,t,l,d,e,n]);return{step:d,answers:f,draft:m,authStatus:v,repo:l,setDraft:h,startWizard:g,advanceWizard:R}}var _S=I(()=>{La();Ma();te();bf()});import{useCallback as gM,useEffect as RS,useState as wf}from"react";function ES({controller:e,cfg:t,envService:r}){let[n,o]=wf(),[i,s]=wf(!1),[a,c]=wf(""),l=n?.name===r,d=l?n?.env:void 0,u=r===""?"config":i&&!l?"loading":l?"resolved":a!==""?"error":"config",f=l||r===""?"":a;RS(()=>{o(void 0),c("")},[t]),RS(()=>{if(!e||r===""||!t?.services[r])return;if(n?.name===r)return;let m=!1;return s(!0),c(""),e.execService(r,[],!0).then((h)=>{if(m)return;o({name:r,env:h.environment??{}}),c("")}).catch((h)=>{if(m)return;c(L(h))}).finally(()=>{s(!1)}),()=>{m=!0}},[t,e,r,n?.name]);let p=gM(async(m)=>{if(!e)return;try{let h=await e.execService(m,[],!0);o({name:m,env:h.environment??{}}),c("")}catch(h){throw c(L(h)),h}},[e]);return{inspectorEnv:d,inspectorEnvStatus:u,inspectorEnvError:f,resolveEnvironment:p}}var TS=I(()=>{te()});import{jsxDEV as Nn}from"@opentui/react/jsx-dev-runtime";function AS(e){let{palette:t,path:r,initialValue:n,error:o,termW:i,termH:s,textareaRef:a}=e;return Nn(pt,{palette:t,title:"config buffer",bottomTitle:`${It("s")} save \xB7 esc discard \xB7 e still opens $EDITOR`,termW:i,termH:s,preferW:88,preferH:28,gap:1,children:[Nn("box",{height:1,overflow:"hidden",children:Nn("text",{fg:t.muted,wrapMode:"none",children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),Nn("textarea",{ref:a,focused:!0,initialValue:n,backgroundColor:t.panel,focusedBackgroundColor:t.panel,textColor:t.text,focusedTextColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this),o!==""?Nn("box",{height:2,overflow:"hidden",children:Nn("text",{fg:t.error,wrapMode:"word",children:o},void 0,!1,void 0,this)},void 0,!1,void 0,this):Nn("box",{height:1,overflow:"hidden",children:Nn("text",{fg:t.muted,children:"validate, then write. invalid YAML is not saved."},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var LS=I(()=>{ke();Rr()});import{jsxDEV as Sf}from"@opentui/react/jsx-dev-runtime";function DS(e){let{palette:t,title:r,body:n,kind:o,termW:i,termH:s}=e,a=lx(o),c=a.map((l)=>`${l.key} ${l.label}`).join(" \xB7 ");return Sf(pt,{palette:t,title:r,bottomTitle:c,termW:i,termH:s,preferW:52,preferH:8,borderColor:t.warning,gap:1,children:[Sf("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),Sf(it,{palette:t,hints:a},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var IS=I(()=>{vr();ke()});import{jsxDEV as MS}from"@opentui/react/jsx-dev-runtime";function OS(e){let{palette:t,termW:r,termH:n}=e,o=Hp();return MS(pt,{palette:t,title:"leader",termW:r,termH:n,preferW:68,preferH:5,anchor:"bottom",children:MS(it,{palette:t,hints:o},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var $S=I(()=>{wo();ke()});import{jsxDEV as Jt,Fragment as vM}from"@opentui/react/jsx-dev-runtime";function FS(e){let{palette:t,event:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;let s=r.raw?Fb(r.raw):void 0,a=r.message.length>120||r.message.includes(`
|
|
2231
|
-
`)||s!==void 0;return Jt(pt,{palette:t,title:"log details",bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:a?84:72,preferH:a?22:14,gap:1,children:Jt("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Jt("box",{flexDirection:"column",overflow:"hidden",children:[Jt("text",{fg:t.text,wrapMode:"word",children:Jc(r.message)},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`time ${r.timestamp}`},void 0,!1,void 0,this),Jt("text",{fg:Ze(r.service,t),children:`service ${r.service}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`source ${r.source}${r.stream?` / ${r.stream}`:""}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`level ${Yc(r.level)}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`pid ${r.pid||"\u2014"}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`request ${r.request_id||"\u2014"}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`identity ${r.identity||"\u2014"}`},void 0,!1,void 0,this),s===void 0?null:Jt(vM,{children:[Jt("text",{fg:t.muted,children:"raw json"},void 0,!1,void 0,this),Jt("text",{fg:t.text,wrapMode:"word",children:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var NS=I(()=>{gr();ke();tt()});import{jsxDEV as U}from"@opentui/react/jsx-dev-runtime";function kf(e,t){switch(e){case"completed":return{icon:"\u2713",label:"Completed",tone:"success",color:t.success};case"failed":return{icon:"\u2717",label:"Failed",tone:"error",color:t.error};case"unhealthy":return{icon:"\u26A0",label:"Unhealthy",tone:"warning",color:t.warning};case"active":return{icon:"\u25CF",label:"In progress",tone:"primary",color:t.primary};default:return{icon:"\u25CB",label:"Queued",tone:"muted",color:t.muted}}}function yM(e,t,r,n){if(t!=="")return{icon:"\u2717",label:"Failed",tone:"error",color:n.error};if(!e)return{icon:"\u2713",label:"Finished",tone:"success",color:n.success};if(r==="unhealthy")return{icon:"\u26A0",label:"Waiting on health",tone:"warning",color:n.warning};return{icon:"\u25CF",label:"Running",tone:"primary",color:n.primary}}function HS(e){return U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:U("text",{wrapMode:"none",children:[U("span",{fg:e.palette.primary,children:e.label.toUpperCase()},void 0,!1,void 0,this),e.detail?U("span",{fg:e.palette.muted,children:` ${e.detail}`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function jS(e){let{palette:t,plan:r,snap:n,busy:o,failed:i,kind:s,termW:a=80,termH:c=24,scrollRef:l,initiallyRunning:d=[],onDismiss:u}=e,f=new Set(d),p=r.waves.map((C,j)=>({wave:C.filter((B)=>!f.has(B)),waveIdx:j})).filter(({wave:C})=>C.length>0),m=Rw(r,n,s),h=r.waves[m.currentWaveIndex]??[],v=Fs(h,n,s),b=kf(v,t),g=yM(o,i,v,t),_=Tw(s,o,i,r.profile),R=Aw(o,i),S=i?t.inverse:t.text,y=i?t.error:t.element,x=Math.min(92,Math.max(40,a-8)),w=x>=82?22:x>=66?17:13,k=12,D=9,O=x>=56,F=x>=72,Z=x<72||r.waves.length>3,W=r.profile?`PROFILE ${r.profile}`:s.toUpperCase();return U(pt,{palette:t,title:_,bottomTitle:"enter close \xB7 esc hide (operation continues)",termW:a,termH:c,preferW:98,preferH:28,borderColor:g.color,gap:0,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[U(Be,{palette:t,label:`${g.icon} ${g.label.toUpperCase()}`,tone:g.tone},void 0,!1,void 0,this),U("box",{paddingLeft:1,flexGrow:1,overflow:"hidden",children:U("text",{fg:t.text,wrapMode:"none",children:W},void 0,!1,void 0,this)},void 0,!1,void 0,this),U(Be,{palette:t,label:`${m.ready}/${m.total} READY`,tone:m.isComplete?"success":"info"},void 0,!1,void 0,this),U(Be,{palette:t,label:`WAVE ${Math.min(m.currentWaveIndex+1,Math.max(1,m.totalWaves))}/${Math.max(1,m.totalWaves)}`,tone:b.tone},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{height:2,flexDirection:"column",overflow:"hidden",flexShrink:0,paddingLeft:1,paddingRight:1,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",children:[U("text",{wrapMode:"none",children:[U("span",{fg:t.muted,children:"PROGRESS "},void 0,!1,void 0,this),U("span",{fg:g.color,children:`[${m.progressBar}]`},void 0,!1,void 0,this),U("span",{fg:t.text,children:` ${m.percent}%`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{flexGrow:1},void 0,!1,void 0,this),U("text",{wrapMode:"none",children:U("span",{fg:b.color,children:`${b.icon} ${b.label}`},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{height:1,overflow:"hidden",children:U("text",{fg:t.muted,wrapMode:"none",children:o?s==="stop"?"Dependents stop first; each wave waits for the previous wave.":"Each wave waits until its services are healthy before the next wave starts.":R.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U(HS,{palette:t,label:"Pipeline",detail:`${r.waves.length} execution wave${r.waves.length===1?"":"s"}`},void 0,!1,void 0,this),U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:r.waves.map((C,j)=>{let B=Fs(C,n,s),z=kf(B,t),de=Z?`W${j+1} ${z.icon} ${C.length}`:`W${j+1} ${z.icon} ${z.label} \xB7 ${C.length}`;return U("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:[U(Be,{palette:t,label:de,tone:z.tone},void 0,!1,void 0,this),j<r.waves.length-1?U("box",{width:3,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:" \u2500\u25BA"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},`step-${j}`,!0,void 0,this)})},void 0,!1,void 0,this),(r.blockers??[]).length>0?U("box",{minHeight:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:U("text",{fg:t.error,wrapMode:"word",children:`BLOCKED ${r.blockers?.map((C)=>`${C.name}: ${C.message}`).join(" \xB7 ")}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,d.length>0?U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,paddingLeft:1,children:U("text",{wrapMode:"none",children:[U("span",{fg:t.success,children:"\u2713 ALREADY READY"},void 0,!1,void 0,this),U("span",{fg:t.muted,children:` ${d.join(", ")}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this):null,U(HS,{palette:t,label:"Wave details",detail:"live process and health state"},void 0,!1,void 0,this),U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:[U("box",{width:2,flexShrink:0},void 0,!1,void 0,this),U("box",{width:w,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("SERVICE",w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:12,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("STATE",12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?U("box",{width:9,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("PORT",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?U("box",{flexGrow:1,overflow:"hidden",children:U("text",{fg:t.muted,children:"DETAIL"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),U("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:U("box",{flexDirection:"column",overflow:"hidden",gap:1,children:p.map(({wave:C,waveIdx:j})=>{let B=Fs(C,n,s),z=kf(B,t),de=`${Ew(s,j,B)} \xB7 ${C.length} service${C.length===1?"":"s"}`;return U("box",{border:!0,borderStyle:"rounded",borderColor:z.color,title:de,titleColor:z.color,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:C.map((Ee)=>{let Ne=n?.services[Ee],ne=tr(Ne),H=_w(Ee,r,n,s),ae=Ee===i||ne==="FAILED",Q=ne==="UNHEALTHY",nt=ne==="HEALTHY"||s==="stop"&&ne==="STOPPED",ft=go(Ne),ie=ae?"\u2717":nt?"\u2713":Q?"\u26A0":ne==="STARTING"||ne==="RUNNING"||ne==="STOPPING"||ne==="RESTARTING"?"\u25CF":"\u25CB",Le=ae?t.error:et(t,ne),Me=ae?t.error:Q?t.warning:nt?t.success:t.muted;return U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[U("box",{width:2,flexShrink:0,overflow:"hidden",children:U("text",{fg:Le,children:ie},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:w,flexShrink:0,overflow:"hidden",children:U("text",{fg:Ze(Ee,t),wrapMode:"none",children:oe(Ee,w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:12,flexShrink:0,overflow:"hidden",children:U("text",{fg:et(t,ne),wrapMode:"none",children:oe(ne,12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?U("box",{width:9,flexShrink:0,overflow:"hidden",children:U("text",{fg:ft?t.info:t.muted,wrapMode:"none",children:oe(ft?`:${ft}`:"\u2014",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?U("box",{flexGrow:1,overflow:"hidden",children:U("text",{fg:Me,wrapMode:"none",children:H},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},Ee,!0,void 0,this)})},`wave-card-${j}`,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{height:3,flexShrink:0,flexDirection:"column",backgroundColor:y,border:["top"],borderStyle:"single",borderColor:g.color,paddingLeft:1,paddingRight:1,overflow:"hidden",onMouseDown:u,children:[U("text",{fg:S,wrapMode:"none",children:R.primary},void 0,!1,void 0,this),U("text",{fg:i?t.inverse:t.muted,wrapMode:"none",children:R.secondary},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var BS=I(()=>{Ot();xl();Lt();ke();tt()});import{jsxDEV as yr}from"@opentui/react/jsx-dev-runtime";function US(e){let{palette:t,route:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;return yr(pt,{palette:t,title:r.name,bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:78,preferH:18,gap:1,children:yr("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:yr("box",{flexDirection:"column",overflow:"hidden",children:[yr("text",{fg:t.muted,children:`auth ${r.authType||"none"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`identity ${r.identityType||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`account ${r.serviceAccount||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`audience ${r.audience||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,wrapMode:"word",children:`client id ${r.clientId||"ADC default"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,wrapMode:"word",children:`client secret ${r.clientSecret||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`match host ${r.matchHost||"*"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`match path ${r.matchPath||"*"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`upstream ${r.upstream}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var WS=I(()=>{ke()});import{jsxDEV as El}from"@opentui/react/jsx-dev-runtime";function GS(e){let{palette:t,title:r,body:n,termW:o,termH:i,scrollRef:s}=e,a=n.length>400||n.split(`
|
|
2232
|
-
`).length>16;return El(pt,{palette:t,title:r,bottomTitle:"j/k scroll \xB7 esc close",termW:o,termH:i,preferW:a?88:76,preferH:a?24:16,gap:1,children:El("scrollbox",{ref:s,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:El("box",{flexDirection:"column",overflow:"hidden",children:El("text",{fg:n===""?t.muted:t.text,wrapMode:"word",children:n===""?"(empty)":n},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var KS=I(()=>{ke()});import{jsxDEV as Wt}from"@opentui/react/jsx-dev-runtime";function qS(e){let{palette:t,items:r,query:n,selected:o,onQuery:i,onSubmit:s,title:a="commands"}=e,c=xo(n)!=="",l=Lx(o,Cf,r.length),d=c?r.slice(l,l+Cf):Dx(r,o,Cf),u=c?[{group:"",items:d}]:$p(d),f=Math.max(d.length+(c?0:u.length),1),p=ui(r,o),m=_x(r);return Wt("box",{height:f+bM,flexShrink:0,border:!0,borderStyle:"rounded",borderColor:t.borderActive,titleColor:t.primary,backgroundColor:t.panel,title:a,flexDirection:"column",overflow:"hidden",children:[d.length===0?Wt("box",{height:1,paddingLeft:1,overflow:"hidden",children:Wt("text",{fg:t.muted,children:"no matching command"},void 0,!1,void 0,this)},void 0,!1,void 0,this):u.flatMap((h)=>[...h.group===""?[]:[Wt("box",{height:1,paddingLeft:1,overflow:"hidden",backgroundColor:t.panel,children:Wt("text",{fg:t.muted,wrapMode:"none",children:h.group},void 0,!1,void 0,this)},`g-${h.group}`,!1,void 0,this)],...h.items.map((b)=>{let g=sl(b)===(p?sl(p):"");return Wt("box",{height:1,flexDirection:"row",overflow:"hidden",paddingLeft:1,paddingRight:1,backgroundColor:g?t.highlight:t.panel,children:[Wt("box",{width:m,flexShrink:0,overflow:"hidden",children:Wt("text",{fg:g?t.primary:t.text,wrapMode:"none",children:`${g?"\u203A":" "} /${Fp(b).slice(0,Op)}`},void 0,!1,void 0,this)},void 0,!1,void 0,this),Wt("box",{flexGrow:1,overflow:"hidden",children:Wt("text",{fg:g?t.accent:t.muted,wrapMode:"none",children:Px(b)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},sl(b),!0,void 0,this)})]),Wt("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.highlight,paddingLeft:1,children:[Wt("box",{width:2,flexShrink:0,children:Wt("text",{fg:t.primary,children:"/"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Wt("box",{flexGrow:1,overflow:"hidden",children:Wt("input",{focused:!0,value:n,placeholder:"start logs settings",onInput:i,onSubmit:()=>s(),backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var Cf=10,bM=3;var VS=I(()=>{wo()});import{jsxDEV as JS}from"@opentui/react/jsx-dev-runtime";function XS(e){let{palette:t,themeName:r,selected:n,termW:o,termH:i,onIndex:s,onPreview:a,onPick:c}=e,l=St.map((u)=>({name:u===r?`${u} (active)`:u,description:u===r?`${pp[u]} \xB7 current`:pp[u],value:u})),d=ol(o,i,zS,YS,"center",!ho(ye()));return JS(pt,{palette:t,title:"/themes",bottomTitle:"enter save \xB7 esc revert",termW:o,termH:i,preferW:zS,preferH:YS,children:JS("select",{height:Math.max(4,d.height-3),options:l,selectedIndex:n,showDescription:!0,showSelectionIndicator:!0,wrapSelection:!0,backgroundColor:t.panel,focusedBackgroundColor:t.panel,textColor:t.text,focusedTextColor:t.text,selectedBackgroundColor:t.highlight,selectedTextColor:t.primary,descriptionColor:t.muted,selectedDescriptionColor:t.accent,onChange:(u)=>{s(u);let f=St[u];if(f)a(f)},onSelect:(u,f)=>{let p=f?.value;if(typeof p==="string"&&p!=="")c(p)}},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var zS=52,YS=22;var QS=I(()=>{Mt();vr();ke();Er();tt()});function Tl(e,t,r){let n=e?.google?.project_id??"";if(n!=="")return{project:n,source:"configuration"};let o=t?.project||r?.projectID||"",i=t?.project_source||r?.projectSource||"";return{project:o,source:i}}import{jsxDEV as st}from"@opentui/react/jsx-dev-runtime";function Al(e){let{palette:t,title:r,tone:n="muted",children:o}=e,i=n==="success"?t.success:n==="warning"?t.warning:n==="error"?t.error:t.border;return st("box",{border:!0,borderStyle:"rounded",borderColor:i,title:r,titleColor:i,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,marginBottom:1,overflow:"hidden",children:o},void 0,!1,void 0,this)}function ZS(e){let{palette:t,cfg:r,google:n,identity:o}=e;if(!n&&!o)return st(fl,{palette:t,label:"Checking Application Default Credentials\u2026"},void 0,!1,void 0,this);let i=xM(r,o),s=o?.user||n?.userEmail||"",{project:a,source:c}=Tl(r,o,n),l=o?.adc??n?.adcAvailable===!0,d=o?.iap??(r?.proxy.routes??[]).some((h)=>h.auth.type.toLowerCase()==="iap"),u=i.filter((h)=>h.inactive!==!0),f=u.some((h)=>h.ok===!0),p=u.some((h)=>h.ok===!1),m=f?"AVAILABLE":p?"UNAVAILABLE":u.length>0?"NOT PROBED":i.length>0?"not required":"not configured";return st(kt,{palette:t,title:"identity",scroll:!0,children:[st(Al,{palette:t,title:"Google identity",tone:l?"success":"warning",children:[st(So,{palette:t,label:"user",value:s||"(unknown)"},void 0,!1,void 0,this),st(So,{palette:t,label:"project",value:a||"(unset)"},void 0,!1,void 0,this),st(So,{palette:t,label:"source",value:c||"\u2014"},void 0,!1,void 0,this),st(So,{palette:t,label:"ADC",value:l?"\u2713 available":"\u2717 missing",tone:l?"success":"error"},void 0,!1,void 0,this),st(So,{palette:t,label:"gcloud",value:n?.gcloudInstalled?"\u2713 installed":"\u2717 not installed",tone:n?.gcloudInstalled?"success":"error"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Al,{palette:t,title:`Service accounts (${i.length})`,tone:u.length===0?"muted":f?"success":"warning",children:[i.length===0?st("text",{fg:t.muted,wrapMode:"word",children:"none configured \u2014 identities come from config, never hard-coded"},void 0,!1,void 0,this):i.map((h)=>st("text",{fg:h.inactive?t.muted:h.ok===!0?t.success:h.ok===!1?t.error:t.text,wrapMode:"none",children:`${h.inactive?"\u2013":h.ok===!0?"\u2713":h.ok===!1?"\u2717":"\u25CB"} ${h.email}${h.inactive?" \u2014 inactive (route auth is none)":""}`},h.email,!1,void 0,this)),st("box",{height:1,flexShrink:0},void 0,!1,void 0,this),st(So,{palette:t,label:"impersonation",value:m,tone:f?"success":u.length>0?"warning":"muted"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Al,{palette:t,title:"IAP",tone:d?"success":"muted",children:[st("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:st(Be,{palette:t,label:d?"routes present":"no IAP routes",tone:d?"success":"idle"},void 0,!1,void 0,this)},void 0,!1,void 0,this),d?st("text",{fg:t.muted,wrapMode:"word",children:"each route needs its own audience"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),!l?st(Al,{palette:t,title:"ADC unavailable",tone:"warning",children:[st("text",{fg:t.text,wrapMode:"word",children:"Local services still start. Cloud identity, impersonation, and IAP need ADC."},void 0,!1,void 0,this),st("text",{fg:t.muted,wrapMode:"word",children:"/auth login suspends this screen and runs gcloud ADC login, then restores the TUI"},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)}function xM(e,t){let r=new Map((e?iu(e):[]).map((i)=>[i.email,i.active])),n=t?.service_account_status??{};return[...new Set([...r.keys(),...Object.keys(n)])].sort((i,s)=>i.localeCompare(s)).map((i)=>{let s=n[i]??"unknown",a=r.get(i)===!1;return{email:i,inactive:a,ok:a||s==="unknown"?void 0:s==="available"}})}var ek=I(()=>{jr();Yt();ke()});import{jsxDEV as M}from"@opentui/react/jsx-dev-runtime";function ok(e){let{palette:t,cfg:r,width:n,selectedTask:o=0}=e,i=ye();if(!r)return M(ze,{palette:t,title:"No configuration",body:"Create .devctl/config.yaml or run setup."},void 0,!1,void 0,this);let s=Math.max(vi+8,n-6-i.pad*2),a=s>=Qw,c=a?Math.floor((s-rk)/2):s,l=Gw(r),d=Kw(r),u=qw(r),f=Vw(r),p=zw(r);return M(kt,{palette:t,title:`config ${r.project.name||"unnamed"}`,children:[M(ut,{palette:t,items:Nw(r)},void 0,!1,void 0,this),M("box",{flexGrow:1,overflow:"hidden",children:M("scrollbox",{ref:e.scrollRef,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:M("box",{flexDirection:"column",overflow:"hidden",gap:nk,children:[M(Pf,{wide:a,left:M(qr,{palette:t,title:"project",children:M(bi,{palette:t,facts:Hw(r,jt),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:"google",children:M(bi,{palette:t,facts:jw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Pf,{wide:a,left:M(qr,{palette:t,title:"runtime",children:M(bi,{palette:t,facts:Bw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:"logs",children:M(bi,{palette:t,facts:Uw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:`proxy ${r.proxy.enabled?"enabled":"disabled"}`,children:[M(bi,{palette:t,facts:Ww(r),width:s},void 0,!1,void 0,this),M(SM,{palette:t,routes:d,width:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this),M(qr,{palette:t,title:`services ${l.length}`,children:M(wM,{palette:t,rows:l,width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:`tasks ${p.length}`,children:M(tk,{palette:t,rows:p,empty:"no tasks",width:s,selected:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Pf,{wide:a,left:M(qr,{palette:t,title:`profiles ${u.length}`,children:M(kM,{palette:t,rows:u,width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:`templates ${f.length}`,children:M(tk,{palette:t,rows:f,empty:"no templates",width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:"extras",children:M(bi,{palette:t,facts:Jw(r),width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:M(it,{palette:t,hints:[{key:"v",label:"validate/save buffer"},{key:"e",label:"open in $EDITOR"},{key:"/diff",label:"config sources"},{key:"/reload",label:"re-read .devctl after editing"},{key:"enter",label:"run task"},{key:"j/k",label:"scroll"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Pf(e){let{wide:t,left:r,right:n}=e;if(!t)return M("box",{flexDirection:"column",overflow:"hidden",gap:nk,flexShrink:0,children:[r,n]},void 0,!0,void 0,this);return M("box",{flexDirection:"row",overflow:"hidden",gap:rk,flexShrink:0,children:[M("box",{flexGrow:1,flexBasis:0,overflow:"hidden",children:r},void 0,!1,void 0,this),M("box",{flexGrow:1,flexBasis:0,overflow:"hidden",children:n},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function qr(e){let{palette:t,title:r,children:n}=e;return M("box",{border:!0,borderStyle:"rounded",borderColor:t.border,title:r,titleColor:t.accent,paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",flexShrink:0,children:n},void 0,!1,void 0,this)}function bi(e){let{palette:t,facts:r,width:n}=e,o=Math.max(4,n-vi-4);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((i)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:vi,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.muted,wrapMode:"none",children:oe(i.label,vi)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:_M(t,i.tone),wrapMode:"none",children:oe(i.value,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},i.label,!0,void 0,this))},void 0,!1,void 0,this)}function wM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no services"},void 0,!1,void 0,this);let o=Xw(r),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:Ze(s.name,t),wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(CM(s),i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},s.name,!0,void 0,this))},void 0,!1,void 0,this)}function SM(e){let{palette:t,routes:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no routes"},void 0,!1,void 0,this);let o=Math.min(vi+4,Math.max(8,...r.map((s)=>s.name.length))),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.primary,wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(PM(s),i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${s.name}-${s.match}`,!0,void 0,this))},void 0,!1,void 0,this)}function kM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no profiles"},void 0,!1,void 0,this);let o=Math.min(12,Math.max(8,...r.map((s)=>s.name.length))),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.primary,wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(s.env==="\u2014"?s.services:`${s.services} env ${s.env}`,i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},s.name,!0,void 0,this))},void 0,!1,void 0,this)}function tk(e){let{palette:t,rows:r,empty:n,width:o,selected:i}=e;if(r.length===0)return M(Ll,{palette:t,text:n},void 0,!1,void 0,this);let s=Math.min(14,Math.max(8,...r.map((c)=>c.name.length))),a=Math.max(8,o-s-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((c,l)=>{let d=i===l;return M("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:d?t.highlight:void 0,children:[M("box",{width:s,flexShrink:0,overflow:"hidden",children:M("text",{fg:d?t.primary:t.text,wrapMode:"none",children:oe(c.name,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:a,flexShrink:0,overflow:"hidden",children:M("text",{fg:d?t.accent:t.muted,wrapMode:"none",children:Re(c.summary,a)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},c.name,!0,void 0,this)})},void 0,!1,void 0,this)}function Ll(e){return M("box",{height:1,overflow:"hidden",children:M("text",{fg:e.palette.muted,wrapMode:"none",children:e.text},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function CM(e){let t=e.extends==="\u2014"?"":` ${e.extends}`;return` ${e.ports} ${e.identity} ${e.health} ${e.restart} \u2190 ${e.depends}${t}`}function PM(e){return` ${e.auth} ${e.identity} ${e.match} \u2192 ${e.upstream}`}function _M(e,t){if(t==="success")return e.success;if(t==="warning")return e.warning;if(t==="error")return e.error;if(t==="muted")return e.muted;return e.text}var rk=1,nk=1;var ik=I(()=>{Yt();gf();Mt();Ot();ke();tt()});import{jsxDEV as Ft}from"@opentui/react/jsx-dev-runtime";function sk(e){let{palette:t,credentials:r}=e;if(!r)return Ft(fl,{palette:t,label:"Reading credential store\u2026"},void 0,!1,void 0,this);let n=r.entries.filter((i)=>i.valid).length,o=r.entries.length-n;return Ft(kt,{palette:t,title:"credentials",scroll:!0,children:[Ft("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[Ft(Be,{palette:t,label:`backend: ${r.backend}`,tone:"info"},void 0,!1,void 0,this),r.entries.length>0?Ft(Be,{palette:t,label:`${n} valid`,tone:"success"},void 0,!1,void 0,this):null,o>0?Ft(Be,{palette:t,label:`${o} expired`,tone:"warning"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),Ft("text",{fg:t.muted,wrapMode:"word",children:"Tokens stay in the OS keychain or ~/.devctl/credentials (0600). They are never shown here."},void 0,!1,void 0,this),Ft("box",{height:1,flexShrink:0},void 0,!1,void 0,this),r.entries.length===0?Ft("text",{fg:t.muted,children:"no cached credentials"},void 0,!1,void 0,this):Ft("box",{flexDirection:"column",overflow:"hidden",gap:1,children:r.entries.map((i)=>Ft("box",{border:!0,borderStyle:"rounded",borderColor:i.valid?t.success:t.warning,title:i.identity,titleColor:i.valid?t.success:t.warning,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[Ft("text",{wrapMode:"none",children:[Ft("span",{fg:i.valid?t.success:t.warning,children:i.valid?"\u2713 valid":"\u2717 expired"},void 0,!1,void 0,this),Ft("span",{fg:t.muted,children:` \xB7 expires ${i.expires_at}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Ft("text",{fg:t.muted,wrapMode:"none",children:`audience: ${i.audience||"(none)"}`},void 0,!1,void 0,this)]},`${i.identity}|${i.audience}`,!0,void 0,this))},void 0,!1,void 0,this),Ft(pl,{palette:t,title:"Actions",body:"Use /refresh to mint again or auth refresh on the CLI. Invalidate clears the store without printing tokens.",hint:"/refresh /auth"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var ak=I(()=>{Yt();ke()});import{jsxDEV as Dl}from"@opentui/react/jsx-dev-runtime";function xi(e){let t=e.newer>0?`g jump to latest \xB7 ${e.newer} new`:"g jump to latest",r=Math.min(e.width,t.length+4);return Dl("box",{position:"absolute",left:Math.max(0,Math.floor((e.width-r)/2)),bottom:e.bottom??2,width:r,height:3,border:!0,borderStyle:"rounded",borderColor:e.palette.warning,backgroundColor:e.palette.panel,alignItems:"center",justifyContent:"center",onMouseDown:e.onJump,children:Dl("text",{wrapMode:"none",children:[Dl("span",{fg:e.palette.primary,children:"g"},void 0,!1,void 0,this),Dl("span",{fg:e.palette.text,children:t.slice(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var ck=()=>{};import{jsxDEV as Hs}from"@opentui/react/jsx-dev-runtime";function wi(e){let{palette:t,logs:r,names:n,service:o,errorOnly:i,width:s,onService:a,onToggleErrors:c,facets:l}=e,d=l?Yb(n,l):zb(n,r),u=s<80,f=d.map((v)=>{let b=v.name===""?"all":v.name;return{id:b,label:u?b:`${b} \xB7 ${v.count}`,color:Ze(v.name,t)}}),p=Math.max(0,d.findIndex((v)=>v.name===o)),m=i?"ERROR+":u?"lvls":"all levels",h=Math.max(ci(f[p]?.label??"all"),s-ci(m));return Hs(Ut,{palette:t,backgroundColor:t.element,children:Hs("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[Hs("box",{flexGrow:1,overflow:"hidden",children:Hs(ul,{palette:t,items:f,active:p,width:h,emphasis:"fill",onPick:(v)=>{let b=d[v];if(b)a(b.name)}},void 0,!1,void 0,this)},void 0,!1,void 0,this),Hs(Be,{palette:t,label:m,tone:i?"error":"muted",onMouseDown:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var lk=I(()=>{gr();In();ke();tt()});import{jsxDEV as RM}from"@opentui/react/jsx-dev-runtime";function js(e){let t=Math.min(e.total,e.start+e.count),r=Math.max(0,e.start),n=Math.max(0,e.total-t);return RM(ut,{palette:e.palette,items:[{text:`view ${e.start+1}\u2013${t} of ${e.total}`,tone:"primary"},{text:r>0?`\u2191 ${r} older`:"start of history",tone:r>0?"info":"idle"},{text:n>0?`\u2193 ${n} newer`:"at latest",tone:n>0?"warning":"success"}],hints:[{key:"pgup/pgdn",label:"move history window"},{key:"g",label:"latest"}]},void 0,!1,void 0,this)}var dk=I(()=>{ke()});import{useEffect as _f,useRef as uk}from"react";import{jsxDEV as Ye}from"@opentui/react/jsx-dev-runtime";function Si(e){let{palette:t,logs:r,limit:n,width:o=80,followTick:i=0,focused:s=!1,wrapMode:a="clip",selected:c=-1,follow:l=!0,showTimestamps:d=!0,showMeta:u=!0,onPick:f,onLeaveLatest:p,viewStart:m=0,search:h="",regex:v=!1}=e,b=uk(null),g=uk(""),_=n===void 0?r:r.slice(-n),R=[...new Set(r.map((k)=>k.service))],S=Ub(o,R),y=Bb({width:o,serviceWidth:S,showTimestamps:d,showMeta:u}),x=_[_.length-1],w=x===void 0?"empty":String(x.seq);return _f(()=>{let k=b.current;if(!k)return;if(k.stickyScroll=l,l)k.stickyStart="bottom",k.scrollTo({x:k.scrollLeft,y:Math.max(0,k.scrollHeight)})},[l,i,w]),_f(()=>{let k=`${m}:${c}:${a}`;if(g.current===k)return;if(g.current=k,l&&(c<0||c>=_.length-1))return;let D=b.current;if(!D||c<0)return;let O=`log-row-${c}`,F=requestAnimationFrame(()=>{D.stickyScroll=!1,D.scrollChildIntoView(O)});return()=>cancelAnimationFrame(F)},[l,c,_.length,m,a]),_f(()=>{if(!l||!p)return;let k=!1,D=0,O=setInterval(()=>{let F=b.current;if(!F)return;let Z=F.viewport.height;if(Z<=0)return;let W=F.scrollHeight;if(F.scrollTop+Z>=W-TM){k=!0,D=W;return}if(W>D){F.stickyScroll=!0,F.stickyStart="bottom",F.scrollTo({x:F.scrollLeft,y:Math.max(0,W)}),D=W;return}if(k)p()},EM);return()=>{clearInterval(O)}},[l,i,p]),Ye("box",{flexGrow:1,height:"100%",overflow:"hidden",children:Ye("scrollbox",{ref:b,focused:s,stickyScroll:l,stickyStart:"bottom",scrollX:!1,style:{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:t.panel},viewportOptions:{backgroundColor:t.panel},contentOptions:{backgroundColor:t.panel},scrollbarOptions:{trackOptions:{foregroundColor:t.primary,backgroundColor:t.element}}},children:Ye("box",{flexDirection:"column",overflow:"hidden",children:_.map((k,D)=>Ye(AM,{id:`log-row-${D}`,palette:t,event:k,serviceWidth:S,msgWidth:y,expanded:Hb(a,D===c),active:D===c,showTimestamps:d,showMeta:u,onPick:f?()=>f(D):void 0,search:h,regex:v},`${k.timestamp}-${k.service}-${D}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function AM(e){let{id:t,palette:r,event:n,serviceWidth:o,msgWidth:i,expanded:s,active:a,showTimestamps:c=!0,showMeta:l=!0,onPick:d,search:u="",regex:f=!1}=e,p=Nb(Xc(n.message,i),i,s);return Ye("box",{id:t,flexShrink:0,flexDirection:"column",overflow:"hidden",backgroundColor:a?r.highlight:void 0,onMouseDown:d,children:p.visible.map((m,h)=>Ye("box",{height:1,flexDirection:"row",overflow:"hidden",children:[c?Ye("box",{width:Vc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0?n.timestamp.slice(11,19):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:o,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:h===0?Ze(n.service,r):r.muted,wrapMode:"none",children:h===0?oe(n.service,o):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{width:As,flexShrink:0,overflow:"hidden",children:Ye("text",{children:" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{width:zc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:h===0?et(r,n.level):r.muted,children:h===0?Yc(String(n.level)):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),l?Ye("box",{width:qc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0&&n.source?oe(n.source,qc):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:As,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0?" ":"\u2502"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{flexGrow:1,overflow:"hidden",children:Ye(LM,{palette:r,level:n.level,text:m,mark:h===0?p.mark:"",search:u,regex:f},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${n.timestamp}-${h}`,!0,void 0,this))},void 0,!1,void 0,this)}function LM(e){let{palette:t,level:r,text:n,mark:o,search:i="",regex:s=!1}=e;return Ye("text",{wrapMode:"none",children:[jb(n,i,s).map((a,c)=>Ye("span",{fg:mb(t,r,a.kind),children:a.text},`${a.kind}-${c}`,!1,void 0,this)),o===""?null:Ye("span",{fg:t.muted,children:o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var EM=200,TM=2;var pk=I(()=>{Ot();gr();tt()});var Rf=I(()=>{ck();lk();dk();pk()});import{useRef as DM}from"react";import{jsxDEV as Ie}from"@opentui/react/jsx-dev-runtime";function Il(e){let{palette:t,names:r,snap:n,selected:o,checked:i,width:s,onOpen:a,onSelectIndex:c,onToggle:l}=e,d=ye(),u=DM(!1),f=new Set(i),p=_p(s),m=Pp(s),h=Cp(s),v=Eb(s);return Ie("box",{flexDirection:"column",flexGrow:1,overflow:"hidden",children:[Ie("box",{height:1,flexDirection:"row",overflow:"hidden",children:[Ie(Gt,{width:2,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:4,fg:t.accent,text:"sel"},void 0,!1,void 0,this),Ie(Gt,{width:2,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:v,fg:t.muted,text:"name"},void 0,!1,void 0,this),Ie(Gt,{width:$t,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:Tr,fg:t.muted,text:"state"},void 0,!1,void 0,this),h?Ie(Gt,{width:Ur,fg:t.muted,text:"health"},void 0,!1,void 0,this):null,m?Ie(Gt,{width:$c,fg:t.muted,text:"port"},void 0,!1,void 0,this):null,p?Ie(Gt,{width:sn,fg:t.muted,text:"pid"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),r.map((b,g)=>{let _=n?.services[b],R=tr(_),S=_?.health??"UNKNOWN",y=f.has(b),x=g===o,w=y?"[x]":"[ ]",k=x?"\u203A":" ";return Ie("box",{height:d.rowH,flexDirection:"row",overflow:"hidden",backgroundColor:x?t.highlight:y?t.element:void 0,onMouseDown:()=>{if(u.current)return;if(x){a(b);return}c(g)},children:[Ie("box",{width:2,flexShrink:0,children:Ie("text",{fg:t.primary,children:k},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie("box",{width:4,flexShrink:0,onMouseDown:()=>{u.current=!0,l?.(b),setTimeout(()=>{u.current=!1},0)},children:Ie("text",{fg:IM(t,y,x),children:w},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie("box",{width:2,flexShrink:0,children:Ie("text",{fg:et(t,R),children:hr(R)},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie(Gt,{width:v,fg:x?t.primary:Ze(b,t),text:oe(b,v)},void 0,!1,void 0,this),Ie(Gt,{width:$t,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:Tr,fg:et(t,R),text:R},void 0,!1,void 0,this),h?Ie(Gt,{width:Ur,fg:et(t,S),text:S},void 0,!1,void 0,this):null,m?Ie(Gt,{width:$c,fg:t.text,text:go(_)},void 0,!1,void 0,this):null,p?Ie(Gt,{width:sn,fg:t.text,text:_?.pid?String(_.pid):""},void 0,!1,void 0,this):null]},b,!0,void 0,this)})]},void 0,!0,void 0,this)}function Ml(e){let{palette:t,checked:r}=e,n=r.length;if(n>0)return Ie(ut,{palette:t,items:[{text:`${n} selected`,tone:"primary"}],hints:[{key:"space",label:"unmark"},{key:"n",label:"start these"},{key:"x",label:"stop these"}]},void 0,!1,void 0,this);return null}function IM(e,t,r){if(t)return e.success;if(r)return e.primary;return e.muted}function Gt(e){return Ie("box",{width:e.width,flexShrink:0,overflow:"hidden",children:Ie("text",{fg:e.fg,wrapMode:"none",children:e.text},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var Ef=I(()=>{Mt();Ot();Lt();ke();tt()});import{jsxDEV as fe}from"@opentui/react/jsx-dev-runtime";function mk(e){let{palette:t,cfg:r,snap:n,logs:o,names:i,selected:s,selectedLog:a=-1,checked:c,width:l,followTick:d,logService:u,logSources:f,errorOnly:p,showSystemLogs:m,onOpen:h,onSelectIndex:v,onToggle:b,onFilterService:g,onToggleErrors:_,onShowErrors:R,wrapMode:S="clip",view:y,follow:x=!0,onLeaveLatest:w,onPickLog:k,viewStart:D=0,viewTotal:O,newer:F=0,onJumpLatest:Z,facets:W,leftover:C,search:j="",regex:B=!1}=e,z=ye();if(!r||i.length===0)return fe(ze,{palette:t,title:"No services configured",body:"Add services to .devctl/config.yaml or finish setup.",hint:"open the setup screen with /setup"},void 0,!1,void 0,this);let de=l<li,Ee=jc(n),Ne=cl(n,i),ne=i.filter((ge)=>n?.services[ge]?.state==="FAILED").length,H=Vb(o),ae=Ee?Lb(C,n?.session_id):void 0,Q=Hc(l,i,de),nt=Math.max(24,de?l-4:l-Q-4),ft=m?o:o.filter((ge)=>!ai(ge.source)),ie=yo(o,{service:u,errorOnly:p,systemLogs:m,search:j,regex:B}),Le=y??ie,Me=O??ie.length,Ke=Math.min(Me,D+Le.length),gt=Me===0?"empty":`${D+1}\u2013${Ke} of ${Me}`,Xe=u===""?"all":u,Ct=[...Ne.running>0?[{text:`${Ne.running}/${i.length}`,tone:"success"}]:[],...ne>0?[{text:`${ne} failed`,tone:"error"}]:[],...c.length>0?[{text:`${c.length} selected`,tone:"primary"}]:[],...H>0?[{text:`${H} errors`,tone:"error",onMouseDown:R}]:[]];return fe("box",{flexGrow:1,flexDirection:de?"column":"row",overflow:"hidden",children:[fe("box",{flexGrow:de?1:0,flexShrink:0,minWidth:de?void 0:Q,width:de?void 0:Q,border:!0,borderStyle:"rounded",borderColor:Ee?t.border:t.borderActive,backgroundColor:t.panel,title:"services",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[Ct.length>0?fe(ut,{palette:t,items:Ct},void 0,!1,void 0,this):null,fe(Ml,{palette:t,checked:c},void 0,!1,void 0,this),fe("box",{flexGrow:1,paddingLeft:z.pad,paddingRight:z.pad,overflow:"hidden",children:fe(Il,{palette:t,names:i,snap:n,selected:s,checked:c,width:Nc(Q,z.pad),onOpen:h,onSelectIndex:v,onToggle:b},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe(FM,{palette:t,names:i,snap:n,width:Q,onOpen:h},void 0,!1,void 0,this),ae?fe(HM,{palette:t,leftover:ae,width:Q},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),fe("box",{position:"relative",flexGrow:2,minWidth:de?void 0:32,minHeight:de?8:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,backgroundColor:t.panel,title:`logs \xB7 ${Xe} \xB7 ${gt}`,titleColor:t.primary,overflow:"hidden",flexDirection:"column",children:[fe(wi,{palette:t,logs:ft,names:f??i,service:u,errorOnly:p,width:nt,onService:g,onToggleErrors:_,facets:W},void 0,!1,void 0,this),Me>Le.length?fe(js,{palette:t,start:D,count:Le.length,total:Me},void 0,!1,void 0,this):null,ie.length===0?fe(ze,{palette:t,title:Ee?"Waiting for logs":o.length===0?"No events yet":"No events in this filter",body:Ee?"Start services to stream output here. Later starts keep earlier logs.":o.length===0?"New lines appear as services write output.":"Click All or another service chip, or turn off ERROR+.",hint:"\u2190 \u2192 cycle service e errors g latest"},void 0,!1,void 0,this):fe(Si,{palette:t,logs:Le,width:nt,followTick:d,focused:!1,wrapMode:S,selected:a,follow:x,onLeaveLatest:w,onPick:k,viewStart:D,search:j,regex:B},d,!1,void 0,this),!x?fe(xi,{palette:t,width:nt,newer:F,bottom:0,onJump:Z},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function OM(e){if(!e)return!1;return e.state===dt||e.health===Oe||e.state===fr||e.restarts>0||e.last_error!==""}function fk(e){if(e.state===dt)return 0;if(e.health===Oe)return 1;if(e.state===fr)return 2;return 3}function $M(e){if(e.last_error!=="")return e.last_error;if(e.restarts>0)return`restarted ${e.restarts}x, no error recorded`;return e.health===Oe?"failing health check":e.state.toLowerCase()}function FM(e){let{palette:t,names:r,snap:n,width:o,onOpen:i}=e,s=r.map((d)=>({name:d,rt:n?.services[d]})).filter((d)=>OM(d.rt)).sort((d,u)=>fk(d.rt)-fk(u.rt));if(s.length===0)return null;let a=s.slice(0,MM),c=s.length-a.length,l=Math.max(8,o-Tf-4);return fe("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[fe("box",{height:1,overflow:"hidden",children:fe("text",{fg:t.error,wrapMode:"none",children:`issues (${s.length})`},void 0,!1,void 0,this)},void 0,!1,void 0,this),a.map(({name:d,rt:u})=>{let f=tr(u);return fe("box",{height:1,flexDirection:"row",overflow:"hidden",onMouseDown:()=>i(d),children:[fe("box",{width:2,flexShrink:0,children:fe("text",{fg:et(t,f),children:hr(f)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:Tf,flexShrink:0,overflow:"hidden",children:fe("text",{fg:Ze(d,t),wrapMode:"none",children:oe(d,Tf)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:l,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,wrapMode:"none",children:Re($M(u),l)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)}),c>0?fe("text",{fg:t.muted,wrapMode:"none",children:`+${c} more \u2014 open a service to see details`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function HM(e){let{palette:t,leftover:r,width:n}=e,o=r.processes.slice(0,NM),i=r.processes.length-o.length,s=Math.max(8,n-Af-4);return fe("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[fe("box",{height:1,overflow:"hidden",children:fe("text",{fg:t.warning,wrapMode:"none",children:"last session"},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("text",{fg:t.muted,wrapMode:"none",children:Re(`${r.session_id} ${r.profile||"(none)"}`,Math.max(8,n-4))},void 0,!1,void 0,this),o.map((a)=>fe("box",{height:1,flexDirection:"row",overflow:"hidden",children:[fe("box",{width:Af,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.text,wrapMode:"none",children:oe(a.name,Af)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:s,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,wrapMode:"none",children:Re(`pid ${a.pid} leftover`,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${a.name}-${a.pid}`,!0,void 0,this)),i>0?fe("text",{fg:t.muted,wrapMode:"none",children:`+${i} more leftover pids`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var MM=4,Tf=14,NM=4,Af=14;var hk=I(()=>{At();Yt();Mt();vr();Ot();gr();Lt();dl();ke();tt();Rf();Ef()});import jM from"cli-spinners";import{useEffect as BM,useState as UM}from"react";import{jsxDEV as re}from"@opentui/react/jsx-dev-runtime";function gk(e){let{palette:t,report:r,loading:n,progress:o,error:i,selected:s,onPick:a,onReload:c}=e,l=fi(s,Df);if(n)return re(WM,{palette:t,progress:o},void 0,!1,void 0,this);if(i)return re(ew,{palette:t,title:"Doctor failed",body:i},void 0,!1,void 0,this);if(!r)return re(ze,{palette:t,title:"Doctor",body:"Diagnostics have not run yet.",hint:"opening this screen runs doctor"},void 0,!1,void 0,this);let d=r.checks.filter((v)=>v.severity==="ok").length,u=r.checks.filter((v)=>v.severity==="warn").length,f=r.checks.filter((v)=>v.severity==="error").length,p=r.checks.length,m=si(p>0?d/p:0),h=f>0?t.error:u>0?t.warning:t.success;return re(kt,{palette:t,title:`doctor ${r.issues} issue(s)`,children:[re("box",{height:4,flexDirection:"column",flexShrink:0,alignItems:"center",justifyContent:"center",overflow:"hidden",border:!0,borderStyle:"rounded",borderColor:t.primary,backgroundColor:t.element,onMouseDown:c,children:[re("text",{wrapMode:"none",children:[re("span",{fg:t.inverse,bg:t.primary,children:" r "},void 0,!1,void 0,this),re("span",{fg:t.primary,children:" RUN DOCTOR AGAIN"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("text",{fg:t.muted,wrapMode:"none",children:"results stay cached until you refresh"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0,overflow:"hidden",children:re("text",{wrapMode:"none",children:[re("span",{fg:h,children:`[${m}]`},void 0,!1,void 0,this),re("span",{fg:t.success,children:` ${d} ok`},void 0,!1,void 0,this),u>0?re("span",{fg:t.warning,children:` \xB7 ${u} warn`},void 0,!1,void 0,this):null,f>0?re("span",{fg:t.error,children:` \xB7 ${f} error`},void 0,!1,void 0,this):null,u===0&&f===0?re("span",{fg:t.muted,children:" \xB7 all clear"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:re("box",{flexDirection:"column",overflow:"hidden",gap:1,children:r.checks.map((v,b)=>re(GM,{palette:t,check:v,index:b,active:b===s,onPick:a},`${v.name}-${b}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function WM({palette:e,progress:t}){let[r,n]=UM(0);return BM(()=>{let o=setInterval(()=>n((i)=>(i+1)%Lf.frames.length),Lf.interval);return()=>clearInterval(o)},[]),re("box",{flexGrow:1,alignItems:"center",justifyContent:"center",overflow:"hidden",children:re("box",{width:"80%",maxWidth:52,height:10,flexShrink:1,border:!0,borderStyle:"rounded",borderColor:e.borderActive,backgroundColor:e.panel,flexDirection:"column",alignItems:"center",justifyContent:"center",overflow:"hidden",children:[re("text",{wrapMode:"none",children:[re("span",{fg:e.primary,children:`${Lf.frames[r]} `},void 0,!1,void 0,this),re("span",{fg:e.text,children:"devctl doctor"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("text",{fg:e.text,wrapMode:"none",children:`Checking ${t.active}`},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),t.checks.slice(-4).map((o,i)=>re("text",{wrapMode:"none",children:[re("span",{fg:et(e,o.severity==="error"?"ERROR":o.severity==="warn"?"WARN":"OK"),children:`${hr(o.severity==="error"?"ERROR":o.severity==="warn"?"WARN":"OK")} `},void 0,!1,void 0,this),re("span",{fg:e.muted,children:o.name},void 0,!1,void 0,this)]},`${o.name}-${i}`,!0,void 0,this)),t.checks.length===0?re("text",{fg:e.muted,children:"Waiting for the first result\u2026"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function GM(e){let{palette:t,check:r,index:n,active:o,onPick:i}=e,s=r.severity==="error"?"ERROR":r.severity==="warn"?"WARN":"OK";if(r.severity==="ok")return re("box",{id:`${Df}-${n}`,flexDirection:"row",flexShrink:0,paddingLeft:1,overflow:"hidden",backgroundColor:o?t.highlight:void 0,onMouseDown:()=>i(n),children:re("text",{wrapMode:"none",children:[re("span",{fg:t.primary,children:o?"\u203A ":" "},void 0,!1,void 0,this),re("span",{fg:et(t,s),children:hr(s)},void 0,!1,void 0,this),re("span",{fg:o?t.primary:t.text,children:` ${r.name}`},void 0,!1,void 0,this),re("span",{fg:t.muted,children:r.message?` \xB7 ${r.message}`:""},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this);let c=r.severity==="error"?t.error:t.warning;return re("box",{id:`${Df}-${n}`,border:!0,borderStyle:"rounded",borderColor:o?t.borderActive:c,title:r.name,titleColor:c,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",backgroundColor:o?t.highlight:void 0,onMouseDown:()=>i(n),children:[re("text",{wrapMode:"word",children:[re("span",{fg:et(t,s),children:`${hr(s)} `},void 0,!1,void 0,this),re("span",{fg:t.text,children:r.message},void 0,!1,void 0,this)]},void 0,!0,void 0,this),r.hint?re("text",{fg:t.info,wrapMode:"word",children:`\u2192 ${r.hint}`},void 0,!1,void 0,this):null,o&&r.action?.kind==="free-port"?re("text",{fg:t.primary,wrapMode:"none",children:`enter stop ${r.action.holder.command} (pid ${r.action.holder.pid})`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Df="doctor-row",Lf;var vk=I(()=>{Yt();Ot();ke();tt();Lf=jM.bouncingBall});import{jsxDEV as We,Fragment as KM}from"@opentui/react/jsx-dev-runtime";function bk(e){let{palette:t,logs:r,names:n,logSources:o,service:i,errorOnly:s,showSystemLogs:a,search:c,searchFocused:l,followTick:d,width:u,onSearch:f,onService:p,onToggleErrors:m,onSelect:h,wrapMode:v="focus",selected:b=-1,follow:g=!0,newer:_=0,viewStart:R=0,viewTotal:S,onLeaveLatest:y,onJumpLatest:x,view:w,fullscreen:k=!1,facets:D,split:O=!1,splitFocus:F=0,serviceB:Z="",viewB:W,selectedB:C=-1,followB:j=!0,newerB:B=0,viewStartB:z=0,viewTotalB:de,onServiceB:Ee,onSelectB:Ne,onLeaveLatestB:ne,onFocusPane:H}=e,ae=ye(),Q=Ep(u,ae.pad,k),nt=a?r:r.filter((ge)=>!ai(ge.source)),ft=yo(r,{service:i,services:e.services,errorOnly:s,search:c,regex:e.regex,source:e.source,systemLogs:a}),ie=w??ft,Le=S??ft.length,Me=Math.min(Le,R+ie.length),Ke=Le===0?"empty":`${R+1}\u2013${Me} of ${Le}`,gt=e.services??[],Xe=gt.length>0?gt.join(","):i===""?"all services":i,Ct=[...g?[]:[{text:_>0?`${O?"left ":""}pinned \xB7 +${_} new`:`${O?"left ":""}pinned`,tone:"warning"}],...O&&!j?[{text:B>0?`right pinned \xB7 +${B} new`:"right pinned",tone:"warning"}]:[],...e.source?[{text:`src ${e.source}`,tone:"info"}]:[],...e.regex?[{text:"regex",tone:"accent"}]:[],...c===""?[]:[{text:`search ${c}`,tone:"accent"}],...O?[{text:F===0?"left pane":"right pane",tone:"accent"}]:[]];if(O){let ge=Math.max(20,Math.floor(u/2)),vt=Math.max(20,u-ge),at=W??[],yt=de??at.length;return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):We(ut,{palette:t,items:Ct},void 0,!1,void 0,this),We("box",{flexGrow:1,flexDirection:"row",overflow:"hidden",children:[We(yk,{palette:t,names:o??n,logs:r,service:i,errorOnly:s,showSystemLogs:a,search:c,regex:e.regex,source:e.source,width:ge,shown:ie,shownTotal:Le,selected:b,follow:g,newer:_,viewStart:R,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:D,focused:F===0,onService:p,onToggleErrors:m,onSelect:h,onLeaveLatest:y,onFocus:()=>H?.(0)},void 0,!1,void 0,this),We(yk,{palette:t,names:o??n,logs:r,service:Z,errorOnly:s,showSystemLogs:a,search:c,regex:e.regex,source:e.source,width:vt,shown:at,shownTotal:yt,selected:C,follow:j,newer:B,viewStart:z,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:D,focused:F===1,onService:Ee??p,onToggleErrors:m,onSelect:Ne,onLeaveLatest:ne,onFocus:()=>H?.(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),!g||!j?We(xi,{palette:t,width:u,newer:Math.max(_,B),onJump:x},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[k?null:We(KM,{children:[Ct.length>0?We(ut,{palette:t,items:Ct},void 0,!1,void 0,this):null,We(wi,{palette:t,logs:nt,names:o??n,service:i,errorOnly:s,width:u,onService:p,onToggleErrors:m,facets:D},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,w&&Le>ie.length?We(js,{palette:t,start:R,count:ie.length,total:Le},void 0,!1,void 0,this):null,We("box",{flexGrow:1,border:!k,borderStyle:"rounded",borderColor:t.border,title:k?void 0:`logs ${Xe} \xB7 ${Ke}`,titleColor:t.primary,padding:k?0:ae.pad,overflow:"hidden",children:ie.length===0?We(ze,{palette:t,title:r.length===0?"No log events":"No events in this filter",body:r.length===0?"Start services to stream logs.":"Pick All, another service, or clear search / ERROR+.",hint:`\u2190 \u2192 cycle filters e errors i internal ${It("l")} clear`},void 0,!1,void 0,this):We(Si,{palette:t,logs:ie,width:Q,followTick:d,focused:!1,wrapMode:v,selected:b,follow:g,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,onPick:h,onLeaveLatest:y,viewStart:R,search:c,regex:e.regex===!0},d,!1,void 0,this)},void 0,!1,void 0,this),!g?We(xi,{palette:t,width:u,newer:_,onJump:x},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function yk(e){let{palette:t,width:r,service:n,shown:o,shownTotal:i,viewStart:s,focused:a}=e,c=e.showSystemLogs?e.logs:e.logs.filter((u)=>!ai(u.source)),l=n===""?"all services":n,d=i===0?"empty":`${s+1}\u2013${Math.min(i,s+o.length)} of ${i}`;return We("box",{width:r,flexGrow:0,flexShrink:0,flexDirection:"column",overflow:"hidden",onMouseDown:e.onFocus,children:[We(wi,{palette:t,logs:c,names:e.names,service:n,errorOnly:e.errorOnly,width:r,onService:(u)=>{e.onFocus(),e.onService(u)},onToggleErrors:()=>{e.onFocus(),e.onToggleErrors()},facets:e.facets},void 0,!1,void 0,this),We("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:a?t.borderActive:t.border,title:`logs ${l} \xB7 ${d}`,titleColor:a?t.primary:t.muted,overflow:"hidden",children:o.length===0?We(ze,{palette:t,title:"No events in this pane",body:"Click the pane, then \u2190 \u2192 to pick a service.",hint:"\\\\ split | focus"},void 0,!1,void 0,this):We(Si,{palette:t,logs:o,width:Ep(r,0,!1),followTick:e.followTick,focused:!1,wrapMode:e.wrapMode,selected:e.selected,follow:e.follow,showTimestamps:e.showTimestamps,showMeta:e.showMeta,onPick:(u)=>{e.onFocus(),e.onSelect?.(u)},onLeaveLatest:e.onLeaveLatest,viewStart:s,search:e.search,regex:e.regex===!0},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var xk=I(()=>{Rf();Yt();Mt();gr();ke();Rr()});import{jsxDEV as Ge}from"@opentui/react/jsx-dev-runtime";function Sk(e){let{palette:t,cfg:r,snap:n,profile:o,selected:i,onPick:s}=e,a=ye(),c=fi(i,wk),l=Object.keys(r?.profiles??{}).sort();if(l.length===0)return Ge(ze,{palette:t,title:"No profiles",body:"Add profiles.<name>.services in config.yaml."},void 0,!1,void 0,this);return Ge(kt,{palette:t,title:"profiles",children:[Ge("box",{height:1,flexShrink:0,overflow:"hidden",children:Ge("text",{fg:t.muted,wrapMode:"none",children:[`${l.length} profile${l.length===1?"":"s"} \xB7 current: `,Ge("span",{fg:t.success,children:o||"(none)"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ge("box",{height:1,flexShrink:0},void 0,!1,void 0,this),Ge("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Ge("box",{flexDirection:"column",overflow:"hidden",gap:a.gap||1,children:l.map((d,u)=>{let f=d===o,p=u===i,m=r?.profiles[d]?.services??[],h=Object.keys(r?.profiles[d]?.environment??{}).length,v=p?t.borderActive:f?t.success:t.border,b=p?t.primary:f?t.success:t.muted;return Ge("box",{id:`${wk}-${u}`,border:!0,borderStyle:"rounded",borderColor:v,title:d,titleColor:b,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",backgroundColor:p?t.highlight:void 0,onMouseDown:()=>s(u),children:[Ge("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ge("text",{wrapMode:"none",children:[f?Ge("span",{fg:t.success,children:"\u25CF current profile"},void 0,!1,void 0,this):Ge("span",{fg:t.muted,children:"\u25CB not active"},void 0,!1,void 0,this),Ge("span",{fg:t.muted,children:` \xB7 ${m.length} service${m.length===1?"":"s"}`},void 0,!1,void 0,this),h>0?Ge("span",{fg:t.muted,children:` \xB7 ${h} env var${h===1?"":"s"}`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ge("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:m.length===0?Ge("text",{fg:t.muted,wrapMode:"none",children:"(no services)"},void 0,!1,void 0,this):Ge("text",{wrapMode:"word",children:m.map((g,_)=>{let R=tr(n?.services[g]);return Ge("span",{children:[Ge("span",{fg:et(t,R),children:hr(R)},void 0,!1,void 0,this),Ge("span",{fg:Ze(g,t),children:` ${g}`},void 0,!1,void 0,this),_<m.length-1?Ge("span",{fg:t.muted,children:" "},void 0,!1,void 0,this):null]},g,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ge(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:Ge(it,{palette:t,hints:[{key:"space",label:"set current"},{key:"enter",label:"set and start"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var wk="profile-row";var kk=I(()=>{Yt();Mt();Lt();ke();tt()});import{jsxDEV as V,Fragment as Rk}from"@opentui/react/jsx-dev-runtime";function qM(e){let t=e.toLowerCase();if(t==="iap")return"info";if(t==="service"||t==="service_account")return"primary";return"idle"}function VM(e,t,r){if(r||t>=500)return e.error;if(t===0||t>=400)return e.warning;return e.success}function zM(e,t){if(t.startsWith("sa:"))return{label:"SA",color:e.primary};if(t!=="")return{label:"USR",color:e.info};return{label:"\u2014",color:e.muted}}function QM(e){let{palette:t,req:r,cols:n}=e,o=VM(t,r.status,r.error),i=r.status>0?String(r.status):"ERR",s=zM(t,r.identity),a=r.route||Ek,c=r.error?`${r.path} \u2014 ${Re(r.error,XM)}`:r.path;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V("box",{width:If,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:r.timestamp.slice(11,19)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showMethod?V("box",{width:Ol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.text,wrapMode:"none",children:oe(r.method,Ol)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:$l,flexShrink:0,overflow:"hidden",children:V("text",{fg:o,wrapMode:"none",children:oe(i,$l)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showDur?V("box",{width:Fl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,wrapMode:"none",children:oe(`${r.durationMs}ms`,Fl)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Nl,flexShrink:0,overflow:"hidden",children:V("text",{fg:s.color,wrapMode:"none",children:oe(s.label,Nl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{width:n.routeCol,flexShrink:0,overflow:"hidden",children:V("text",{fg:r.route?t.info:t.muted,wrapMode:"none",children:oe(a,n.routeCol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,overflow:"hidden",children:V("text",{fg:r.error?t.error:t.text,wrapMode:"none",children:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function ZM(e){let{palette:t,cols:r}=e;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[V("box",{width:If,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("TIME",If)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showMethod?V("box",{width:Ol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("METHOD",Ol)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:$l,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ST",$l)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showDur?V("box",{width:Fl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("DUR",Fl)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Nl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("WHO",Nl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{width:r.routeCol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ROUTE",r.routeCol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,overflow:"hidden",children:V("text",{fg:t.muted,children:"PATH"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function eO(e){let{palette:t,name:r,auth:n,identity:o,upstream:i,match:s,onMouseDown:a}=e;return V("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",onMouseDown:a,children:[V("text",{fg:t.text,wrapMode:"none",children:r},void 0,!1,void 0,this),V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V(Be,{palette:t,label:n||"no auth",tone:qM(n)},void 0,!1,void 0,this),o?V(Be,{palette:t,label:o,tone:"idle"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),s?V("text",{fg:t.muted,wrapMode:"word",children:`match: ${s}`},void 0,!1,void 0,this):null,V("text",{fg:t.muted,wrapMode:"word",children:`\u2192 ${i}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Tk(e){let{palette:t,cfg:r,snap:n,width:o,onSelectRoute:i}=e,s=n?.proxy.routes??[],a=new Map((r?.proxy.routes??[]).map((S)=>[S.name,S])),c=new Map((r?.proxy.routes??[]).map((S)=>[S.name,S.match])),l=n?.proxy.recentRequests??[],d=n?.proxy.requestTotal??0,u=n?.proxy.requestErrors??0,f=o<li,p=Math.max(Ck,Math.min(tO,Math.floor(o*0.36))),m=f?o-Pk:o-p-Pk,h=Math.max(20,m-2),v=h>=_k,b=h>=rO,g=h>=_k?nO:oO,_=l.reduce((S,y)=>Math.min(g,Math.max(S,(y.route||Ek).length+JM)),YM),R={showMethod:v,showDur:b,routeCol:_};return V(kt,{palette:t,title:"proxy",children:[V(ut,{palette:t,items:[{text:n?.proxy.running?"RUNNING":"STOPPED",tone:n?.proxy.running?"success":"idle"},...n?.proxy.address?[{text:n.proxy.address,tone:"info"}]:[],{text:`${s.length} routes`,tone:s.length>0?"primary":"idle"},...d>0?[{text:`${d} requests`,tone:"idle"},...u>0?[{text:`${u} errors`,tone:"warning"}]:[]]:[]]},void 0,!1,void 0,this),V("box",{flexGrow:1,flexDirection:f?"column":"row",overflow:"hidden",children:[V("box",{flexGrow:f?1:0,flexBasis:f?0:void 0,flexShrink:0,minWidth:f?void 0:Ck,width:f?void 0:p,minHeight:f?8:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,title:"routes",titleColor:t.primary,paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",children:s.length===0?V(ze,{palette:t,title:"No proxy routes",body:"Add routes under proxy.routes or .devctl/proxy/routes.yaml."},void 0,!1,void 0,this):V(Rk,{children:[i?V("text",{fg:t.muted,wrapMode:"word",children:"click a route for full details"},void 0,!1,void 0,this):null,V("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:V("box",{flexDirection:"column",overflow:"hidden",gap:1,children:s.map((S)=>{let y=c.get(S.name),x=y&&(y.host||y.path)?`${y.host||"*"}${y.path?y.path:""}`:void 0,w=a.get(S.name);return V(eO,{palette:t,name:S.name,auth:S.auth,identity:S.identity,upstream:S.upstream,match:x,onMouseDown:i?()=>i({name:S.name,authType:w?.auth.type||S.auth,identityType:w?.auth.identity.type??"",serviceAccount:w?.auth.identity.service_account||w?.auth.service_account||"",audience:w?.auth.audience??"",clientId:w?.auth.client_id??"",clientSecret:Ba(w?.auth.client_secret??"")??((w?.auth.client_secret??"").trim()?"inline":""),matchHost:w?.match.host??"",matchPath:w?.match.path??"",upstream:S.upstream}):void 0},S.name,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,flexBasis:0,minWidth:f?void 0:30,minHeight:f?10:void 0,border:!0,borderStyle:"rounded",borderColor:t.borderActive,title:"requests",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:l.length===0?V("box",{paddingLeft:1,paddingRight:1,children:V("text",{fg:t.muted,wrapMode:"word",children:n?.proxy.running?"No requests seen yet. Send one through the proxy to see it show up here \u2014 no need to restart or press r.":"Start the proxy, then send it a request to see live traffic here."},void 0,!1,void 0,this)},void 0,!1,void 0,this):V(Rk,{children:[V(ZM,{palette:t,cols:R},void 0,!1,void 0,this),V("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:V("box",{flexDirection:"column",overflow:"hidden",children:l.map((S)=>V(QM,{palette:t,req:S,cols:R},S.requestId,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),V(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:V(it,{palette:t,hints:[{key:"n",label:"start proxy"},{key:"x",label:"stop proxy"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var If=9,Ol=8,$l=4,Fl=8,Nl=4,YM=8,JM=2,XM=64,Ek="(none)",Ck=26,tO=56,Pk=2,_k=46,rO=54,nO=20,oO=12;var Ak=I(()=>{Yt();vr();Ot();ke()});import{jsxDEV as pe}from"@opentui/react/jsx-dev-runtime";function Ik(e){let{palette:t,name:r}=e;return pe("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:r,titleColor:Ze(r,t),flexDirection:"column",overflow:"hidden",children:pe(Mf,{...e,compact:!1},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Mf(e){let{palette:t,cfg:r,snap:n,name:o,reveal:i,width:s,compact:a=!0,envScrollRef:c}=e,l=ye(),d=r?.services[o];if(!d)return pe(ze,{palette:t,title:"Unknown service",body:`No service named ${o}.`,hint:"j/k move"},void 0,!1,void 0,this);let u=n?.services[o],f=tr(u),p=u?.health??"UNKNOWN",m=go(u)||Es(d,u),h=Ab(d,i,r?.secrets.extra_markers??[],r?.secrets.extra_patterns??[],e.resolvedEnv),v=e.envStatus==="error"?"error":e.envStatus==="resolved"?"success":e.envStatus==="loading"?"info":"muted",b=e.envStatus==="resolved"?"resolved":e.envStatus==="loading"?"resolving":e.envStatus==="error"?"config fallback":"config",g=s>=sO,_=[{label:"health",value:Wc(d)},{label:"workdir",value:d.working_dir||"."},{label:"restart",value:Kc(d)},{label:"caps",value:d.capabilities.join(", ")||"\u2014"},{label:"runtime",value:d.container?`${d.container.runtime||"docker"} \xB7 ${d.container.image}`:"host"},...d.watch.enabled?[{label:"watch",value:d.watch.paths.join(" ")||"on"}]:[]],R=[{label:"identity",value:Gc(d,u)},{label:"depends",value:d.dependencies.map(Ra).join(", ")||"\u2014"},{label:"ports",value:Es(d,u)},{label:"tries",value:String(u?.restarts??0),tone:u?.restarts?"warning":"muted"}],S=Math.max(Co+8,s-2),y=g?Math.floor(S/2):S;return pe("box",{padding:l.pad,flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(ut,{palette:t,items:[{text:f,tone:Dk(f)},{text:p,tone:Dk(p)},{text:u?.pid?`pid ${u.pid}`:"pid \u2014",tone:u?.pid?"info":"idle"},{text:m==="\u2014"?"port \u2014":`port ${m}`,tone:m==="\u2014"?"idle":"info"}]},void 0,!1,void 0,this),d.description?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:Re(d.description,Math.max(8,s-4))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,u?.last_error?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.error,wrapMode:"none",children:Re(u.last_error,iO)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,pe(Of,{palette:t,item:{label:"command",value:Uc(d)},valueWidth:Math.max(8,S-Co)},void 0,!1,void 0,this),pe(aO,{palette:t,left:_,right:R,wide:g,colWidth:y},void 0,!1,void 0,this),pe(cO,{palette:t,entries:h,reveal:i,width:s,focused:!a,scrollRef:c,source:b,sourceTone:v,error:e.envError,onSelect:e.onSelectEnv},void 0,!1,void 0,this),a?null:pe(it,{palette:t,hints:[{key:"n",label:"start"},{key:"x",label:"stop"},{key:"R",label:"restart"},{key:"o",label:"config"},{key:"l",label:"logs"},{key:"/reveal",label:"secrets"},{key:"esc",label:"back"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function aO(e){let{palette:t,left:r,right:n,wide:o,colWidth:i}=e,s=Math.max(4,i-Co);if(!o)return pe("box",{width:i,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[...r,...n].map((a)=>pe(Of,{palette:t,item:a,valueWidth:s},a.label,!1,void 0,this))},void 0,!1,void 0,this);return pe("box",{height:r.length,flexShrink:0,flexDirection:"row",overflow:"hidden",children:[pe(Lk,{palette:t,items:r,width:i},void 0,!1,void 0,this),pe(Lk,{palette:t,items:n,width:i},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Lk(e){let t=Math.max(4,e.width-Co);return pe("box",{width:e.width,flexShrink:0,flexDirection:"column",overflow:"hidden",children:e.items.map((r)=>pe(Of,{palette:e.palette,item:r,valueWidth:t},r.label,!1,void 0,this))},void 0,!1,void 0,this)}function Of(e){let{palette:t,item:r,valueWidth:n}=e,o=r.tone==="warning"?t.warning:r.tone==="muted"?t.muted:t.text;return pe("box",{height:1,width:Co+n,flexDirection:"row",overflow:"hidden",children:[pe("box",{width:Co,flexShrink:0,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:oe(r.label,Co)},void 0,!1,void 0,this)},void 0,!1,void 0,this),pe("box",{width:n,flexShrink:0,overflow:"hidden",children:pe("text",{fg:o,wrapMode:"none",children:oe(r.value,n)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function cO(e){let{palette:t,entries:r,reveal:n,width:o,focused:i,scrollRef:s,source:a,sourceTone:c,error:l,onSelect:d}=e;return pe("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(ut,{palette:t,items:[{text:`env ${r.length}`,tone:r.length>0?"info":"idle"},{text:a,tone:c},{text:n?"secrets shown":"redacted",tone:n?"warning":"muted"}],hints:i?[{key:"j/k",label:"scroll"}]:[{key:"/reveal",label:"secrets"}]},void 0,!1,void 0,this),l?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.warning,wrapMode:"none",children:Re(l,Math.max(8,o-4))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,r.length===0?pe("text",{fg:t.muted,children:"no service env overrides"},void 0,!1,void 0,this):pe("box",{flexGrow:1,height:"100%",overflow:"hidden",children:[d?pe("text",{fg:t.muted,wrapMode:"word",children:"click a variable for its full value"},void 0,!1,void 0,this):null,pe("scrollbox",{ref:s,focused:i,stickyScroll:!1,scrollX:!1,style:{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:t.panel},viewportOptions:{backgroundColor:t.panel},contentOptions:{backgroundColor:t.panel},scrollbarOptions:{trackOptions:{foregroundColor:t.primary,backgroundColor:t.element}}},children:pe("box",{flexDirection:"column",overflow:"hidden",children:r.map((u)=>{let f=u.required?t.warning:u.fromConfig?t.accent:t.muted,p=u.value===""&&u.required?"(required)":u.value;return pe("box",{flexDirection:"row",overflow:"hidden",onMouseDown:d?()=>d(u):void 0,children:pe("text",{wrapMode:"word",children:[pe("span",{fg:f,children:u.required?`${u.key}*`:u.key},void 0,!1,void 0,this),pe("span",{fg:u.value===""?t.muted:t.text,children:` ${p}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},u.key,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function Dk(e){switch(e.toUpperCase()){case"HEALTHY":case"RUNNING":return"success";case"UNHEALTHY":case"WARNING":case"WARN":case"STARTING":case"RESTARTING":return"warning";case"FAILED":case"ERROR":return"error";default:return"muted"}}var iO=72,sO=56,Co=9;var $f=I(()=>{ht();Yt();Mt();Ot();Lt();ke();tt()});import{jsxDEV as Hn}from"@opentui/react/jsx-dev-runtime";function Mk(e){let{palette:t,cfg:r,names:n,snap:o,selected:i,checked:s,width:a,reveal:c,onOpen:l,onSelectIndex:d,onToggle:u,resolvedEnv:f,envStatus:p,envError:m,onSelectEnv:h}=e;if(n.length===0)return Hn(ze,{palette:t,title:"No services",body:"This configuration does not define any services."},void 0,!1,void 0,this);let v=a<li,b=n[i]??"",g=Hc(a,n,v),_=4,R=v?Math.max(Ff,a-_):Math.max(Ff,a-g-_);return Hn("box",{flexGrow:1,flexDirection:v?"column":"row",overflow:"hidden",children:[Hn("box",{flexGrow:v?1:0,flexShrink:0,minWidth:v?void 0:Oc,width:v?void 0:g,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"services",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[Hn(Ml,{palette:t,checked:s},void 0,!1,void 0,this),Hn("box",{flexGrow:1,overflow:"hidden",children:Hn(Il,{palette:t,names:n,snap:o,selected:i,checked:s,width:Nc(g),onOpen:l,onSelectIndex:d,onToggle:u},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Hn("box",{flexGrow:2,minWidth:v?void 0:Ff,minHeight:v?lO:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,backgroundColor:t.panel,title:b||"detail",titleColor:b?Ze(b,t):t.primary,overflow:"hidden",flexDirection:"column",children:Hn(Mf,{palette:t,cfg:r,snap:o,name:b,reveal:c,width:R,resolvedEnv:f,envStatus:p,envError:m,onSelectEnv:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Ff=36,lO=12;var Ok=I(()=>{Yt();vr();Lt();tt();$f();Ef()});import{jsxDEV as we}from"@opentui/react/jsx-dev-runtime";function uO(e){switch(e){case"cycle":return"\u2194";case"toggle":return"\u23FB";case"page":return"\u25B8";case"action":return"!";default:return"\xB7"}}function Hk(e){let{palette:t,items:r,selected:n,locked:o,width:i,onPick:s,onActivate:a}=e,c=ye(),l=vp(r,n),d=Ln(r,l);return we("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"settings",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[we("box",{height:2,paddingLeft:c.pad,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[we("text",{fg:t.primary,wrapMode:"none",children:"Preferences"},void 0,!1,void 0,this),we("text",{fg:t.muted,wrapMode:"none",children:o?"DEVCTL_TUI_CONFIG is set \u2014 changes stay in this session.":"Changes save to your user tui.json unless noted."},void 0,!1,void 0,this)]},void 0,!0,void 0,this),we("box",{flexGrow:1,paddingLeft:c.pad,paddingRight:c.pad,overflow:"hidden",flexShrink:1,children:we(pO,{palette:t,items:r,active:l,scale:c,width:i,onPick:s,onActivate:a},void 0,!1,void 0,this)},void 0,!1,void 0,this),we(mO,{palette:t,item:d,scale:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function pO(e){let{palette:t,items:r,active:n,scale:o,width:i,onPick:s,onActivate:a}=e,c=fi(n,Nk),l=Math.max(8,i-$k-Fk-10),d=0,u=Sb(r);return we("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:we("box",{flexDirection:"column",overflow:"hidden",children:u.map((f,p)=>{let m=d;d+=f.items.length;let h=n>=m&&n<m+f.items.length;return we("box",{flexDirection:"column",flexShrink:0,marginBottom:p<u.length-1?Math.max(1,o.gap):0,overflow:"hidden",children:[we("box",{height:1,overflow:"hidden",children:we("text",{fg:h?t.primary:t.muted,wrapMode:"none",children:f.group},void 0,!1,void 0,this)},void 0,!1,void 0,this),f.group==="MCP"?we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.muted,wrapMode:"none",children:"dedicated page \u2014 also /mcp or /agent"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,f.items.map((v,b)=>{let g=m+b;return we(fO,{index:g,palette:t,item:v,active:g===n,rowH:o.rowH,valueWidth:l,onPick:()=>s(g),onActivate:()=>a(v)},v.id,!1,void 0,this)})]},f.group,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function fO(e){let{palette:t,index:r,item:n,active:o,rowH:i,valueWidth:s,onPick:a,onActivate:c}=e;return we("box",{id:`${Nk}-${r}`,height:i,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:o?t.highlight:void 0,onMouseDown:()=>{if(o){c();return}a()},children:[we("box",{width:2,flexShrink:0,overflow:"hidden",children:we("text",{fg:t.primary,children:o?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{width:Fk,flexShrink:0,overflow:"hidden",children:we("text",{fg:o?t.primary:t.muted,children:uO(n.kind)},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{width:$k,flexShrink:0,overflow:"hidden",children:we("text",{fg:o?t.primary:t.text,wrapMode:"none",children:n.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{flexGrow:1,overflow:"hidden",children:we("text",{fg:hO(t,n,o),wrapMode:"none",children:Re(n.value,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function mO(e){let{palette:t,item:r,scale:n}=e,o=r?.id==="font",i=r?.kind==="page"?`${r.name} \u2192 dedicated page`:r?.name??"";return we("box",{height:dO,flexShrink:0,border:!0,borderStyle:"rounded",borderColor:t.border,title:"selected",titleColor:t.muted,backgroundColor:t.element,paddingLeft:n.pad,paddingRight:n.pad,flexDirection:"column",overflow:"hidden",children:[we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.primary,wrapMode:"none",children:o?`${xb(n.steps)} ${n.label}`:i},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.text,wrapMode:"word",children:r?.detail??""},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.muted,wrapMode:"none",children:r?r.kind==="info"?"read-only":r.hint:""},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function hO(e,t,r){if(t.id==="mouse")return t.value==="on"?e.success:e.text;if(t.kind==="page")return e.info;if(t.kind==="info")return e.text;return r?e.primary:e.text}var $k=14,Fk=2,dO=5,Nk="settings-row";var jk=I(()=>{Mt();Ot();ke();Er()});import{jsxDEV as Ae}from"@opentui/react/jsx-dev-runtime";function Bk(e){let{palette:t,cfg:r,google:n,bootError:o,bootErrorMissing:i=!1,step:s=0}=e,a=ye();if(o&&!r)return i?Ae(ze,{palette:t,title:"No configuration found",body:"Would you like to run setup?",hint:"[Enter] 9-step setup [Esc] Exit"},void 0,!1,void 0,this):Ae(ze,{palette:t,title:"Configuration error",body:o,hint:"Fix .devctl/config.yaml, then restart devctl [Esc] Exit"},void 0,!1,void 0,this);let c=e.issues,l=gO(r,n,c),d=l.filter((g)=>g.ok).length,u=l.length,f=u>0?Math.round(d/u*100):0,p=20,m=Math.round(f/100*p),h="\u2588".repeat(m)+"\u2591".repeat(Math.max(0,p-m)),v=f===100?t.success:f===0?t.muted:t.primary,b=l[s];return Ae(kt,{palette:t,title:"setup",scroll:!0,children:[Ae("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae("text",{fg:t.primary,children:"Developer onboarding"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae("text",{wrapMode:"none",children:[Ae("span",{fg:v,children:`[${h}]`},void 0,!1,void 0,this),Ae("span",{fg:t.text,children:` ${d}/${u} ready`},void 0,!1,void 0,this),Ae("span",{fg:t.muted,children:` (${f}%)`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),l.map((g,_)=>{let R=_===s;return Ae("box",{height:a.rowH,flexDirection:"row",overflow:"hidden",backgroundColor:R?t.highlight:void 0,paddingLeft:1,children:[Ae("box",{width:2,flexShrink:0,children:Ae("text",{fg:et(t,g.ok?"OK":"WARN"),children:hr(g.ok?"OK":"WARN")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:3,flexShrink:0,children:Ae("text",{fg:R?t.primary:t.muted,children:String(_+1).padStart(2," ")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:20,flexShrink:0,overflow:"hidden",children:Ae("text",{fg:R?t.primary:t.text,children:g.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{flexGrow:1,overflow:"hidden",children:Ae("text",{fg:R?t.text:t.muted,wrapMode:"none",children:g.detail},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},g.name,!0,void 0,this)}),Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),b?Ae("box",{border:!0,borderStyle:"rounded",borderColor:b.ok?t.success:t.warning,title:b.name,titleColor:b.ok?t.success:t.warning,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:Ae("text",{fg:b.ok?t.text:t.warning,wrapMode:"word",children:b.detail},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),Ae("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae(Be,{palette:t,label:"never hard-coded",tone:"idle"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("text",{fg:t.muted,wrapMode:"word",children:"Service accounts and IAP audiences stay in configuration. Run `devctl setup` for the interactive CLI wizard."},void 0,!1,void 0,this),Ae(it,{palette:t,hints:[{key:"j/k",label:"steps"},{key:"enter",label:r?"start":"begin setup"},{key:"esc",label:"exit"},{key:"o",label:"profiles"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function gO(e,t,r=[]){let n=Object.values(e?.services??{}).flatMap((s)=>s.ports.filter((a)=>!a.auto)),o=(e?.proxy.routes??[]).some((s)=>s.auth.type.toLowerCase()==="iap"),i=Tl(e,void 0,t).project;return[{name:ln[0],ok:Boolean(e?.repoRoot),detail:e?.repoRoot||"unknown"},{name:ln[1],ok:Boolean(e?.project.name),detail:e?.project.name||"set project.name"},{name:ln[2],ok:Boolean(i),detail:i||"optional for local-only"},{name:ln[3],ok:t?.adcAvailable===!0,detail:t?.adcAvailable?"ADC available":"needed only for cloud identity / IAP"},{name:ln[4],ok:!0,detail:"read from configuration; never hard-coded"},{name:ln[5],ok:!o||(e?.proxy.routes??[]).every((s)=>s.auth.type.toLowerCase()!=="iap"||s.auth.audience!==""),detail:o?"IAP routes present":"no IAP routes"},{name:ln[6],ok:n.length===0||Boolean(e),detail:`${n.length} fixed ports from configuration`},{name:ln[7],ok:Object.keys(e?.profiles??{}).length>0,detail:`${Object.keys(e?.profiles??{}).length} profile(s)`},{name:ln[8],ok:r.length===0,detail:r.length===0?"configuration is valid":r[0]??"invalid"}]}var ln;var Uk=I(()=>{Yt();Mt();ke();tt();ln=["Repository","Environment","Google project","Authentication","Service accounts","IAP","Ports","Profiles","Validation"]});import{useEffect as vO,useState as yO}from"react";import{jsxDEV as ee}from"@opentui/react/jsx-dev-runtime";function ki(e){let{palette:t,title:r,tone:n="muted",children:o}=e,i=n==="success"?t.success:n==="warning"?t.warning:n==="error"?t.error:t.border;return ee("box",{border:!0,borderStyle:"rounded",borderColor:i,title:r,titleColor:i,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,marginBottom:1,overflow:"hidden",children:o},void 0,!1,void 0,this)}function Nf(e,t){if(t.color)return t.color;switch(t.tone){case"success":return e.success;case"warning":return e.warning;case"error":return e.error;case"primary":return e.primary;case"muted":return e.muted;default:return e.text}}function Us(e){let{palette:t,cells:r,header:n,gap:o=0,stripe:i}=e;return ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:i?t.element:void 0,children:r.map((s,a)=>ee("box",{flexDirection:"row",flexShrink:0,overflow:"hidden",children:[a>0&&o>0?ee("box",{width:o,flexShrink:0},void 0,!1,void 0,this):null,ee("box",{width:s.width,flexShrink:0,overflow:"hidden",children:ee("text",{fg:n?t.primary:Nf(t,s),wrapMode:"none",children:oe(s.text,s.width)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${s.text}-${a}`,!0,void 0,this))},void 0,!1,void 0,this)}function Wk(e){let{palette:t,width:r}=e;return ee("box",{height:1,flexShrink:0,overflow:"hidden",children:ee("text",{fg:t.border,wrapMode:"none",children:oe("\u2500".repeat(Math.max(1,r)),r)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Bs(e){let{palette:t,facts:r,width:n}=e,o=Gp(r,n),i=[{text:"what",width:o.what},{text:"reading",width:o.reading}];if(o.meter>0)i.push({text:"used",width:o.meter});return i.push({text:"meaning",width:o.meaning}),ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,header:!0,gap:pi,cells:i},void 0,!1,void 0,this),ee(Wk,{palette:t,width:n},void 0,!1,void 0,this),r.map((s,a)=>{let c=s.meaning===""?[""]:Xc(s.meaning,Math.max(1,o.meaning)),l=s.meter?jx(s.meter):o.meter>0?"":void 0,d=[{text:s.what,width:o.what,tone:"text"},{text:s.reading,width:o.reading,tone:s.tone??"text"}];if(o.meter>0)d.push({text:l??"",width:o.meter,tone:s.meter?s.tone??"text":"muted"});return d.push({text:c[0]??"",width:o.meaning,tone:"muted"}),ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,gap:pi,stripe:a%2===1,cells:d},void 0,!1,void 0,this),c.slice(1).map((u,f)=>{let p=[{text:"",width:o.what},{text:"",width:o.reading}];if(o.meter>0)p.push({text:"",width:o.meter});return p.push({text:u,width:o.meaning,tone:"muted"}),ee(Us,{palette:t,gap:pi,stripe:a%2===1,cells:p},`${s.what}-more-${f}`,!1,void 0,this)})]},`${s.what}-${s.reading}-${a}`,!0,void 0,this)})]},void 0,!0,void 0,this)}function Gk(e){let{palette:t,cfg:r,snap:n,width:o,onRefresh:i}=e,s=ye(),[,a]=yO(0);if(vO(()=>{let B=setInterval(()=>{a((z)=>{let de=z+1;if(i&&de%xO===0)i();return de})},bO);return()=>clearInterval(B)},[i]),!r)return ee(ze,{palette:t,title:"No configuration loaded",body:"Add services to .devctl/config.yaml or finish setup."},void 0,!1,void 0,this);let c=Object.keys(r.services).sort(),l=c.some((B)=>Bx(r.services[B])),d=Ix(c,n),u=zx(d,l),f=l&&d.live>0?d.healthy/d.live:d.total>0?d.live/d.total:0,p=si(f,wO),m=d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",h=Fx(o,s.pad),v=n?.session_id?Jl(n.session_id):void 0,b=v?ks(Date.now()-v.getTime()):"just started",g=n?.logs.total??0,_=n?.logs.errors??0,R=$x(n?.logs.counts??{}),S=n?.proxy.routes??[],y=n?.credentials,x=(y?.entries??[]).filter((B)=>B.valid).length,w=n?.system,k=w?w.memAvailableKB??w.memFreeKB:0,D=w?Wx(w.loadAvg1,w.cpuCount):void 0,O=w?Gx(k,w.memTotalKB):void 0,F=[];if(w&&D&&O){F.push(D,O,{what:"Computer",reading:Ux(w.platform),meaning:`on for ${ks(w.hostUptimeSec*SO)}`,tone:"text"});let B=n?.stats_series;if(B&&B.cpu.length>1)F.push({what:"Load trend",reading:Wp(B.cpu),meaning:`${Math.round(B.interval_ms/1000)}s samples`,tone:"text"});if(B&&B.mem.length>1)F.push({what:"RAM trend",reading:Wp(B.mem),meaning:"used memory over the same window",tone:"text"})}let Z=d.failed>0||D?.tone==="error"||O?.tone==="error"?"error":m==="warning"||D?.tone==="warning"||O?.tone==="warning"?"warning":"success",W=[{what:"This session",reading:b,meaning:"how long this run has been going",tone:"text"},{what:"Profile",reading:n?.profile||"none",meaning:"which group of services is selected",tone:n?.profile?"text":"muted"},{what:"This window",reading:n?.detached?"closed":"watching",meaning:n?.detached?"services keep running without this screen":"you are watching the services from here",tone:n?.detached?"warning":"text"},{what:"Project folder",reading:"",meaning:r.repoRoot,tone:"muted"}],C=[{what:"Lines kept",reading:String(g),meaning:"log lines still in memory",tone:"text"},{what:"Error lines",reading:String(_),meaning:_>0?"open Logs and press e to see only errors":"no ERROR or FATAL lines",tone:_>0?"error":"muted"},...R.map(([B,z])=>({what:B,reading:String(z),meaning:"lines from this service",tone:"text"}))],j=[{what:"Web proxy",reading:n?.proxy.running?"on":"off",meaning:n?.proxy.running?`${S.length} apps${n.proxy.address?` at ${n.proxy.address}`:""}`:"not forwarding local URLs",tone:n?.proxy.running?"success":"muted"},{what:"Agent tools",reading:n?.mcp?.running?"on":"off",meaning:n?.mcp?.address?`Cursor talks to devctl at ${n.mcp.address}`:"turn on from Settings \u2192 MCP",tone:n?.mcp?.running?"success":"muted"},{what:"Google account",reading:n?.identity.user?"signed in":"not signed in",meaning:[n?.identity.user,n?.identity.project].filter(Boolean).join(" \xB7 ")||"no Google user for this session",tone:n?.identity.user?"success":"muted"},{what:"Saved tokens",reading:y?`${x} of ${y.entries.length} good`:"none",meaning:y?Vx(y.backend):"nothing stored yet",tone:y&&x<y.entries.length?"warning":y?"success":"muted"}];return ee(kt,{palette:t,title:"stats",scroll:!0,children:[ee(ki,{palette:t,title:`Your services ${d.live} of ${d.total} started`,tone:Z,children:[ee(Bs,{palette:t,facts:u,width:h},void 0,!1,void 0,this),ee("box",{height:1,flexShrink:0},void 0,!1,void 0,this),ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:ee("text",{wrapMode:"none",children:[ee("span",{fg:t.muted,children:oe(l?"Ready":"Started",Gp(u,h).what)},void 0,!1,void 0,this),ee("span",{children:" ".repeat(pi)},void 0,!1,void 0,this),ee("span",{fg:Nf(t,{text:"",width:1,tone:m}),children:`[${p}]`},void 0,!1,void 0,this),ee("span",{fg:Nf(t,{text:"",width:1,tone:m}),children:l?` ${d.healthy} of ${d.live||d.total} passing checks`:` ${d.live} of ${d.total} up`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),w?ee(ki,{palette:t,title:"This computer",tone:D?.tone==="error"||O?.tone==="error"?"error":D?.tone==="warning"||O?.tone==="warning"?"warning":"success",children:ee(Bs,{palette:t,facts:F,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,ee(ki,{palette:t,title:"This session",children:ee(Bs,{palette:t,facts:W,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Each service",tone:d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",children:d.total===0?ee("text",{fg:t.muted,children:"No services in this project yet."},void 0,!1,void 0,this):ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(kO,{palette:t,names:c,snap:n,width:h},void 0,!1,void 0,this),ee("box",{height:1,flexShrink:0},void 0,!1,void 0,this),ee("text",{fg:t.muted,wrapMode:"none",children:oe("CPU is % of one core. RAM is what that process is holding now.",h)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Log lines",tone:_>0?"warning":"muted",children:ee(Bs,{palette:t,facts:C,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Other parts",children:ee(Bs,{palette:t,facts:j,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function kO(e){let{palette:t,names:r,snap:n,width:o}=e,i=Nx(o),s=[{text:"service",width:i.name},{text:"status",width:Tr}];if(i.health)s.push({text:"check",width:Ur});if(i.cpu)s.push({text:"cpu",width:_s});if(i.mem)s.push({text:"ram",width:Rs});if(i.up)s.push({text:"up",width:Ps});if(i.rst)s.push({text:"restarts",width:ll});if(i.pid)s.push({text:"pid",width:sn});return ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,header:!0,gap:$t,cells:s},void 0,!1,void 0,this),ee(Wk,{palette:t,width:o},void 0,!1,void 0,this),r.map((a,c)=>ee(RO,{palette:t,name:a,rt:n?.services[a],cols:i,stripe:c%2===1},a,!1,void 0,this))]},void 0,!0,void 0,this)}function _O(e){if(e===void 0)return"muted";if(e>PO)return"error";if(e>CO)return"warning";return"text"}function RO(e){let{palette:t,name:r,rt:n,cols:o,stripe:i}=e,s=Kx(n),a=qx(n),c=n?.restarts??0,l=[{text:r,width:o.name,color:Ze(r,t)},{text:s,width:Tr,color:et(t,n?.state??"STOPPED")}];if(o.health)l.push({text:a,width:Ur,color:et(t,n?.health??"UNKNOWN")});if(o.cpu)l.push({text:n?.cpuPercent!==void 0?Pb(n.cpuPercent):"\u2014",width:_s,tone:_O(n?.cpuPercent)});if(o.mem)l.push({text:n?.memoryKB!==void 0?Cs(n.memoryKB):"\u2014",width:Rs,tone:"text"});if(o.up)l.push({text:Ox(n),width:Ps,tone:"text"});if(o.rst)l.push({text:String(c),width:ll,tone:c>0?"warning":"muted"});if(o.pid)l.push({text:n?.pid?String(n.pid):"\u2014",width:sn,tone:"muted"});return ee("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",children:[ee(Us,{palette:t,gap:$t,stripe:i,cells:l},void 0,!1,void 0,this),n?.last_error?ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:ee("text",{fg:t.error,wrapMode:"none",children:oe(` ${n.last_error}`,Math.max(24,o.name+$t+Tr+Ur))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var bO=1000,xO=5,wO=16,SO=1000,CO=80,PO=100;var Kk=I(()=>{Yt();Mt();Ot();gr();Lt();dl();ke();tt()});import{useRenderer as EO,useTerminalDimensions as TO}from"@opentui/react";import{useCallback as jn,useEffect as AO,useMemo as qk,useRef as Vr,useState as Je}from"react";import{jsxDEV as he,Fragment as LO}from"@opentui/react/jsx-dev-runtime";function Vk({controller:e,tui:t,onQuit:r,onDown:n,onAttached:o,bootError:i,bootErrorMissing:s=!1,terminalBackground:a,workspace:c}){let{saveTuiPreferences:l,resolveTuiOverridePath:d,userTuiConfigPath:u,validateConfigText:f,freePort:p,readTextFile:m,writeTextFile:h,createStarterConfig:v,validate:b}=c,[g,_]=Je(e),[R,S]=Je(i),y=EO(),{width:x,height:w}=TO(),[k,D]=Je(g?"dashboard":"setup"),[O,F]=Je("none"),[Z,W]=Je(""),[C,j]=Je("commands"),[B,z]=Je(0),[de,Ee]=Je(0),[Ne,ne]=Je(0),[H,ae]=Je([]),[Q,nt]=Je(),[ft,ie]=Je(R??""),[Le,Me]=Je({}),[Ke,gt]=Je(),[Xe,Ct]=Je(Tb(g?.cfg)),[ge,vt]=Je(""),[at,yt]=Je(!1),[rr,Qe]=Je(),[Xt,qe]=Je(),[mt,br]=Je(),Kt=Vr(null),xr=Vr(null),nr=Vr(null),qt=Vr(null),or=Vr(null),ir=Vr(null),sr=Vr(null),Vt=Vr(""),[ce,K]=Je("quit"),A=Vr(void 0),ve=Vr(0),[J,Dt]=Je(g?.cfg),ct=g?.previousPersisted,[Ce,Po]=Je(void 0),P=wS({tui:t,controller:g,resolveTuiOverridePath:d,terminalBackground:a,userTuiConfigPath:u,snap:Q,setStatus:ie,saveTuiPreferences:l,setPaletteIndex:z,setOverlay:F,setConfirmKind:K,setScreen:D,setSelected:ne,screen:k}),{themeName:ue,setThemeName:Yr,fontSize:Jr,prefsLocked:wr,palette:se,rootBackground:Bn,settingRows:Un,persistPrefs:Dr,persistTheme:dn,activateSetting:Ws}=P,Ci=lS({controller:g,workspace:c,cfg:J,snap:Q,screen:k,configReloadError:Ce,setSnap:nt,setStatus:ie}),{google:Pi,doctor:le,doctorLoading:bt,doctorError:Pt,doctorProgress:Xr,setDoctorTick:Gs}=Ci,_o=t.keybinds.copy??Zr(),Sr=qk(()=>Object.keys(J?.services??{}).sort(),[J]),un=jn(async()=>{if(!g)return;try{let X=await g.status();if(nt(X),X.profile&&Xe==="")Ct(X.profile);return X}catch(X){ie(L(X));return}},[g,Xe]),Hl=hS({controller:g,tui:t,names:Sr,screen:k,refresh:un,setStatus:ie}),{logs:Gf,setLogs:yC,paused:Kf,errorOnly:jl,setErrorOnly:Bl,showSystemLogs:qf,logSearch:Vf,setLogSearch:zf,logSearchFocused:bC,setLogSearchFocused:xC,logFollow:Yf,logSince:wC,logService:Jf,setLogService:Ul,logServiceB:SC,setLogServiceB:kC,splitLogs:Wl,splitFocus:Gl,setSplitFocus:CC,logSliceB:Xf,logWindowB:Qf,logSelectedB:Zf,logPinnedB:PC,pinLogViewB:_C,logServices:RC,logShowTimestamps:EC,logShowMeta:TC,logSource:AC,logRegex:em,logWrap:tm,logPinned:rm,logSelected:nm,logsFullscreen:Kl,dashboardLogCursor:LC,setDashboardLogCursor:DC,logFacets:om,logSources:im,filteredLogs:sm,filteredLogsB:IC,logWindow:Ks,logSlice:qs,pinLogView:ql,applyLogCursorA:MC,applyLogCursorB:OC,jumpToLatestLogs:am}=Hl,Ro=qk(()=>{if(C==="tasks")return Np(Object.keys(J?.tasks??{}),Z,"tasks","run this task");if(C==="services")return Np(Object.keys(J?.services??{}),Z,"services","exec in this service");return Tx(Z,{services:Object.keys(J?.services??{}),tasks:Object.keys(J?.tasks??{})})},[J,Z,C]);AO(()=>{Ee(0)},[Z]);let Vl=ex(k,{doctor:le?.checks.length??0,settings:Un.length,profiles:Object.keys(J?.profiles??{}).length,services:Sr.length,logs:Wl&&Gl===1?Xf.length:qs.length,mcp:mw(),config:Object.keys(J?.tasks??{}).length}),cm=k==="logs"?Wl&&Gl===1?Zf:nm:Ne,Ir=Vl<=0?Math.max(0,cm):Math.max(0,Math.min(cm,Vl-1)),$C=k==="detail"?ge:k==="services"?Sr[Ir]??"":"",{inspectorEnv:lm,inspectorEnvStatus:dm,inspectorEnvError:um,resolveEnvironment:FC}=ES({controller:g,cfg:J,envService:$C}),Eo=jn(()=>{if(F("none"),W(""),j("commands"),xC(!1),A.current)clearTimeout(A.current)},[]),zl=jn((X)=>{ae((xt)=>xt.includes(X)?xt.filter((QC)=>QC!==X):[...xt,X])},[]),pm=yS({tui:t,controller:g,cfg:J,persistPrefs:Dr,snap:Q,refresh:un,setStatus:ie}),{mcpPortDraft:NC,mcpPort:HC,toggleMcpTool:jC,toggleMcp:BC,copyMcpSnippet:UC}=pm;iS({controller:g,cfg:J,paused:Kf,logSince:wC,refresh:un,setLogs:yC,setCfg:Dt,setConfigReloadError:Po,setStatus:ie});let Vs=jn((X)=>{vt(X),D("detail")},[]),fm=jn((X)=>{let xt=X.value!==""?X.value:X.required?"(required, not set)":"(empty)";br({title:X.key,body:xt}),F("scroll-text")},[]),Yl=uS({controller:g,workspace:c,cfg:J,snap:Q,refresh:un,setStatus:ie,setOverlay:F}),{plan:zs,planInitiallyRunning:WC,planBusy:GC,lifecycle:mm}=Yl,Qr=PS({workspace:c,renderer:y,setOverlay:F,setStatus:ie,setScreen:D,onAttached:o,setController:_,setCfg:Dt,setBootError:S}),{configEditRef:KC,configEditText:qC,configEditError:VC,setConfigEditError:zC,openConfigBuffer:hm,saveConfigBuffer:YC}=nS({cfg:J,readTextFile:m,setOverlay:F,setStatus:ie,validateConfigText:f,writeTextFile:h,controller:g,setConfirmKind:K,refresh:un}),gm=jn(async()=>{let X=y.getSelection()?.getSelectedText()??"";if(X.trim()===""){ie("Nothing selected \u2014 drag to highlight text");return}try{await ml(X);let xt=X.split(`
|
|
2233
|
-
`).length;ie(xt===1?"Copied selection":`Copied ${xt} selected lines`)}catch(xt){ie(L(xt))}},[y]),{runCommand:Ys}=Zw({setOverlay:F,setQuery:W,setSlashPicker:j,checked:H,setConfirmKind:K,setStatus:ie,persistTheme:dn,setPaletteIndex:z,themeName:ue,setScreen:D,setSelected:ne,screen:k,renderer:y,cfg:J,setScrollText:br,reveal:at,controller:g,refresh:un,profile:Xe,setReveal:yt,resolveEnvironment:FC,openDetail:Vs,copySelection:gm,lastExportPath:Vt,openConfigBuffer:hm,onDown:n??((X)=>r(X)),workspace:c,logView:Hl,diagnostics:Ci,lifecycleActions:Yl}),vm=jn(()=>{let X=ui(Ro,de);if(C==="tasks"){if(!X){ie(Ro.length===0?"no tasks configured":"pick a task");return}j("commands");let xt=Wr("run");if(xt)Ys(xt,[X.name]);return}if(C==="services"){if(!X){ie(Ro.length===0?"no services configured":"pick a service");return}j("commands"),W(`exec ${X.name} -- `);return}if(!X){ie(Z.trim()===""?"pick a command from the list":`unknown command /${Z}`),Eo();return}Ys(X,Ex(X,Z))},[Eo,Ro,Z,Ys,de,C]),ym=jn((X)=>{dn(X),Eo()},[Eo,dn]);Mw({tui:t,controller:g,cfg:J,snap:Q,ui:{screen:k,overlay:O,onQuit:r,onDown:n??((X)=>r(X)),closeOverlay:Eo,confirmKind:ce,confirmDetail:Le,portTarget:Ke,profile:Xe,listCursor:Ir,names:Sr,listCount:Vl,openDetail:Vs,copySelection:gm,height:w,paletteIndex:B,applyTheme:ym,runCommand:Ys,saveConfigBuffer:YC,filtered:Ro,slashIndex:de,submitSlash:vm,advanceWizard:()=>void Qr.advanceWizard(),bootErrorMissing:s,bootError:R,createStarterConfig:v,startWizard:Qr.startWizard,toggleChecked:zl,checked:H,detailName:ge,refresh:un,openConfigBuffer:hm,setOverlay:F,setConfirmKind:K,setConfirmDetail:Me,setPortTarget:gt,setLogDetail:Qe,logDetail:rr,setLogSearch:zf,setProfile:Ct,setStatus:ie,setPaletteIndex:z,setSlashIndex:Ee,setQuery:W,setSlashPicker:j,setScreen:D,setSelected:ne,setChecked:ae,setConfigEditError:zC,freePort:p},logView:Hl,lifecycleActions:Yl,mcp:pm,preferences:P,diagnostics:Ci,refs:{interruptArmedAt:ve,leaderTimer:A,logDetailsScrollRef:qt,scrollTextScrollRef:ir,routeDetailsScrollRef:or,planScrollRef:sr,helpScrollRef:xr,configScrollRef:Kt,detailScrollRef:nr}});let bm=cx(ce,Xe,Le),JC=new Set(zs?.waves.flat()??[]),XC=zs===void 0||mm==="stop"?"":Object.entries(Q?.services??{}).find(([X,xt])=>JC.has(X)&&xt.state==="FAILED")?.[0]??"";return he(bp.Provider,{value:ws(Jr),children:he("box",{flexDirection:"column",width:x,height:w,backgroundColor:Bn,overflow:"hidden",children:[Kl?null:he(LO,{children:[he(Xx,{palette:se,cfg:J,snap:Q,google:Pi,profile:Xe,reveal:at,width:x},void 0,!1,void 0,this),he(Qx,{palette:se,screen:k,width:x,onSelect:D},void 0,!1,void 0,this),Ce?he("box",{height:1,paddingLeft:1,backgroundColor:se.panel,overflow:"hidden",children:he("text",{fg:se.error,wrapMode:"none",children:`\u26A0 configuration reload failed: ${Ce}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),he("box",{flexGrow:1,overflow:"hidden",children:[k==="dashboard"?he(mk,{palette:se,cfg:J,snap:Q,logs:Gf,names:Sr,selected:Ir,checked:H,width:x,followTick:Yf,logService:Jf,errorOnly:jl,showSystemLogs:qf,onOpen:Vs,onSelectIndex:ne,selectedLog:LC,onToggle:zl,logSources:im,onFilterService:Ul,onToggleErrors:()=>Bl((X)=>!X),onShowErrors:()=>{Ul(""),Bl(!0)},wrapMode:tm,view:qs,follow:!rm,onLeaveLatest:ql,onPickLog:(X)=>{let xt=qs[X];if(!xt)return;DC(X),Qe(xt),ql()},viewStart:Ks.start,viewTotal:sm.length,newer:Ks.newer,onJumpLatest:am,facets:om,leftover:ct,search:Vf,regex:em},void 0,!1,void 0,this):null,k==="services"?he(Mk,{palette:se,cfg:J,names:Sr,snap:Q,selected:Ir,checked:H,width:x,reveal:at,onOpen:Vs,onSelectIndex:ne,onToggle:zl,resolvedEnv:lm,envStatus:dm,envError:um,onSelectEnv:fm},void 0,!1,void 0,this):null,k==="detail"?he(Ik,{palette:se,cfg:J,snap:Q,name:ge,reveal:at,width:x,envScrollRef:nr,resolvedEnv:lm,envStatus:dm,envError:um,onSelectEnv:fm},void 0,!1,void 0,this):null,k==="logs"?he(bk,{palette:se,logs:Gf,names:Sr,logSources:im,service:Jf,errorOnly:jl,showSystemLogs:qf,search:Vf,searchFocused:bC,followTick:Yf,width:x,onSearch:zf,onService:Ul,onToggleErrors:()=>Bl((X)=>!X),source:AC,regex:em,services:RC,showTimestamps:EC,showMeta:TC,view:qs,wrapMode:tm,selected:nm,follow:!rm,newer:Ks.newer,viewStart:Ks.start,viewTotal:sm.length,onSelect:MC,fullscreen:Kl,onLeaveLatest:ql,onJumpLatest:am,facets:om,split:Wl,splitFocus:Gl,serviceB:SC,viewB:Xf,selectedB:Zf,followB:!PC,newerB:Qf.newer,viewStartB:Qf.start,viewTotalB:IC.length,onServiceB:kC,onSelectB:OC,onLeaveLatestB:_C,onFocusPane:CC},void 0,!1,void 0,this):null,k==="auth"?he(ZS,{palette:se,cfg:J,google:Pi,identity:Q?.identity},void 0,!1,void 0,this):null,k==="credentials"?he(sk,{palette:se,credentials:Q?.credentials},void 0,!1,void 0,this):null,k==="proxy"?he(Tk,{palette:se,cfg:J,snap:Q,width:x,onSelectRoute:(X)=>{qe(X),F("route-details")}},void 0,!1,void 0,this):null,k==="mcp"?he(hw,{palette:se,snap:Q,port:HC,portDraft:NC,selected:Ir,onPick:ne,onToggle:()=>{BC()},onCopy:UC,onToggleTool:(X)=>{jC(X)}},void 0,!1,void 0,this):null,k==="doctor"?he(gk,{palette:se,report:le,loading:bt,progress:Xr,error:Pt,selected:Ir,onPick:ne,onReload:()=>{Gs((X)=>X+1),ie("Re-running doctor")}},void 0,!1,void 0,this):null,k==="stats"?he(Gk,{palette:se,cfg:J,snap:Q,width:x,onRefresh:un},void 0,!1,void 0,this):null,k==="config"?he(ok,{palette:se,cfg:J,width:x,selectedTask:Ir,scrollRef:Kt},void 0,!1,void 0,this):null,k==="profiles"?he(Sk,{palette:se,cfg:J,snap:Q,profile:Xe,selected:Ir,onPick:ne},void 0,!1,void 0,this):null,k==="setup"?he(Bk,{issues:J?b(J):["configuration not loaded"],palette:se,cfg:J,google:Pi,bootError:R,bootErrorMissing:s,step:Ir},void 0,!1,void 0,this):null,k==="settings"?he(Hk,{palette:se,items:Un,selected:Ir,locked:wr,width:x,onPick:ne,onActivate:Ws},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),O==="slash"?he(qS,{palette:se,items:Ro,query:Z,selected:de,title:C==="tasks"?"tasks":C==="services"?"services":"commands",onQuery:W,onSubmit:vm},void 0,!1,void 0,this):null,O==="themes"?he(XS,{palette:se,themeName:ue,selected:B%St.length,termW:x,termH:w,onIndex:z,onPreview:Yr,onPick:ym},void 0,!1,void 0,this):null,O==="help"?he(bw,{palette:se,termW:x,termH:w,copyKey:_o,scrollRef:xr},void 0,!1,void 0,this):null,O==="leader"?he(OS,{palette:se,termW:x,termH:w},void 0,!1,void 0,this):null,O==="confirm"?he(DS,{palette:se,title:bm.title,body:bm.body,kind:ce,termW:x,termH:w},void 0,!1,void 0,this):null,O==="setup-wizard"?he(kS,{palette:se,termW:x,termH:w,step:Qr.step,answers:Qr.answers,repo:Qr.repo,draft:Qr.draft,authStatus:Qr.authStatus,onDraft:Qr.setDraft,onSubmit:()=>void Qr.advanceWizard()},void 0,!1,void 0,this):null,O==="log-details"?he(FS,{palette:se,event:rr,termW:x,termH:w,scrollRef:qt},void 0,!1,void 0,this):null,O==="route-details"?he(US,{palette:se,route:Xt,termW:x,termH:w,scrollRef:or},void 0,!1,void 0,this):null,O==="scroll-text"&&mt?he(GS,{palette:se,title:mt.title,body:mt.body,termW:x,termH:w,scrollRef:ir},void 0,!1,void 0,this):null,O==="config-edit"&&J?he(AS,{palette:se,path:J.configPath,initialValue:qC,error:VC,termW:x,termH:w,textareaRef:KC},void 0,!1,void 0,this):null,O==="plan"&&zs?he(jS,{palette:se,plan:zs,snap:Q,busy:GC,failed:XC,kind:mm,termW:x,termH:w,scrollRef:sr,initiallyRunning:WC,onDismiss:Eo},void 0,!1,void 0,this):null,Kl?null:he(Zx,{palette:se,screen:k,overlay:O,status:ft,paused:Kf,errorOnly:jl,width:x,copyKey:_o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var zk=I(()=>{te();Yt();qp();Mt();vr();wo();In();Lt();Ow();eS();oS();sS();dS();pS();gS();bS();SS();_S();TS();LS();IS();ff();$S();NS();BS();WS();KS();bf();VS();QS();ek();ik();ak();hk();vk();xk();$s();kk();Ak();$f();Ok();jk();Uk();Kk();Er();tt();Rr()});var Yk={};rP(Yk,{renderApp:()=>jf,runTui:()=>Hf,runTuiWithController:()=>MO});import{createCliRenderer as DO}from"@opentui/core";import{createRoot as IO}from"@opentui/react";import{jsxDEV as OO}from"@opentui/react/jsx-dev-runtime";async function MO(e,t){let r=e.loadTuiConfig(t.cfg.repoRoot,t.cfg.ui.keymap);await jf(e,t,r)}async function Hf(e,t){let r,n,o=!1;try{r=await e.openTui("",t)}catch(s){n=L(s),o=ba(s,qn)}let i=e.loadTuiConfig(r?.cfg.repoRoot??process.cwd(),r?.cfg.ui.keymap);await jf(e,r,i,n,o)}async function jf(e,t,r=e.loadTuiConfig(t?.cfg.repoRoot??process.cwd(),t?.cfg.ui.keymap),n,o=!1){Js();let i=Sm(),s=await DO({exitOnCtrlC:!1,useMouse:r.mouse,enableMouseMovement:r.mouse,useKittyKeyboard:{}}),a=IO(s),c=null;try{c=(await s.getPalette({timeout:120,size:16})).defaultBackground}catch{}await new Promise((l)=>{let d=(f)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.close({detach:f,shutdownSupervisor:!0}).finally(p)},u=(f)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.shutdown({stopServices:!f}).then(()=>t?.close({detach:!0})).finally(p)};a.render(OO(Vk,{workspace:db(e),controller:t,tui:r,onQuit:d,onDown:u,onAttached:(f)=>{t=f},bootError:n,bootErrorMissing:o,terminalBackground:c},void 0,!1,void 0,this))})}var Bf=I(()=>{ub();te();zk()});if(process.env.METADATA_SERVER_DETECTION===void 0)process.env.METADATA_SERVER_DETECTION="bios-only";if(process.env.GCE_METADATA_TIMEOUT===void 0)process.env.GCE_METADATA_TIMEOUT="0";import{existsSync as ia,readFileSync as ra}from"fs";import{dirname as IP,isAbsolute as Dm,join as oa,resolve as MP}from"path";Xs();Xs();import{spawnSync as dP}from"child_process";import{randomBytes as Zl}from"crypto";import{copyFileSync as uP,existsSync as pn,mkdirSync as pP,readdirSync as fP,readFileSync as ed,renameSync as td,statSync as mP,unlinkSync as Xl,writeFileSync as hP}from"fs";import{homedir as km}from"os";import{basename as gP,dirname as Ql,isAbsolute as vP,join as _t,resolve as yP}from"path";var bP=448,xP=384;function _i(e,t){let r=e.startsWith("~/")?_t(km(),e.slice(2)):e;return vP(r)?r:yP(t,r)}function cr(){let e=process.env.DEVCTL_HOME;if(e&&e!=="")return e;return _t(km(),".devctl")}function Mr(e){pP(e,{recursive:!0,mode:bP})}function To(e){let t=_t(cr(),"state",ar(e)),r=_t(cr(),"sessions",ar(e));if(!pn(t)&&pn(r)){Mr(Ql(t));try{td(r,t)}catch{Mr(t);for(let n of["state.json","devctl.lock","devctl.sock"]){let o=_t(r,n);if(pn(o))uP(o,_t(t,n))}}}return Mr(t),t}function Qs(){let e=_t(cr(),"logs");return Mr(e),e}function Zs(){let e=_t(cr(),"exports");return Mr(e),e}function kr(){let e=_t(cr(),"credentials");return Mr(e),e}function ea(e,t=process.platform){if(t==="win32")return`\\\\.\\pipe\\devctl-${ar(e)}`;return _t(To(e),"devctl.sock")}function wP(e){return _t(To(e),"devctl.lock")}function Cm(e){return _t(To(e),"state.json")}function Ri(e){return _t(To(e),"bootstrap.log")}var SP=5,Pm="bootstrap-";function _m(e){let t=Ri(e);if(!pn(t))return;let r=To(e),n=_t(r,`${Pm}${rd()}.log`);try{td(t,n)}catch{return}kP(r)}function kP(e){let t;try{t=fP(e).filter((n)=>n.startsWith(Pm)&&n.endsWith(".log"))}catch{return}let r=t.map((n)=>{let o=_t(e,n);try{return{path:o,mtime:mP(o).mtimeMs}}catch{return}}).filter((n)=>n!==void 0).sort((n,o)=>o.mtime-n.mtime);for(let n=SP;n<r.length;n++)try{Xl(r[n].path)}catch{}}function Cr(e,t){Mr(Ql(e));let r=_t(Ql(e),`.${gP(e)}.tmp-${process.pid}-${Zl(4).toString("hex")}`);hP(r,t,{mode:xP}),td(r,e)}function rd(e=new Date){return`${e.toISOString().slice(0,19).replace(/:/g,"-")+"Z"}-${Zl(3).toString("hex")}`}function fn(e){return nd(Cm(e))}function nd(e){if(!pn(e))return;try{let t=JSON.parse(ed(e,"utf8"));if(Array.isArray(t.processes)&&typeof t.repo_root==="string")return t;return}catch{return}}function Rm(e,t){Cr(Cm(e),`${JSON.stringify(t,null,2)}
|
|
2234
|
-
`)}function Em(e,t){let r=wP(e);if(
|
|
2235
|
-
`),t}function FP(e){let t=aa(e);if(t)return t;let r=[e||process.cwd(),oa(e||process.cwd(),".devctl"),cr()],n=["tui.json","tui.jsonc"];for(let o of r)for(let i of n){let s=oa(o,i);if(ia(s))return s}return}function cd(e,t,r){let n=Wn(t)?t:{},o={...e.keybinds};if(Wn(n.keybinds)){for(let[c,l]of Object.entries(n.keybinds))if(typeof l==="string")o[c]=l}let i={...e.cursor};if(Wn(n.cursor)){if(HP(n.cursor.style))i.style=n.cursor.style;if(typeof n.cursor.blinking==="boolean")i.blinking=n.cursor.blinking}let s={...e.scroll_acceleration};if(Wn(n.scroll_acceleration)&&typeof n.scroll_acceleration.enabled==="boolean")s.enabled=n.scroll_acceleration.enabled;let a={...e.attention,sounds:{...e.attention.sounds}};if(Wn(n.attention)){if(typeof n.attention.enabled==="boolean")a.enabled=n.attention.enabled;if(typeof n.attention.notifications==="boolean")a.notifications=n.attention.notifications;if(typeof n.attention.sound==="boolean")a.sound=n.attention.sound;if(typeof n.attention.volume==="number")a.volume=n.attention.volume;if(typeof n.attention.sound_pack==="string")a.sound_pack=n.attention.sound_pack;if(Wn(n.attention.sounds)){for(let[c,l]of Object.entries(n.attention.sounds))if(typeof l==="string")a.sounds[c]=NP(l,r)}}return{theme:typeof n.theme==="string"?n.theme:e.theme,leader_timeout:typeof n.leader_timeout==="number"?n.leader_timeout:e.leader_timeout,font_size:typeof n.font_size==="number"&&Number.isFinite(n.font_size)?Math.round(n.font_size):e.font_size,keybinds:o,scroll_speed:typeof n.scroll_speed==="number"?n.scroll_speed:e.scroll_speed,scroll_acceleration:s,diff_style:n.diff_style==="stacked"?"stacked":"auto",cursor:i,mouse:typeof n.mouse==="boolean"?n.mouse:e.mouse,attention:a,log_timestamps:typeof n.log_timestamps==="boolean"?n.log_timestamps:e.log_timestamps,log_metadata:typeof n.log_metadata==="boolean"?n.log_metadata:e.log_metadata,mcp_enabled:typeof n.mcp_enabled==="boolean"?n.mcp_enabled:e.mcp_enabled,mcp_port:typeof n.mcp_port==="number"&&Number.isInteger(n.mcp_port)?n.mcp_port:e.mcp_port,mcp_disabled_tools:Array.isArray(n.mcp_disabled_tools)?n.mcp_disabled_tools.filter((c)=>typeof c==="string"):e.mcp_disabled_tools,path:r}}function NP(e,t){if(e.startsWith("file://")||Dm(e))return e;return oa(IP(t),e)}function HP(e){return e==="block"||e==="underline"||e==="line"||e==="default"}function Wn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function na(e){let t=e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");return JSON.parse(t)}import{existsSync as
|
|
2236
|
-
`,l=S_(a.service),d=this.streamFor(l);this.lastWrite.set(l,new Promise((u)=>{d.write(c,()=>u())}))}return a}async flush(){await Promise.all([...this.lastWrite.values()])}parseLine(e){let t={};for(let r of this.parsers)try{let n=r.parse(e);if(n)t={...t,...n}}catch{}return t}streamFor(e){let t=this.streams.get(e);if(t===void 0)t=m_(No(this.persistDir,`${e}.log`),{flags:"a",mode:384}),t.on("error",()=>{}),this.streams.set(e,t);return t}async close(){await this.flush(),await Promise.all([...this.streams.values()].map((e)=>new Promise((t)=>{e.end(()=>t())}))),this.streams.clear(),this.lastWrite.clear()}query(e){let t=wd(e),r=[];return this.forEachEvent((n)=>{if(t(n))r.push(n)}),r}queryPage(e,t={}){let r=Zm(t.limit),n=t.cursor?x_(t.cursor):void 0,o=n!==void 0&&n.session!==this.sessionID,i=o?void 0:n,s=i?t.direction??"backward":"backward",a=wd(e),c=[];this.forEachEvent((m)=>{if(a(m))c.push(m)});let l;if(!i)l=c.slice(Math.max(0,c.length-r));else if(s==="forward")l=c.filter((m)=>m.seq>i.seq).slice(0,r);else{let m=c.filter((h)=>h.seq<i.seq);l=m.slice(Math.max(0,m.length-r))}let d=l[0]?.seq,u=l[l.length-1]?.seq,f=d!==void 0&&c.some((m)=>m.seq<d),p=u!==void 0&&c.some((m)=>m.seq>u);return{events:l,prevCursor:th({session:this.sessionID,seq:d??i?.seq??0}),nextCursor:th({session:this.sessionID,seq:u??i?.seq??this.nextSeq-1}),hasNext:p,hasPrev:f,sessionChanged:o}}queryFacets(e){let t=kd(e,"services"),r=kd(e,"level"),n=kd(e,"source"),o=xd(e),i=0,s={},a={},c={};return this.forEachEvent((l)=>{if(!o(l))return;if(Fo(e,l))i+=1;if(Fo(t,l))s[l.service]=(s[l.service]??0)+1;if(Fo(r,l))a[l.level]=(a[l.level]??0)+1;if(Fo(n,l))c[l.source]=(c[l.source]??0)+1}),{total:i,byService:s,byLevel:a,bySource:c}}snapshot(){let e={},t=0;return this.forEachEvent((r)=>{if(e[r.service]=(e[r.service]??0)+1,r.level===Kn||r.level===Oo)t+=1}),{total:this.events.length,errors:t,counts:e}}forEachEvent(e){let t=this.events.length;for(let r=0;r<t;r+=1){let n=this.events[(this.eventStart+r)%t];if(n)e(n)}}exportTo(e,t){Ed(e,this.query(t))}}function nh(e){return{append:(t)=>{e.append(t)},query:async(t)=>e.query(t),queryPage:async(t,r)=>e.queryPage(t,r),queryFacets:async(t)=>e.queryFacets(t),snapshot:()=>e.snapshot(),exportTo:async(t,r)=>{e.exportTo(t,r)},setParsers:(t)=>{e.setParsers(t)},setSecrets:(t,r)=>{},close:()=>e.close()}}function w_(e=new Date){let t=e.toISOString().replace(/[:.]/g,"-");return No(Zs(),`devctl-logs-${t}.log`)}function oh(e=""){if(e==="")return w_();return _i(e,process.cwd())}function Ed(e,t){
|
|
2229
|
+
Enter is preview only. /import compose ${Yr} --write to save.`}),e("scroll-text")}catch(se){i(L(se))}return}case"diff":if(!p){i("no configuration loaded");return}m({title:"config sources",body:Yw(p,h)}),e("scroll-text");return;case"daemon":{let P=p?.repoRoot;if(!P){i("no repository");return}let ue=B(P);if(!z(ue)){i("no daemon bootstrap log yet for this repository");return}m({title:"daemon bootstrap",body:de(ue)}),e("scroll-text");return}case"reload":if(!v)return;v.reload().then((P)=>{if(i(P.restart_required.length===0?"Configuration reloaded":`Reload requires restart: ${P.restart_required.join(", ")}`),P.restart_required.length>0)o("reload"),e("confirm");b()});return;case"doctor":l("doctor");return;case"stats":l("stats");return;case"start":await K(Po,g);return;case"stop":await A(Po);return;case"restart":{let P=vx(Ce),ue=Rp(P.services,n);await ve(ue,g,P.cascade);return}case"run":{let P=Ce[0]??"";if(!P){r("tasks"),t(""),e("slash"),i(Object.keys(p?.tasks??{}).length===0?"no tasks configured":"pick a task");return}if(!v){i("no daemon attached");return}try{let ue=await v.runTask(P);Ke(`task:${P}`),l("logs"),i(`task ${P} exited ${ue.code}`)}catch(ue){throw Ke(`task:${P}`),l("logs"),ue}return}case"exec":{let P=yx(Ce);if(!P.service){r("services"),t(""),e("slash"),i(Object.keys(p?.services??{}).length===0?"no services configured":"pick a service");return}if(!v){i("no daemon attached");return}if(P.printEnv){if(!p?.services[P.service]){i(`unknown service ${P.service}`);return}if(P.reveal)R(!0);i(`Resolving environment for ${P.service}\u2026`);try{await E(P.service),S(P.service),i(`Resolved environment for ${P.service}`)}catch(ue){S(P.service),i(L(ue))}return}if(P.command.length===0){i("usage: /exec <service> -- <command\u2026>");return}try{let ue=await v.execService(P.service,P.command);Ke(`${P.service}:exec`),l("logs"),i(`${P.service}:exec exited ${ue.code}`)}catch(ue){throw Ke(`${P.service}:exec`),l("logs"),ue}return}case"refresh":if(u==="auth"){await Vt();return}await b(),i("Refreshed status and logs");return;case"pause":Xe((P)=>!P),l("logs");return;case"filter":Ct((P)=>!P),l("logs");return;case"system":ge(),l("logs");return;case"clear":vt();return;case"reveal":{let P=!h;R(P),i(P?"Secrets visible this session":"Secrets hidden");return}case"wrap":{let P=Qc(at);yt(P),i(`Log ${el(P)}`);return}case"copy":await y();return;case"export":{let P=ne(Ce[0]);if(v)await v.logs({export:P,services:rr,level:Qe?"ERROR":Xt,search:qe,regex:mt,source:br});else H(P,Kt);x.current=P,i(`Exported ${P}`);return}case"exports":{let P=x.current||ae();Q(P),i(`Opened ${ae()}`);return}case"regex":xr((P)=>!P),i(mt?"Substring search":"Regex search");return;case"since":nr(Ce[0]||new Date(Date.now()-3600000).toISOString()),i(`Logs since ${Ce[0]||"1h"}`);return;case"until":qt(Ce[0]||new Date().toISOString()),i(`Logs until ${Ce[0]||"now"}`);return;case"history":{let P=nt(),ue=Ce[0]||P[0];if(!ue){i("No persisted log sessions");return}or(ft(ue)),i(`Loaded session ${ue}`);return}case"buffer":w();return;case"edit":{if(!p)return;let P=process.env.DEVCTL_EDITOR||process.env.EDITOR,ue=P?[P,p.configPath]:process.platform==="darwin"?["open",p.configPath]:["xdg-open",p.configPath];Bun.spawn({cmd:ue,stdout:"ignore",stderr:"ignore",stdin:"ignore"}),i(`Opened ${p.configPath} in ${ue[0]}. Run /reload after saving.`);return}default:i(`/${ct.name}`)}}catch(P){i(L(P))}finally{setTimeout(()=>{J.current=!1},cM)}},[ve,K,A,n,p,vt,v,y,Qe,Kt,Xt,mt,qe,rr,br,at,k,w,S,s,g,b,Vt,f,h,u,ir,r,c,sr,ge])}}var cM=50;var eS=I(()=>{te();hf();gf();gr();Lt();tt()});import{useCallback as tS,useRef as lM,useState as rS}from"react";function nS({cfg:e,readTextFile:t,setOverlay:r,setStatus:n,validateConfigText:o,writeTextFile:i,controller:s,setConfirmKind:a,refresh:c}){let l=lM(null),[d,u]=rS(""),[f,p]=rS(""),m=tS(()=>{if(!e)return;try{u(t(e.configPath)),p(""),r("config-edit")}catch(v){n(L(v))}},[e]),h=tS(()=>{if(!e)return;let v=l.current?.plainText??d,b=o(e.repoRoot,e.configPath,v);if(b.length>0){p(b.join(`
|
|
2230
|
+
`)),n("Config buffer not saved");return}if(i(e.configPath,v),p(""),r("none"),!s){n(`Wrote ${e.configPath}`);return}s.reload().then((g)=>{if(n(g.restart_required.length===0?"Configuration saved and reloaded":`Saved; restart required: ${g.restart_required.join(", ")}`),g.restart_required.length>0)a("reload"),r("confirm");c()})},[e,d,s,c]);return{configEditRef:l,configEditText:d,configEditError:f,setConfigEditError:p,openConfigBuffer:m,saveConfigBuffer:h}}var oS=I(()=>{te()});import{useEffect as dM}from"react";function iS({controller:e,cfg:t,paused:r,logSince:n,refresh:o,setLogs:i,setCfg:s,setConfigReloadError:a,setStatus:c}){return dM(()=>{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 uM,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,E]=yi(0),[S,y]=yi({active:"Preparing diagnostics",checks:[]}),x=cS(void 0),w=cS(0),k=uM(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===R&&D.sessionID===O&&D.reloadError===i)return;x.current={cfg:r,tick:R,sessionID:O,reloadError:i};let F=++w.current;v(!0),g(""),y({active:"Preparing diagnostics",checks:[]});let Z;if(r.configPath!==""){Z="";try{d(r.repoRoot,r.configPath)}catch(W){Z=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,R,n?.session_id,i]),{google:u,setGoogle:f,doctor:p,doctorLoading:h,doctorError:b,doctorProgress:S,setDoctorTick:E,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"),R=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]),E=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:R,beginStop:E,beginRestart:S}}var pS=I(()=>{te();xl();Lt()});import{useCallback as Dr,useEffect as Kr,useMemo as Fn,useRef as pM,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,R]=be(0),[E,S]=be(""),[y,x]=be(""),[w,k]=be(""),[D,O]=be(""),[F,Z]=be(!1),[W,C]=be(0),[j,B]=be(Lr-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(Lr-1),[rr,Qe]=be(!1),[Xt,qe]=be(-1),[mt,br]=be(),[Kt,xr]=be(""),[nr,qt]=be(!1),[or,ir]=be(!1),sr=pM([]),[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:E,until:y,systemLogs:f}),[d,Ke,m,w,ne,E,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:E,until:y,systemLogs:f}),[d,Ke,m,D,ne,E,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(Lr,A.length)-1))},[d,m,w,f]),Kr(()=>{de(!1),B(Math.max(0,Math.min(Lr,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(Lr,A.length)-1))},[A.length,ge,n]),Kr(()=>{if(n!=="logs"||z)return;B(Math.max(0,Math.min(Lr,Dt.length)-1))},[Dt.length,z,n]);let Po=Dr(()=>{if(!ge)ce(Zc(A.length)),vt(!0)},[A.length,ge]),P=Dr(()=>{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=Dr((le)=>mS(le,ue,ce,vt,yt),[ue]),wr=Dr((le)=>mS(le,Yr,Ne,de,B),[Yr]),se=Dr((le)=>{if(F&&W===1){wr(le);return}Jr(le)},[Jr,wr,W,F]),Bn=Dr((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=Dr(()=>{if(vt(!1),de(!1),qe(-1),n==="logs")yt(Math.max(0,Math.min(Lr,A.length)-1)),B(Math.max(0,Math.min(Lr,Dt.length)-1));R((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]),Ir=Fn(()=>({services:F?fS:w!==""?[w]:ne,level:d?"ERROR":Le,search:m,regex:Ke,source:Me,since:E,until:y}),[d,Le,Ke,m,w,ne,E,Me,y,F]),un=Dr(async()=>{if(!e)return;try{br(await e.logsStats(Ir))}catch(le){i(L(le))}},[e,Ir]),Ws=Dr(async()=>{if(!e||c)return;try{let le=await e.logsPage(Ir);a((bt)=>Kb(bt,le.events)),xr(le.prevCursor),qt(le.hasPrev),await un()}catch(le){i(L(le))}},[e,Ir,c,un]),Ci=Dr(()=>{a([]),S(new Date().toISOString()),vt(!1),de(!1),i("Cleared on-screen log buffer")},[]),Pi=Dr(()=>{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({...Ir,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,Ir,or,nr,ge,z,Kt,ve.start,ct.start,F]),Kr(()=>{if(!e||c||n!=="logs")return;let le=setInterval(()=>{un()},fM);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:E,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,fM=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 R=Rn(g);d(R);let E=r?.repoRoot??process.cwd();if(Dy(E,R)){n({mcp_port:null},`MCP port ${R} (default)`);return}n({mcp_port:R},`MCP port ${R}`)},[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 R=o?.mcp?.disabled_tools??[],E=An(g.name,R),S=E?[...R,g.name]:R.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=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((R)=>{s(L(R))})},[]),b=ko((g)=>{let R=nf(g);if(R===void 0)return!1;let E=o?.mcp?.address??mi(o?.mcp?.port??l),S=Ms(E,o?.mcp?.token??"")[R];if(!S)return!1;return v(S),!0},[v,l,o?.mcp?.address,o?.mcp?.port,o?.mcp?.token]);return{mcpPortDraft:a,setMcpPortDraft:c,mcpPort:l,persistMcpPort:u,applyMcpPortDraft:f,restartMcpOnPort:p,toggleMcpTool:m,toggleMcp:h,copyMcpSnippet:v,copyFocusedMcpSnippet:b}}var bS=I(()=>{te();yl();fs();qp();$s()});import{RGBA as mM}from"@opentui/core";import{useCallback as ln,useEffect as hM,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),R=Pl(e.mouse),[E,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)?mM.defaultBackground(n??void 0):O.background,Z=xS(()=>wb({themeName:m,fontSize:x,mouse:b,leaderMs:E,locked:D,configPath:D?e.path||yp(r(),o()):yp(r(),o()),mcpRunning:i?.mcp?.running===!0}),[x,E,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),R.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),R.current=H.mouse,S(H.leader_timeout),y.current=H.leader_timeout,w(H.font_size),k.current=H.font_size,W(H,"restored default preferences")},[W]),Ee=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(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"),f(0);return}s(H.detail)},[z,B,x,E,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(E,H));return}if(Q.id==="font"){z(xs(x,H));return}if(Q.id==="mouse")j()},[z,B,x,E,C,Z,m,j]),ne=ln(()=>{h(v.current)},[]);return hM(()=>{if(p==="settings")return;h(v.current),w(k.current),S(y.current),g(R.current)},[p]),{themeName:m,setThemeName:h,leaderMs:E,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(()=>{Tr();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=gM(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 gM(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]),R=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]),E=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 R(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,R,t,l,d,e,n]);return{step:d,answers:f,draft:m,authStatus:v,repo:l,setDraft:h,startWizard:g,advanceWizard:E}}var _S=I(()=>{La();Ma();te();bf()});import{useCallback as vM,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=vM(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();Er()});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 yM}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(yM,{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 bM(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=bM(o,i,v,t),R=Tw(s,o,i,r.profile),E=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:R,bottomTitle:"enter close \xB7 esc hide (operation continues)",termW:a,termH:c,preferW:98,preferH:28,borderColor:g.color,gap:0,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[U(Be,{palette:t,label:`${g.icon} ${g.label.toUpperCase()}`,tone:g.tone},void 0,!1,void 0,this),U("box",{paddingLeft:1,flexGrow:1,overflow:"hidden",children:U("text",{fg:t.text,wrapMode:"none",children:W},void 0,!1,void 0,this)},void 0,!1,void 0,this),U(Be,{palette:t,label:`${m.ready}/${m.total} READY`,tone:m.isComplete?"success":"info"},void 0,!1,void 0,this),U(Be,{palette:t,label:`WAVE ${Math.min(m.currentWaveIndex+1,Math.max(1,m.totalWaves))}/${Math.max(1,m.totalWaves)}`,tone: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.":E.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:E.primary},void 0,!1,void 0,this),U("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 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+xM,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,xM=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();Tr();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=wM(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 wM(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(kM,{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(SM,{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(CM,{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:RM(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 SM(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(PM(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 kM(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(_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 CM(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 PM(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 RM(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 EM}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 EM(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(""),R=n===void 0?r:r.slice(-n),E=[...new Set(r.map((k)=>k.service))],S=Ub(o,E),y=Bb({width:o,serviceWidth:S,showTimestamps:d,showMeta:u}),x=R[R.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>=R.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,R.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-AM){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()},TM);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:R.map((k,D)=>Ye(LM,{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 LM(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(DM,{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 DM(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 TM=200,AM=2;var pk=I(()=>{Ot();gr();tt()});var Rf=I(()=>{ck();lk();dk();pk()});import{useRef as IM}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=IM(!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:Ar,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 R=n?.services[b],E=tr(R),S=R?.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:MM(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:hr(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:Ar,fg:et(t,E),text:E},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(R)},void 0,!1,void 0,this):null,p?Ie(Gt,{width:an,fg:t.text,text:R?.pid?String(R.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 MM(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:R,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: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:E}]:[]];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(NM,{palette:t,names:i,snap:n,width:Q,onOpen:h},void 0,!1,void 0,this),ae?fe(jM,{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:R,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 $M(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 FM(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 NM(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)=>$M(d.rt)).sort((d,u)=>fk(d.rt)-fk(u.rt));if(s.length===0)return null;let a=s.slice(0,OM),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(FM(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 jM(e){let{palette:t,leftover:r,width:n}=e,o=r.processes.slice(0,HM),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 OM=4,Tf=14,HM=4,Af=14;var hk=I(()=>{At();Yt();Mt();vr();Ot();gr();Lt();dl();ke();tt();Rf();Ef()});import BM from"cli-spinners";import{useEffect as UM,useState as WM}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(GM,{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(KM,{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 GM({palette:e,progress:t}){let[r,n]=WM(0);return UM(()=>{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 KM(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=BM.bouncingBall});import{jsxDEV as We,Fragment as qM}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:R=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: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,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:R>0?`${O?"left ":""}pinned \xB7 +${R} new`:`${O?"left ":""}pinned`,tone:"warning"}],...O&&!j?[{text:B>0?`right pinned \xB7 +${B} new`:"right pinned",tone:"warning"}]:[],...e.source?[{text:`src ${e.source}`,tone:"info"}]:[],...e.regex?[{text:"regex",tone:"accent"}]:[],...c===""?[]:[{text:`search ${c}`,tone:"accent"}],...O?[{text:F===0?"left pane":"right pane",tone:"accent"}]:[]];if(O){let ge=Math.max(20,Math.floor(u/2)),vt=Math.max(20,u-ge),at=W??[],yt=de??at.length;return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):We(ut,{palette:t,items:Ct},void 0,!1,void 0,this),We("box",{flexGrow:1,flexDirection:"row",overflow:"hidden",children:[We(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:R,viewStart:E,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(R,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(qM,{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: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(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:E,search:c,regex:e.regex===!0},d,!1,void 0,this)},void 0,!1,void 0,this),!g?We(xi,{palette:t,width:u,newer:R,onJump: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();Er()});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,R)=>{let E=tr(n?.services[g]);return Ge("span",{children:[Ge("span",{fg:et(t,E),children:hr(E)},void 0,!1,void 0,this),Ge("span",{fg:Ze(g,t),children:` ${g}`},void 0,!1,void 0,this),R<m.length-1?Ge("span",{fg:t.muted,children:" "},void 0,!1,void 0,this):null]},g,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ge(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:Ge(it,{palette:t,hints:[{key:"space",label:"set current"},{key:"enter",label:"set and start"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var 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 VM(e){let t=e.toLowerCase();if(t==="iap")return"info";if(t==="service"||t==="service_account")return"primary";return"idle"}function zM(e,t,r){if(r||t>=500)return e.error;if(t===0||t>=400)return e.warning;return e.success}function YM(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 ZM(e){let{palette:t,req:r,cols:n}=e,o=zM(t,r.status,r.error),i=r.status>0?String(r.status):"ERR",s=YM(t,r.identity),a=r.route||Ek,c=r.error?`${r.path} \u2014 ${Re(r.error,QM)}`: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 eO(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 tO(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:VM(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(rO,Math.floor(o*0.36))),m=f?o-Pk:o-p-Pk,h=Math.max(20,m-2),v=h>=_k,b=h>=nO,g=h>=_k?oO:iO,R=l.reduce((S,y)=>Math.min(g,Math.max(S,(y.route||Ek).length+XM)),JM),E={showMethod:v,showDur:b,routeCol:R};return V(kt,{palette:t,title:"proxy",children:[V(ut,{palette:t,items:[{text:n?.proxy.running?"RUNNING":"STOPPED",tone:n?.proxy.running?"success":"idle"},...n?.proxy.address?[{text:n.proxy.address,tone:"info"}]:[],{text:`${s.length} routes`,tone:s.length>0?"primary":"idle"},...d>0?[{text:`${d} requests`,tone:"idle"},...u>0?[{text:`${u} errors`,tone:"warning"}]:[]]:[]]},void 0,!1,void 0,this),V("box",{flexGrow:1,flexDirection:f?"column":"row",overflow:"hidden",children:[V("box",{flexGrow:f?1:0,flexBasis:f?0:void 0,flexShrink:0,minWidth:f?void 0: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(tO,{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(eO,{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(ZM,{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 If=9,Ol=8,$l=4,Fl=8,Nl=4,JM=8,XM=2,QM=64,Ek="(none)",Ck=26,rO=56,Pk=2,_k=46,nO=54,oO=20,iO=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>=aO,R=[{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"}]:[]],E=[{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,sO)},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(cO,{palette:t,left:R,right:E,wide:g,colWidth:y},void 0,!1,void 0,this),pe(lO,{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 cO(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 lO(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 sO=72,aO=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),R=4,E=v?Math.max(Ff,a-R):Math.max(Ff,a-g-R);return Hn("box",{flexGrow:1,flexDirection:v?"column":"row",overflow:"hidden",children:[Hn("box",{flexGrow:v?1:0,flexShrink:0,minWidth:v?void 0: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?dO: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:E,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,dO=12;var Ok=I(()=>{Yt();vr();Lt();tt();$f();Ef()});import{jsxDEV as we}from"@opentui/react/jsx-dev-runtime";function pO(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(fO,{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(hO,{palette:t,item:d,scale:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function fO(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(mO,{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 mO(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:pO(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:gO(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 hO(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:uO,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 gO(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,uO=5,Nk="settings-row";var jk=I(()=>{Mt();Ot();ke();Tr()});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=vO(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,R)=>{let E=R===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: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:E?t.primary:t.muted,children:String(R+1).padStart(2," ")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:20,flexShrink:0,overflow:"hidden",children:Ae("text",{fg: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 vO(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 yO,useState as bO}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]=bO(0);if(yO(()=>{let B=setInterval(()=>{a((z)=>{let de=z+1;if(i&&de%wO===0)i();return de})},xO);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,SO),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,R=n?.logs.errors??0,E=$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*kO)}`,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(R),meaning:R>0?"open Logs and press e to see only errors":"no ERROR or FATAL lines",tone:R>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?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(CO,{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:R>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 CO(e){let{palette:t,names:r,snap:n,width:o}=e,i=Nx(o),s=[{text:"service",width:i.name},{text:"status",width:Ar}];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(EO,{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 RO(e){if(e===void 0)return"muted";if(e>_O)return"error";if(e>PO)return"warning";return"text"}function EO(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:Ar,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:RO(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+Ar+Ur))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var xO=1000,wO=5,SO=16,kO=1000,PO=80,_O=100;var Kk=I(()=>{Yt();Mt();Ot();gr();Lt();dl();ke();tt()});import{useRenderer as TO,useTerminalDimensions as AO}from"@opentui/react";import{useCallback as jn,useEffect as LO,useMemo as qk,useRef as Vr,useState as Je}from"react";import{jsxDEV as he,Fragment as DO}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,R]=Je(e),[E,S]=Je(i),y=TO(),{width:x,height:w}=AO(),[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(E??""),[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:Ir,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]);LO(()=>{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,Mr=Vl<=0?Math.max(0,cm):Math.max(0,Math.min(cm,Vl-1)),$C=k==="detail"?ge:k==="services"?Sr[Mr]??"":"",{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:Ir,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:R,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:Mr,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:E,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(DO,{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:Mr,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:Mr,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:Mr,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:Mr,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:Mr,scrollRef:Kt},void 0,!1,void 0,this):null,k==="profiles"?he(Sk,{palette:se,cfg:J,snap:Q,profile:Xe,selected:Mr,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:E,bootErrorMissing:s,step:Mr},void 0,!1,void 0,this):null,k==="settings"?he(Hk,{palette:se,items:Un,selected:Mr,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();Tr();tt();Er()});var Yk={};rP(Yk,{renderApp:()=>jf,runTui:()=>Hf,runTuiWithController:()=>OO});import{createCliRenderer as IO}from"@opentui/core";import{createRoot as MO}from"@opentui/react";import{jsxDEV as $O}from"@opentui/react/jsx-dev-runtime";async function OO(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 IO({exitOnCtrlC:!1,useMouse:r.mouse,enableMouseMovement:r.mouse,useKittyKeyboard:{}}),a=MO(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($O(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 Or(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)){Or(Ql(t));try{td(r,t)}catch{Or(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 Or(t),t}function Qs(){let e=_t(cr(),"logs");return Or(e),e}function Zs(){let e=_t(cr(),"exports");return Or(e),e}function kr(){let e=_t(cr(),"credentials");return Or(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){Or(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 Em(e,t){let r=wP(e);if(fn(r)){let n=CP(r);if(n&&Qt(n.pid))throw Error(`supervisor already running (pid ${n.pid})`);try{Xl(r)}catch{}}return Cr(r,JSON.stringify({pid:process.pid,socket:t})),{release:()=>{try{Xl(r)}catch{}}}}function CP(e){if(!fn(e))return;try{let t=JSON.parse(ed(e,"utf8"));if(typeof t.pid==="number"&&typeof t.socket==="string")return{pid:t.pid,socket:t.socket}}catch{return}return}function Qt(e){if(e<=0)return!1;if(process.platform==="win32")return PP(e);try{return process.kill(e,0),!0}catch{return!1}}function od(e){try{let t=dP("cmd.exe",["/d","/c",`tasklist /FO CSV /NH /FI "PID eq ${e}"`],{encoding:"buffer",windowsHide:!0,timeout:5000});return _P(t.stdout)}catch{return""}}function PP(e){try{return process.kill(e,0),!0}catch{}let t=od(e).toLowerCase();if(t===""||t.includes("no tasks")||t.includes("no matching"))return!1;return t.includes(String(e))}function _P(e){if(!e)return"";if(typeof e==="string")return e;if(e.length>=2&&e[0]===255&&e[1]===254)return e.toString("utf16le");if(e.length>=4&&e[1]===0&&e[3]===0)return e.toString("utf16le");return e.toString("utf8")}function id(){return Zl(24).toString("hex")}function RP(e){return _t(To(e),"mcp-token")}function Tm(e){let t=RP(e);if(fn(t)){let n=ed(t,"utf8").trim();if(n!=="")return n}let r=id();return Cr(t,r),r}ta();ta();var OP="OPENCODE_TUI_CONFIG";function sa(){return oa(cr(),"tui.json")}function aa(e=""){for(let t of[Am,OP]){let r=process.env[t];if(!r||r==="")continue;let n=Dm(r)?r:MP(e||process.cwd(),r);if(ia(n))return n}return}function $P(e,t){if(!t||Object.keys(t).length===0)return e;return{...e,keybinds:{...e.keybinds,...t}}}function Do(e,t){let r=$P(Lm(),t),n=aa(e);if(n)return cd(r,na(ra(n,"utf8")),n);let o=FP(e),i=o?cd(r,na(ra(o,"utf8")),o):r,s=sa();if(!ia(s)||o===s)return i;return cd(i,na(ra(s,"utf8")),s)}function Im(e){let t=sa(),r={};if(ia(t)){let o=na(ra(t,"utf8"));if(Wn(o))r=o}let n={...r};if(e.theme!==void 0)n.theme=e.theme;if(e.mouse!==void 0)n.mouse=e.mouse;if(e.leader_timeout!==void 0)n.leader_timeout=e.leader_timeout;if(e.font_size!==void 0)n.font_size=e.font_size;if(e.log_timestamps!==void 0)n.log_timestamps=e.log_timestamps;if(e.log_metadata!==void 0)n.log_metadata=e.log_metadata;if(e.mcp_enabled!==void 0)n.mcp_enabled=e.mcp_enabled;if(e.mcp_port===null)delete n.mcp_port;else if(e.mcp_port!==void 0)n.mcp_port=e.mcp_port;if(e.mcp_disabled_tools!==void 0)n.mcp_disabled_tools=e.mcp_disabled_tools;return Cr(t,`${JSON.stringify(n,null,2)}
|
|
2235
|
+
`),t}function FP(e){let t=aa(e);if(t)return t;let r=[e||process.cwd(),oa(e||process.cwd(),".devctl"),cr()],n=["tui.json","tui.jsonc"];for(let o of r)for(let i of n){let s=oa(o,i);if(ia(s))return s}return}function cd(e,t,r){let n=Wn(t)?t:{},o={...e.keybinds};if(Wn(n.keybinds)){for(let[c,l]of Object.entries(n.keybinds))if(typeof l==="string")o[c]=l}let i={...e.cursor};if(Wn(n.cursor)){if(HP(n.cursor.style))i.style=n.cursor.style;if(typeof n.cursor.blinking==="boolean")i.blinking=n.cursor.blinking}let s={...e.scroll_acceleration};if(Wn(n.scroll_acceleration)&&typeof n.scroll_acceleration.enabled==="boolean")s.enabled=n.scroll_acceleration.enabled;let a={...e.attention,sounds:{...e.attention.sounds}};if(Wn(n.attention)){if(typeof n.attention.enabled==="boolean")a.enabled=n.attention.enabled;if(typeof n.attention.notifications==="boolean")a.notifications=n.attention.notifications;if(typeof n.attention.sound==="boolean")a.sound=n.attention.sound;if(typeof n.attention.volume==="number")a.volume=n.attention.volume;if(typeof n.attention.sound_pack==="string")a.sound_pack=n.attention.sound_pack;if(Wn(n.attention.sounds)){for(let[c,l]of Object.entries(n.attention.sounds))if(typeof l==="string")a.sounds[c]=NP(l,r)}}return{theme:typeof n.theme==="string"?n.theme:e.theme,leader_timeout:typeof n.leader_timeout==="number"?n.leader_timeout:e.leader_timeout,font_size:typeof n.font_size==="number"&&Number.isFinite(n.font_size)?Math.round(n.font_size):e.font_size,keybinds:o,scroll_speed:typeof n.scroll_speed==="number"?n.scroll_speed:e.scroll_speed,scroll_acceleration:s,diff_style:n.diff_style==="stacked"?"stacked":"auto",cursor:i,mouse:typeof n.mouse==="boolean"?n.mouse:e.mouse,attention:a,log_timestamps:typeof n.log_timestamps==="boolean"?n.log_timestamps:e.log_timestamps,log_metadata:typeof n.log_metadata==="boolean"?n.log_metadata:e.log_metadata,mcp_enabled:typeof n.mcp_enabled==="boolean"?n.mcp_enabled:e.mcp_enabled,mcp_port:typeof n.mcp_port==="number"&&Number.isInteger(n.mcp_port)?n.mcp_port:e.mcp_port,mcp_disabled_tools:Array.isArray(n.mcp_disabled_tools)?n.mcp_disabled_tools.filter((c)=>typeof c==="string"):e.mcp_disabled_tools,path:r}}function NP(e,t){if(e.startsWith("file://")||Dm(e))return e;return oa(IP(t),e)}function HP(e){return e==="block"||e==="underline"||e==="line"||e==="default"}function Wn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function na(e){let t=e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");return JSON.parse(t)}import{existsSync as HT,readFileSync as jT,writeFileSync as BT}from"fs";import{spawn as Sd}from"child_process";import{createWriteStream as m_,existsSync as Oi,mkdirSync as h_,readdirSync as Pd,readFileSync as g_,rmSync as v_,statSync as Cd,writeFileSync as y_}from"fs";import{dirname as rh,join as No}from"path";ga();ga();var b_=50000,_d="session-";function th(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}function x_(e){try{let t=JSON.parse(Buffer.from(e,"base64url").toString("utf8"));if(typeof t==="object"&&t!==null&&typeof t.session==="string"&&typeof t.seq==="number")return t}catch{}return}function kd(e,t){let r={...e};return r[t]=void 0,r}class Rd{events=[];eventStart=0;nextSeq=1;max;bus;detector;persistDir;persist;sessionID;streams=new Map;lastWrite=new Map;parsers=[];constructor(e,t,r,n,o,i,s=0,a=0){this.max=e>0?e:b_,this.bus=t,this.detector=r,this.sessionID=i;let c=o===""||o.startsWith("~/")?Qs():o;if(this.persist=n&&i!=="",this.persistDir=this.persist?No(c,`${_d}${i}`):"",this.persist)h_(this.persistDir,{recursive:!0,mode:448}),k_(c,s,a)}sessionDir(){return this.persistDir}setParsers(e){this.parsers=e}append(e){let t=gd(e.message),r=this.parseLine(t),n=(c)=>this.detector?this.detector.redactText(c):c,o=r.raw!==void 0,i=n(t),s=r.message!==void 0?n(gd(r.message)):void 0,a={...e,timestamp:e.timestamp||new Date().toISOString(),level:e.level||r.level||yd(t),request_id:e.request_id||r.request_id||bd(t),message:o&&s!==void 0?s:i,raw:o?i:void 0,seq:this.nextSeq++};if(this.events.length<this.max)this.events.push(a);else this.events[this.eventStart]=a,this.eventStart=(this.eventStart+1)%this.max;if(this.bus?.publish(Se(Io,a.service,{event:a,level:a.level})),this.persist){let c=`${a.timestamp} ${a.service} ${a.level} ${a.raw??a.message}
|
|
2236
|
+
`,l=S_(a.service),d=this.streamFor(l);this.lastWrite.set(l,new Promise((u)=>{d.write(c,()=>u())}))}return a}async flush(){await Promise.all([...this.lastWrite.values()])}parseLine(e){let t={};for(let r of this.parsers)try{let n=r.parse(e);if(n)t={...t,...n}}catch{}return t}streamFor(e){let t=this.streams.get(e);if(t===void 0)t=m_(No(this.persistDir,`${e}.log`),{flags:"a",mode:384}),t.on("error",()=>{}),this.streams.set(e,t);return t}async close(){await this.flush(),await Promise.all([...this.streams.values()].map((e)=>new Promise((t)=>{e.end(()=>t())}))),this.streams.clear(),this.lastWrite.clear()}query(e){let t=wd(e),r=[];return this.forEachEvent((n)=>{if(t(n))r.push(n)}),r}queryPage(e,t={}){let r=Zm(t.limit),n=t.cursor?x_(t.cursor):void 0,o=n!==void 0&&n.session!==this.sessionID,i=o?void 0:n,s=i?t.direction??"backward":"backward",a=wd(e),c=[];this.forEachEvent((m)=>{if(a(m))c.push(m)});let l;if(!i)l=c.slice(Math.max(0,c.length-r));else if(s==="forward")l=c.filter((m)=>m.seq>i.seq).slice(0,r);else{let m=c.filter((h)=>h.seq<i.seq);l=m.slice(Math.max(0,m.length-r))}let d=l[0]?.seq,u=l[l.length-1]?.seq,f=d!==void 0&&c.some((m)=>m.seq<d),p=u!==void 0&&c.some((m)=>m.seq>u);return{events:l,prevCursor:th({session:this.sessionID,seq:d??i?.seq??0}),nextCursor:th({session:this.sessionID,seq:u??i?.seq??this.nextSeq-1}),hasNext:p,hasPrev:f,sessionChanged:o}}queryFacets(e){let t=kd(e,"services"),r=kd(e,"level"),n=kd(e,"source"),o=xd(e),i=0,s={},a={},c={};return this.forEachEvent((l)=>{if(!o(l))return;if(Fo(e,l))i+=1;if(Fo(t,l))s[l.service]=(s[l.service]??0)+1;if(Fo(r,l))a[l.level]=(a[l.level]??0)+1;if(Fo(n,l))c[l.source]=(c[l.source]??0)+1}),{total:i,byService:s,byLevel:a,bySource:c}}snapshot(){let e={},t=0;return this.forEachEvent((r)=>{if(e[r.service]=(e[r.service]??0)+1,r.level===Kn||r.level===Oo)t+=1}),{total:this.events.length,errors:t,counts:e}}forEachEvent(e){let t=this.events.length;for(let r=0;r<t;r+=1){let n=this.events[(this.eventStart+r)%t];if(n)e(n)}}exportTo(e,t){Ed(e,this.query(t))}}function nh(e){return{append:(t)=>{e.append(t)},query:async(t)=>e.query(t),queryPage:async(t,r)=>e.queryPage(t,r),queryFacets:async(t)=>e.queryFacets(t),snapshot:()=>e.snapshot(),exportTo:async(t,r)=>{e.exportTo(t,r)},setParsers:(t)=>{e.setParsers(t)},setSecrets:(t,r)=>{},close:()=>e.close()}}function w_(e=new Date){let t=e.toISOString().replace(/[:.]/g,"-");return No(Zs(),`devctl-logs-${t}.log`)}function oh(e=""){if(e==="")return w_();return _i(e,process.cwd())}function Ed(e,t){Or(rh(e));let r=t.map((n)=>`${n.timestamp} ${n.service} ${n.level} ${n.raw??n.message}`);y_(e,`${r.join(`
|
|
2237
2237
|
`)}
|
|
2238
|
-
`,{mode:384})}function ih(e){let t=Oi(e)&&Cd(e).isDirectory()?e:rh(e);if(
|
|
2238
|
+
`,{mode:384})}function ih(e){let t=Oi(e)&&Cd(e).isDirectory()?e:rh(e);if(Or(t),process.platform==="darwin"){let r=Oi(e)&&Cd(e).isFile()?["-R",e]:[t];Sd("open",r,{detached:!0,stdio:"ignore"}).unref();return}if(process.platform==="win32"){Sd("explorer",[t],{detached:!0,stdio:"ignore"}).unref();return}Sd("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref()}function sh(e=Qs()){if(!Oi(e))return[];return Pd(e).filter((t)=>t.startsWith(_d)).sort().reverse()}function ah(e,t=Qs()){let r=No(t,e);if(!Oi(r))return[];let n=[];for(let i of Pd(r)){if(!i.endsWith(".log"))continue;let s=g_(No(r,i),"utf8");for(let a of s.split(`
|
|
2239
2239
|
`)){if(a.trim()==="")continue;let c=a.split(" "),l=c.slice(3).join(" "),d=vd(l);n.push({timestamp:c[0]??"",service:c[1]??i.replace(/\.log$/,""),source:"history",level:c[2]??"INFO",message:d?.message??l,raw:d?l:void 0,pid:0,seq:0})}}let o=n.sort((i,s)=>i.timestamp.localeCompare(s.timestamp));return o.forEach((i,s)=>{i.seq=s+1}),o}function S_(e){let t=e.replace(/[^A-Za-z0-9._-]+/g,"_");return t===""?"service":t}function k_(e,t,r){if(!Oi(e))return;let n=Pd(e).filter((i)=>i.startsWith(_d)).map((i)=>{let s=No(e,i),a=Cd(s);return{path:s,mtime:a.mtimeMs}}).sort((i,s)=>s.mtime-i.mtime),o=t>0?Date.now()-t*86400000:0;n.forEach((i,s)=>{let a=o>0&&i.mtime<o,c=r>0&&s>=r;if(a||c)v_(i.path,{recursive:!0,force:!0})})}te();var{spawn:Ld}=globalThis.Bun;import{createServer as mh}from"net";var C_=65535,Ad=1;async function hh(e,t,r={}){let n={};for(let[i,s]of Object.entries(r))for(let a of Object.values(s))n[a]=i;let o={};for(let[i,s]of Object.entries(e.services)){if(t.length>0&&!t.includes(i))continue;let a={};for(let c of s.ports){let l=r[i]?.[c.name],d=l&&l>=Ad?l:c.value;if(l===void 0&&c.auto)d=await P_();if(d<Ad||d>C_)throw new Ve(Y,`invalid port ${d} on service ${i}`,{service:i});if(n[d]&&n[d]!==i)throw new Ve(Y,`duplicate port ${d} used by ${n[d]} and ${i}`,{service:i});if(l!==d&&!c.auto&&!await jo(d))throw await __(i,c.name,d);n[d]=i,a[c.name]=d}o[i]=a}return o}function P_(){return new Promise((e,t)=>{let r=mh();r.listen(0,"127.0.0.1",()=>{let n=r.address();r.close(()=>{if(n&&typeof n==="object")e(n.port);else t(Pe(Y,"unable to allocate dynamic port",Error("no address")))})}),r.on("error",(n)=>t(Pe(Y,"unable to allocate dynamic port",n)))})}async function xa(e){let t=e.ports.filter((n)=>!n.auto&&n.value>=Ad);if(t.length===0)return;let r={};for(let n of t){if(await jo(n.value))return;r[n.name]=n.value}return r}function jo(e){return new Promise((t)=>{let r=mh();r.listen(e,"127.0.0.1",()=>{r.close(()=>t(!0))}),r.on("error",()=>t(!1))})}async function __(e,t,r){let n=await zn(r);return R_(e,t,r,n)}function R_(e,t,r,n){let o=t===""?`${e} port`:`${e} ports.${t}`;if(n&&n.pid===process.pid)return Te(He,`${e} blocked: this TUI already holds port ${r}`,`Stop the proxy from the Proxy screen, or change ${o} in .devctl.`,e);if(n)return Te(He,`${e} blocked: ${n.command} (pid ${n.pid}) is using port ${r}`,`Open Doctor and press enter to free port ${r}, or change ${o} in .devctl.`,e);return Te(He,`${e} blocked: port ${r} is already in use`,`Something is listening on ${r}. Open Doctor to free it, or change ${o} in .devctl.`,e)}function E_(e,t){let r=e.split(`
|
|
2240
2240
|
`).filter((n)=>n.trim()!==""&&!n.startsWith("COMMAND"));for(let n of r){let o=n.trim().split(/\s+/),i=o[0]??"",s=Number(o[1]);if(i!==""&&Number.isInteger(s)&&s>0)return{port:t,pid:s,command:i}}return}async function zn(e){if(process.platform==="win32")return D_(e);let t=await T_(e);if(t)return t;return A_(e)}async function T_(e){try{let t=Ld({cmd:["lsof","-nP",`-iTCP:${e}`,"-sTCP:LISTEN"],stdout:"pipe",stderr:"ignore"}),r=await new Response(t.stdout).text();return await t.exited,E_(r,e)}catch{return}}async function A_(e){let t;try{let o=Ld({cmd:["fuser","-n","tcp",String(e)],stdout:"pipe",stderr:"pipe"});t=`${await new Response(o.stdout).text()} ${await new Response(o.stderr).text()}`,await o.exited}catch{return}let r=t.match(/(\d+)/),n=r?Number(r[1]):0;if(!Number.isInteger(n)||n<=0)return;return{port:e,pid:n,command:"process"}}function L_(e,t){for(let r of e.split(`
|
|
2241
|
-
`)){if(!r.includes("LISTENING")&&!r.includes("LISTEN"))continue;if(!r.includes(`:${t}`)&&!r.includes(` ${t} `))continue;let n=r.trim().match(/(\d+)\s*$/),o=n?Number(n[1]):0;if(Number.isInteger(o)&&o>0)return{port:t,pid:o,command:"process"}}return}async function D_(e){let t=Ld({cmd:["netstat","-ano"],stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,L_(r,e)}var I_=400;async function wa(e){if(e.pid===process.pid)throw Te(He,`port ${e.port} is held by this TUI`,"stop the proxy from the proxy screen, or quit the TUI");try{process.kill(e.pid,"SIGTERM")}catch(t){throw Pe(He,`could not stop pid ${e.pid} on port ${e.port}`,t)}await M_(I_);try{process.kill(e.pid,0),process.kill(e.pid,"SIGKILL")}catch{return}}function M_(e){return new Promise((t)=>{setTimeout(t,e)})}Ma();te();import{readdirSync as KR,readFileSync as qR}from"fs";import{basename as Eg,dirname as VR,extname as zR,join as eo,resolve as YR}from"path";import{parse as JR}from"yaml";ht();function N(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Ud(e){return N(e)?new Set(Object.keys(e)):new Set}function G(e){if(typeof e==="string")return e;if(typeof e==="number"||typeof e==="boolean")return String(e);return""}function _e(e){if(typeof e==="number")return e;if(typeof e==="string"){let t=Number(e);return Number.isNaN(t)?0:t}return 0}function ot(e){return e===!0}function Rt(e){if(!Array.isArray(e))return[];return e.map((t)=>G(t))}function Wd(e){if(!Array.isArray(e))return[];return e.map((t)=>N(t)?{service:G(t.service),condition:G(t.condition)||"service_started"}:G(t))}function Sn(e){if(!N(e))return{};let t={};for(let[r,n]of Object.entries(e))t[r]=G(n);return t}function Pr(e){if(typeof e==="string")return{args:e.split(/\s+/).filter((t)=>t!==""),shell:!1};if(Array.isArray(e))return{args:e.map((t)=>G(t)),shell:!1};return Bo()}function Gd(e){if(e===void 0||e===null)return[];if(typeof e==="string"||typeof e==="number")return[Bd(e,"http")];if(Array.isArray(e))return e.map((t,r)=>Bd(t,`port_${r}`));if(N(e))return Object.entries(e).map(([t,r])=>Bd(r,t));return[]}function Bd(e,t){if(typeof e==="string"&&e.toLowerCase()==="auto")return{name:t,value:0,auto:!0};return{name:t,value:_e(e),auto:!1}}function _h(e){if(!N(e))return Md();let t={},r=[],n={};for(let[o,i]of Object.entries(e))if(o==="required")r=Rt(i);else if(o==="defaults")n=Sn(i);else t[o]=G(i);return{vars:t,required:r,defaults:n}}function G_(e){if(!N(e))return $d();return{type:G(e.type),mode:G(e.mode),service_account:G(e.service_account),config:N(e.config)?{...e.config}:{}}}function K_(e){if(!N(e))return Od();return{type:G(e.type),url:G(e.url),address:G(e.address),command:Pr(e.command),interval_seconds:_e(e.interval_seconds),timeout_seconds:_e(e.timeout_seconds),start_period_seconds:_e(e.start_period_seconds),unhealthy_threshold:e.unhealthy_threshold===void 0?3:_e(e.unhealthy_threshold),healthy_reset_threshold:e.healthy_reset_threshold===void 0?10:_e(e.healthy_reset_threshold)}}function q_(e){if(!N(e))return{policy:"",max_retries:0,backoff_seconds:0};return{enabled:e.enabled===void 0?void 0:ot(e.enabled),policy:G(e.policy),max_retries:_e(e.max_retries),backoff_seconds:_e(e.backoff_seconds)}}function V_(e){if(!N(e))return{wait_for_healthy:!1,timeout_seconds:0};return{wait_for_healthy:ot(e.wait_for_healthy),timeout_seconds:_e(e.timeout_seconds)}}function ji(e){if(!N(e))return vn();let t=N(e.logs)?e.logs:{};return{extends:G(e.extends),description:G(e.description),command:Pr(e.command),shell:ot(e.shell),working_dir:G(e.working_dir),dependencies:Wd(e.dependencies),ports:Gd(e.ports),environment:_h(e.environment),health:K_(e.health),identity:G_(e.identity),logs:{stdout:ot(t.stdout),stderr:ot(t.stderr)},restart:q_(e.restart),startup:V_(e.startup),capabilities:Rt(e.capabilities),proxy:Vd(e.proxy),expose:qd(e.expose),container:Kd(e.container),watch:z_(e.watch),hooks:Y_(e.hooks)}}function z_(e){if(!N(e))return Dd();return{enabled:ot(e.enabled),paths:Rt(e.paths),debounce_ms:Pa(_e(e.debounce_ms)),ignore:e.ignore===void 0?[...$i]:Rt(e.ignore)}}function Y_(e){let t=N(e)?e:{};return{pre_start:Pr(t.pre_start),post_start:Pr(t.post_start)}}function Rh(e){let t=N(e)?e:{};return{command:Pr(t.command),shell:ot(t.shell),working_dir:G(t.working_dir),dependencies:Rt(t.dependencies),environment:_h(t.environment)}}function Kd(e){if(!N(e))return;let t={};if(N(e.ports))for(let[r,n]of Object.entries(e.ports))t[r]=_e(n);return{image:G(e.image),runtime:G(e.runtime),ports:t,env:Sn(e.env),volumes:Rt(e.volumes)}}function qd(e){if(e===!0)return{enabled:!0,host:"",port:""};if(e===!1)return{enabled:!1,host:"",port:""};if(N(e))return{enabled:e.enabled!==void 0?ot(e.enabled):!0,host:G(e.host),port:G(e.port)};return Id()}function Vd(e){if(e===void 0||e===null)return[];if(Array.isArray(e))return e.map((t)=>Xn(t));if(N(e))return[Xn(e)];return[]}function Oa(e){if(!N(e))return{services:[],environment:{}};return{services:Rt(e.services),environment:Sn(e.environment)}}function J_(e){if(typeof e==="string")return{type:e,service_account:""};if(!N(e))return{type:"",service_account:""};return{type:G(e.type),service_account:G(e.service_account)}}function X_(e){if(!N(e))return Fi();return{type:G(e.type),identity:J_(e.identity),audience:G(e.audience),service_account:G(e.service_account),client_id:G(e.client_id),client_secret:G(e.client_secret),credentials:G(e.credentials)}}function Xn(e){if(!N(e))return{name:"",match:{host:"",path:""},upstream:{url:""},auth:Fi()};let t=N(e.match)?e.match:{},r=N(e.upstream)?e.upstream:{};return{name:G(e.name),match:{host:G(t.host),path:G(t.path)},upstream:{url:G(r.url),service:G(r.service),port:G(r.port)},auth:X_(e.auth)}}te();import{existsSync as Q_,statSync as Th}from"fs";import{basename as Eh,dirname as $a,isAbsolute as Z_,join as Fa,resolve as Ah}from"path";function Qn(e,t){if(t!=="")return eR(t);let r=e===""?process.cwd():e;r=Z_(r)?r:Ah(r);for(;;){let n=Fa(r,Nt,lr);if(Bi(n))return{repoRoot:r,configPath:n};let o=Fa(r,"devctl.yaml");if(Bi(o))return{repoRoot:r,configPath:o};let i=$a(r);if(i===r)break;r=i}throw Te(qn,"no devctl configuration found","run `devctl setup` or create a .devctl/config.yaml in the repository root")}function eR(e){let t=Ah(e),r;try{r=Th(t)}catch(o){throw Pe(qn,"config path not found",o)}if(r.isDirectory()){if(Eh(t)===Nt)return{repoRoot:$a(t),configPath:Fa(t,lr)};return{repoRoot:t,configPath:Fa(t,lr)}}let n=$a(t);if(Eh(n)===Nt)n=$a(n);return{repoRoot:n,configPath:t}}function Bi(e){return Q_(e)&&!Th(e).isDirectory()}ht();function ja(){return{services:{},templates:{},provenance:{}}}function rn(e,t,r,n,o=""){if(N(t)&&Object.keys(t).length>0){for(let[i,s]of Object.entries(t))rn(e,s,r,n,o===""?i:`${o}.${i}`);return}if(o==="")return;(e[o]??=[]).push({source:r,layer:n})}var tR=["environment","restart","startup","health","logs","identity","container","watch","hooks"];function Na(e,t,r){let n=Ud(r);if(N(r))for(let i of tR){let s=r[i];if(N(s))for(let a of Object.keys(s))n.add(`${i}.${a}`)}let o=e[t];e[t]=o?new Set([...o,...n]):n}function Lh(e,t,r=ja(),n={source:"unknown",layer:"unknown"}){if(rn(r.provenance,t,n.source,n.layer),t.version!==void 0)e.version=_e(t.version);if(N(t.project)&&t.project.name!==void 0)e.project.name=G(t.project.name);if(N(t.google)){if(t.google.project_id!==void 0)e.google.project_id=G(t.google.project_id);if(t.google.region!==void 0)e.google.region=G(t.google.region)}if(N(t.profiles))for(let[o,i]of Object.entries(t.profiles)){let s=e.profiles[o];e.profiles[o]=s?rR(s,i):Oa(i)}if(N(t.templates))for(let[o,i]of Object.entries(t.templates)){let s=e.templates[o];e.templates[o]=s?Ha(s,i):ji(i),Na(r.templates,o,i)}if(N(t.services))for(let[o,i]of Object.entries(t.services)){let s=e.services[o];e.services[o]=s?Ha(s,i):ji(i),Na(r.services,o,i)}if(N(t.tasks))for(let[o,i]of Object.entries(t.tasks))e.tasks[o]=e.tasks[o]?nR(e.tasks[o],i):Rh(i);if(N(t.proxy))zd(e.proxy,t.proxy);if(N(t.logs)){if(t.logs.max_memory_events!==void 0)e.logs.max_memory_events=_e(t.logs.max_memory_events);if(N(t.logs.persistence)){let o=t.logs.persistence;if(o.enabled!==void 0)e.logs.persistence.enabled=ot(o.enabled);if(o.directory!==void 0)e.logs.persistence.directory=G(o.directory);if(o.retention_days!==void 0)e.logs.persistence.retention_days=_e(o.retention_days);if(o.max_session_logs!==void 0)e.logs.persistence.max_session_logs=_e(o.max_session_logs)}}if(N(t.auth)&&t.auth.refresh_threshold_seconds!==void 0)e.auth.refresh_threshold_seconds=_e(t.auth.refresh_threshold_seconds);if(N(t.shutdown)){if(t.shutdown.stop_services_on_exit!==void 0)e.shutdown.stop_services_on_exit=ot(t.shutdown.stop_services_on_exit);if(t.shutdown.grace_seconds!==void 0)e.shutdown.grace_seconds=_e(t.shutdown.grace_seconds)}if(N(t.ui)){if(t.ui.theme!==void 0)e.ui.theme=G(t.ui.theme);if(t.ui.keymap!==void 0)e.ui.keymap=Sn(t.ui.keymap)}if(N(t.secrets)){if(t.secrets.extra_markers!==void 0)e.secrets.extra_markers=Rt(t.secrets.extra_markers);if(t.secrets.extra_patterns!==void 0)e.secrets.extra_patterns=Rt(t.secrets.extra_patterns)}if(N(t.doctor)&&Array.isArray(t.doctor.tools))e.doctor.tools=t.doctor.tools.filter(N).map((o)=>({name:G(o.name),command:G(o.command)}));if(Array.isArray(t.plugins))e.plugins=t.plugins.filter(N).map((o)=>({path:G(o.path)})).filter((o)=>o.path!=="");if(N(t.environment)){if(Array.isArray(t.environment.sources))e.environment.sources=Rt(t.environment.sources);if(N(t.environment.secrets))e.environment.secrets={...e.environment.secrets,...Sn(t.environment.secrets)}}}function zd(e,t){if(t.enabled!==void 0)e.enabled=ot(t.enabled);if(t.gateway!==void 0)e.gateway=ot(t.gateway);if(t.credentials!==void 0)e.credentials=G(t.credentials);if(N(t.listen)){if(t.listen.host!==void 0)e.listen.host=G(t.listen.host);if(t.listen.port!==void 0)e.listen.port=_e(t.listen.port)}if(N(t.token_endpoint)){let r=t.token_endpoint;if(r.enabled!==void 0)e.token_endpoint.enabled=ot(r.enabled);if(r.host!==void 0)e.token_endpoint.host=G(r.host);if(r.port!==void 0)e.token_endpoint.port=_e(r.port)}if(Array.isArray(t.routes))e.routes=t.routes.map((r)=>Xn(r))}function rR(e,t){if(!N(t))return e;return{services:t.services!==void 0?Rt(t.services):e.services,environment:t.environment!==void 0?{...e.environment,...Sn(t.environment)}:e.environment}}function nR(e,t){if(!N(t))return e;return{command:t.command!==void 0?Pr(t.command):e.command,shell:t.shell!==void 0?ot(t.shell):e.shell,working_dir:t.working_dir!==void 0?G(t.working_dir):e.working_dir,dependencies:t.dependencies!==void 0?Rt(t.dependencies):e.dependencies,environment:Dh(e.environment,t.environment)}}function Ha(e,t){if(!N(t))return e;let r=Ud(t),n={...e,environment:Dh(e.environment,t.environment),health:sR(e.health,t.health),identity:aR(e.identity,t.identity),logs:cR(e.logs,t.logs),restart:lR(e.restart,t.restart),startup:dR(e.startup,t.startup),container:uR(e.container,t.container),watch:oR(e.watch,t.watch),hooks:iR(e.hooks,t.hooks)};if(r.has("extends"))n.extends=G(t.extends);if(r.has("description"))n.description=G(t.description);if(r.has("command"))n.command=Pr(t.command);if(r.has("shell"))n.shell=ot(t.shell);if(r.has("working_dir"))n.working_dir=G(t.working_dir);if(r.has("dependencies"))n.dependencies=Wd(t.dependencies);if(r.has("ports"))n.ports=Gd(t.ports);if(r.has("capabilities"))n.capabilities=Rt(t.capabilities);if(r.has("proxy"))n.proxy=Vd(t.proxy);if(r.has("expose"))n.expose=qd(t.expose);return n}function oR(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,paths:t.paths!==void 0?Rt(t.paths):e.paths,debounce_ms:t.debounce_ms!==void 0?Pa(_e(t.debounce_ms)):e.debounce_ms,ignore:t.ignore!==void 0?Rt(t.ignore):e.ignore}}function iR(e,t){if(!N(t))return e;return{pre_start:t.pre_start!==void 0?Pr(t.pre_start):e.pre_start,post_start:t.post_start!==void 0?Pr(t.post_start):e.post_start}}function Dh(e,t){if(!N(t))return e;let r={...e.vars},n={...e.defaults},o=e.required;for(let[i,s]of Object.entries(t))if(i==="required")o=Rt(s);else if(i==="defaults")Object.assign(n,Sn(s));else r[i]=G(s);return{vars:r,required:o,defaults:n}}function sR(e,t){if(!N(t))return e;return{type:t.type!==void 0?G(t.type):e.type,url:t.url!==void 0?G(t.url):e.url,address:t.address!==void 0?G(t.address):e.address,command:t.command!==void 0?Pr(t.command):e.command,interval_seconds:t.interval_seconds!==void 0?_e(t.interval_seconds):e.interval_seconds,timeout_seconds:t.timeout_seconds!==void 0?_e(t.timeout_seconds):e.timeout_seconds,start_period_seconds:t.start_period_seconds!==void 0?_e(t.start_period_seconds):e.start_period_seconds,unhealthy_threshold:t.unhealthy_threshold!==void 0?_e(t.unhealthy_threshold):e.unhealthy_threshold,healthy_reset_threshold:t.healthy_reset_threshold!==void 0?_e(t.healthy_reset_threshold):e.healthy_reset_threshold}}function aR(e,t){if(!N(t))return e;return{type:t.type!==void 0?G(t.type):e.type,mode:t.mode!==void 0?G(t.mode):e.mode,service_account:t.service_account!==void 0?G(t.service_account):e.service_account,config:t.config!==void 0&&N(t.config)?{...e.config??{},...t.config}:e.config}}function cR(e,t){if(!N(t))return e;return{stdout:t.stdout!==void 0?ot(t.stdout):e.stdout,stderr:t.stderr!==void 0?ot(t.stderr):e.stderr}}function lR(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,policy:t.policy!==void 0?G(t.policy):e.policy,max_retries:t.max_retries!==void 0?_e(t.max_retries):e.max_retries,backoff_seconds:t.backoff_seconds!==void 0?_e(t.backoff_seconds):e.backoff_seconds}}function dR(e,t){if(!N(t))return e;return{wait_for_healthy:t.wait_for_healthy!==void 0?ot(t.wait_for_healthy):e.wait_for_healthy,timeout_seconds:t.timeout_seconds!==void 0?_e(t.timeout_seconds):e.timeout_seconds}}function uR(e,t){if(!N(t))return e;let r=Kd(t);if(!r)return e;return{image:t.image!==void 0?r.image:e?.image??"",runtime:t.runtime!==void 0?r.runtime:e?.runtime??"",ports:t.ports!==void 0?{...e?.ports??{},...r.ports}:e?.ports??{},env:t.env!==void 0?{...e?.env??{},...r.env}:e?.env??{},volumes:t.volumes!==void 0?r.volumes:e?.volumes??[]}}function Ih(e,t){let r=Object.keys(e.services).sort();for(let n of r){let o=e.services[n]?.proxy??[];if(o.length===0)continue;o.forEach((i,s)=>{let c={name:o.length===1?n:`${n}-${s+1}`,match:i.match,upstream:i.upstream,auth:i.auth},l=e.proxy.routes.length;if(e.proxy.routes.push(c),t)rn(t,c,`synthesized from services.${n}.proxy`,"synthesized",`proxy.routes.${l}`)})}pR(e,t)}function Mh(e){let t=e.repoRoot||process.cwd(),r=e.proxy.credentials.trim()===""?"":_i(e.proxy.credentials.trim(),t);e.proxy.credentials=r;for(let n of e.proxy.routes){if(n.auth.client_id.trim()==="")continue;let o=(n.auth.credentials??"").trim();n.auth.credentials=o===""?r:_i(o,t)}}function pR(e,t){if(!e.proxy.enabled)return;let r=new Set(e.proxy.routes.map((n)=>n.name));for(let n of Object.keys(e.services).sort()){let o=e.services[n];if(!o)continue;if(!(o.expose.enabled!==!1&&(o.expose.enabled===!0||e.proxy.gateway&&Jn(o.ports,"http")!==void 0))||r.has(n))continue;let a={name:n,match:{host:o.expose.host||`${n}.local`,path:""},upstream:{url:"",service:n,port:o.expose.port||"http"},auth:Fi()},c=e.proxy.routes.length;if(e.proxy.routes.push(a),r.add(n),t){let l=o.expose.enabled===!0?`services.${n}.expose`:"proxy.gateway";rn(t,a,`synthesized from ${l}`,"synthesized",`proxy.routes.${c}`)}}}function Oh(e,t=ja()){if(Object.keys(e.templates).length===0)return;let r={};for(let[n,o]of Object.entries(e.services))r[n]=$h(e,o,t.services[n]??new Set,t,{});e.services=r}function $h(e,t,r,n,o){if(t.extends==="")return t;if(o[t.extends])throw Error(`template cycle involving "${t.extends}"`);let i=e.templates[t.extends]??vn();if(!e.templates[t.extends])throw Error(`service extends unknown template "${t.extends}"`);let s={...o,[t.extends]:!0},a=n.templates[t.extends]??new Set,c=$h(e,i,a,n,s),l=fR(c,t,r);return l.extends=t.extends,l}function fR(e,t,r){let n={...e};if(r.has("extends"))n.extends=t.extends;if(r.has("description"))n.description=t.description;if(r.has("command"))n.command=t.command;if(r.has("shell"))n.shell=t.shell;if(r.has("working_dir"))n.working_dir=t.working_dir;if(r.has("dependencies"))n.dependencies=t.dependencies;if(r.has("ports"))n.ports=t.ports;if(r.has("capabilities"))n.capabilities=t.capabilities;if(r.has("proxy"))n.proxy=t.proxy;if(r.has("container")){let o=t.container,i=e.container;n.container=o&&i?{image:r.has("container.image")?o.image:i.image,runtime:r.has("container.runtime")?o.runtime:i.runtime,ports:r.has("container.ports")?o.ports:i.ports,env:r.has("container.env")?o.env:i.env,volumes:r.has("container.volumes")?o.volumes:i.volumes}:o??i}if(r.has("watch"))n.watch=t.watch;return n.hooks={pre_start:r.has("hooks.pre_start")?t.hooks.pre_start:e.hooks.pre_start,post_start:r.has("hooks.post_start")?t.hooks.post_start:e.hooks.post_start},n.health={type:r.has("health.type")?t.health.type:e.health.type,url:r.has("health.url")?t.health.url:e.health.url,address:r.has("health.address")?t.health.address:e.health.address,command:r.has("health.command")?t.health.command:e.health.command,interval_seconds:r.has("health.interval_seconds")?t.health.interval_seconds:e.health.interval_seconds,timeout_seconds:r.has("health.timeout_seconds")?t.health.timeout_seconds:e.health.timeout_seconds,start_period_seconds:r.has("health.start_period_seconds")?t.health.start_period_seconds:e.health.start_period_seconds,unhealthy_threshold:r.has("health.unhealthy_threshold")?t.health.unhealthy_threshold:e.health.unhealthy_threshold,healthy_reset_threshold:r.has("health.healthy_reset_threshold")?t.health.healthy_reset_threshold:e.health.healthy_reset_threshold},n.identity={type:r.has("identity.type")?t.identity.type:e.identity.type,mode:r.has("identity.mode")?t.identity.mode:e.identity.mode,service_account:r.has("identity.service_account")?t.identity.service_account:e.identity.service_account,config:r.has("identity.config")?t.identity.config:e.identity.config},n.logs={stdout:r.has("logs.stdout")?t.logs.stdout:e.logs.stdout,stderr:r.has("logs.stderr")?t.logs.stderr:e.logs.stderr},n.restart={enabled:r.has("restart.enabled")?t.restart.enabled:e.restart.enabled,policy:r.has("restart.policy")?t.restart.policy:e.restart.policy,max_retries:r.has("restart.max_retries")?t.restart.max_retries:e.restart.max_retries,backoff_seconds:r.has("restart.backoff_seconds")?t.restart.backoff_seconds:e.restart.backoff_seconds},n.startup={wait_for_healthy:r.has("startup.wait_for_healthy")?t.startup.wait_for_healthy:e.startup.wait_for_healthy,timeout_seconds:r.has("startup.timeout_seconds")?t.startup.timeout_seconds:e.startup.timeout_seconds},n.environment={vars:{...e.environment.vars,...t.environment.vars},defaults:{...e.environment.defaults,...t.environment.defaults},required:r.has("environment.required")?t.environment.required:e.environment.required},n}te();ht();function Fh(e){if(e.version===0)throw E(Y,"version is required");if(e.version===Uo)return e;throw E(Y,`unsupported config version ${e.version} (expected ${Uo}); no migration is available`)}var Nh=["version","project","google","profiles","templates","services","tasks","proxy","logs","auth","shutdown","ui","secrets","doctor","plugins","environment"],Hh=["extends","description","command","shell","working_dir","dependencies","ports","environment","health","identity","logs","restart","startup","capabilities","proxy","expose","container","watch","hooks"],jh=["type","url","address","command","interval_seconds","timeout_seconds","start_period_seconds","unhealthy_threshold","healthy_reset_threshold"],Bh=["service","condition"],Uh=["type","mode","service_account","config"],Wh=["enabled","policy","max_retries","backoff_seconds"],Gh=["wait_for_healthy","timeout_seconds"],Kh=["enabled","host","port"],qh=["enabled","gateway","credentials","listen","token_endpoint","routes"],Vh=["host","port"],Yd=["name","match","upstream","auth"],Jd=["host","path"],Xd=["url","service","port"],Qd=["type","identity","audience","service_account","client_id","client_secret","credentials"],zh=["max_memory_events","persistence"],Yh=["enabled","directory","retention_days","max_session_logs"],Jh=["refresh_threshold_seconds"],Xh=["stop_services_on_exit","grace_seconds"],Qh=["theme","keymap"],Zh=["name"],eg=["project_id","region"],tg=["services","environment"],Zd=["required","defaults"],rg=["stdout","stderr"],ng=["extra_markers","extra_patterns"],og=["tools"],ig=["name","command"],sg=["enabled","host","port"],ag=["path"],cg=["sources","secrets"],lg=["image","runtime","ports","env","volumes"],dg=["enabled","paths","debounce_ms","ignore"],ug=["pre_start","post_start"],pg=["command","shell","working_dir","dependencies","environment"],fg=["google","google_api","iap","network","service_identity","local_http"],mg=["|","||","&&",";",">",">>","<","&"];var mR=2;function kn(e,t){if(Array.isArray(e))return e.flatMap((o,i)=>kn(o,hg(t,String(i))));if(!wR(e))return[];let r=gR(t),n=[];for(let o of Object.keys(e)){let i=hg(t,o);if(r.length>0&&!r.includes(o)&&!hR(t))n.push(i);n.push(...kn(e[o],i))}return n}function hR(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return!0;if(e.endsWith(".environment")||e.endsWith(".defaults")||e.endsWith(".keymap"))return!0;if(e.endsWith(".container.ports")||e.endsWith(".container.env"))return!0;if(e.endsWith(".identity.config")||e.includes(".identity.config."))return!0;return e.includes(".environment.")&&!e.endsWith(".environment")}function gR(e){switch(e){case"":return Nh;case"project":return Zh;case"google":return eg;case"proxy":return qh;case"proxy.listen":return Vh;case"proxy.token_endpoint":return sg;case"logs":return zh;case"logs.persistence":return Yh;case"auth":return Jh;case"shutdown":return Xh;case"ui":return Qh;case"secrets":return ng;case"doctor":return og;case"environment":return cg;default:return vR(e)}}function vR(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return[];if(e.startsWith("services.")||e.startsWith("templates.")){if(e.includes(".dependencies."))return Bh;return yR(e)}if(e.startsWith("profiles.")){if(e.split(".").length===2)return tg}if(e.startsWith("tasks.")){let t=e.split(".");if(t.length===2)return pg;if(t[2]==="environment")return Zd}if(e.includes("routes"))return xR(e);if(e.startsWith("doctor.tools")&&e.split(".").length>=3)return ig;if(e==="plugins")return[];if(e.startsWith("plugins.")&&e.split(".").length===2)return ag;return[]}function yR(e){let t=e.split(".");if(t.length===2)return Hh;if(t.length>=3)switch(t[2]){case"health":return jh;case"identity":return Uh;case"restart":return Wh;case"startup":return Gh;case"logs":return rg;case"environment":return Zd;case"proxy":return bR(t);case"expose":return Kh;case"container":return lg;case"watch":return dg;case"hooks":return ug;default:return[]}return[]}function bR(e){let t=e.slice(3),r=t[0]!==void 0&&/^\d+$/.test(t[0])?1:0,n=t[r]??"";if(n==="match")return Jd;if(n==="upstream")return Xd;if(n==="auth")return Qd;return Yd}function xR(e){if(e==="proxy.routes")return[];if(e.endsWith(".match"))return Jd;if(e.endsWith(".upstream"))return Xd;if(e.endsWith(".auth"))return Qd;if(e.includes("proxy.routes")&&e.split(".").length===mR+1)return Yd;return[]}function hg(e,t){if(e==="")return t;return`${e}.${t}`}function wR(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Wo(e){return`unknown fields: ${e.join(", ")}`}ht();function Cn(e){let t=e.trim().toLowerCase();if(t==="")return!0;if(t==="localhost"||t==="::1")return!0;if(gg(t))return!0;return SR(t)}function gg(e){let t=e.split(".");if(t.length!==4)return!1;let r=t.map((n)=>Number(n));if(r.some((n)=>!Number.isInteger(n)||n<0||n>255))return!1;return r[0]===127}function SR(e){if(!e.startsWith("::ffff:"))return!1;return gg(e.slice(7))}import{existsSync as PR}from"fs";import{isAbsolute as _R,resolve as RR}from"path";import{fileURLToPath as ER}from"url";ht();function vg(e,t){if(t){if(t.http!==void 0)return t.http;let n=Object.values(t)[0];if(n!==void 0)return n}let r=Sh(e.ports);if(r&&!r.auto)return r.value;return}function kR(e,t,r){let n=e,o="";for(;;){let i=n.indexOf("${");if(i<0)return o+n;o+=n.slice(0,i);let s=n.slice(i).indexOf("}");if(s<0)throw Error(`unclosed environment reference in "${e}"`);let a=n.slice(i+2,i+s);o+=CR(a,t,r),n=n.slice(i+s+1)}}function CR(e,t,r){let n=e.split(".");if(n.length<3||n[0]!=="services")throw Error(`unsupported reference \${${e}}`);let o=n[1]??"",i=t.services[o];if(!i)throw Error(`reference \${${e}}: unknown service`);let s=r[o];if(n[2]==="host"||n[2]==="url"){let a=t.proxy.enabled?t.proxy.routes.find((l)=>l.upstream.service===o):void 0;if(n[2]==="host")return a?a.match.host||"127.0.0.1":"127.0.0.1";if(a)return`http://${a.match.host||"127.0.0.1"}:${t.proxy.listen.port}${a.match.path}`;let c=vg(i,s);if(c===void 0)throw Error(`unresolvable reference \${${e}}`);return`http://127.0.0.1:${c}`}if(n[2]==="port"){let a=vg(i,s);if(a!==void 0)return String(a)}if(n[2]==="ports"){let a=n[3];if(!a)throw Error(`reference \${${e}}: missing port name`);if(s&&s[a]!==void 0)return String(s[a]);let c=Jn(i.ports,a);if(c&&!c.auto)return String(c.value);let l=Number.parseInt(a,10);if(!Number.isNaN(l)&&l>=0&&l<i.ports.length){let d=i.ports[l];if(d&&!d.auto)return String(d.value)}}throw Error(`unresolvable reference \${${e}}`)}function eu(e,t,r){let n={};for(let[o,i]of Object.entries(e))n[o]=kR(i,t,r);return n}function yg(e){let t=[],r=e;for(;;){let n=r.indexOf("${");if(n<0)return t;let o=r.slice(n).indexOf("}");if(o<0)return t;t.push(r.slice(n+2,n+o)),r=r.slice(n+o+1)}}function bg(e,t){let r=e.split(".");if(r.length<2)return!1;if(r[0]!=="services"||r.length<3)return!1;let n=t.services[r[1]??""];if(!n)return!1;if(r[2]==="port"||r[2]==="ports"){if(r.length===3)return n.ports.length>0;if(r.length>=4){let o=r[3]??"";if(!Number.isNaN(Number.parseInt(o,10)))return!0;return Jn(n.ports,o)!==void 0}}return!0}ht();var Wa=65535,Ga=1,Pg=["http","tcp","process","command"];function ru(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=n.health.type;if(o!==""&&!Pg.includes(o.toLowerCase()))t.push({service:r,type:o})}return t}var TR=0,kg=1,Cg=2;function Zn(e){let t=[];if(e.version===0)t.push("version is required");else if(e.version!==Uo)t.push(`unsupported config version ${e.version} (expected ${Uo}); no migration is available`);if(Object.keys(e.services).length===0)t.push("at least one service must be defined");t.push(...LR(e)),t.push(...AR(e)),t.push(...OR(e)),t.push(...$R(e)),t.push(...FR(e));for(let[r,n]of e.plugins.entries())if(n.path==="")t.push(`plugins.${r}.path is required`);else try{let o=n.path.startsWith("file:")?ER(n.path):_R(n.path)?n.path:RR(e.repoRoot,n.path);if(!PR(o))t.push(`plugins.${r}.path does not exist: ${n.path}`)}catch{t.push(`plugins.${r}.path is invalid: ${n.path}`)}if(e.logs.max_memory_events<0)t.push("logs.max_memory_events must be >= 0");return t}function AR(e){let t=[];for(let[r,n]of Object.entries(e.tasks)){let o=`tasks.${r}`;if(yn(n.command))t.push(`${o}.command is required`);t.push(...Ua(o,n.command,n.shell));for(let i of n.dependencies)if(!e.services[i])t.push(`${o}.dependencies: unknown service "${i}"`);t.push(..._g(`${o}.environment`,n.environment,e))}return t}function LR(e){let t=[],r={};for(let[n,o]of Object.entries(e.services)){let i=`services.${n}`;if(yn(o.command)&&!o.container)t.push(`${i}.command is required`);t.push(...Ua(i,o.command,o.shell)),t.push(...Ua(`${i}.hooks.pre_start`,o.hooks.pre_start,o.shell)),t.push(...Ua(`${i}.hooks.post_start`,o.hooks.post_start,o.shell)),t.push(...UR(i,o.capabilities));for(let c of o.dependencies){let l=zt(c);if(!e.services[l])t.push(`${i}.dependencies: unknown service "${l}"`);if(l===n)t.push(`${i}.dependencies: service cannot depend on itself`);if(!["service_started","service_healthy"].includes(gn(c)))t.push(`${i}.dependencies: condition must be service_started or service_healthy`);if(gn(c)==="service_healthy"&&e.services[l]?.health.type==="")t.push(`${i}.dependencies: service_healthy requires ${l} to define a health check`)}if(o.extends!==""&&!e.templates[o.extends])t.push(`${i}.extends: unknown template "${o.extends}"`);for(let c of o.ports.filter((l)=>!l.auto))if(c.value<Ga||c.value>Wa)t.push(`${i}.ports.${c.name}: invalid port ${c.value}`);else if(r[c.value])t.push(`duplicate port ${c.value} used by ${r[c.value]} and ${n}`);else r[c.value]=n;let s=MR(`${i}.identity`,o.identity,e.plugins.length>0);if(s!=="")t.push(s);if(t.push(...DR(i,o,e.plugins.length>0)),o.health.start_period_seconds<0)t.push(`${i}.health.start_period_seconds must be >= 0`);if(o.health.unhealthy_threshold<1)t.push(`${i}.health.unhealthy_threshold must be >= 1`);if(o.health.healthy_reset_threshold<1)t.push(`${i}.health.healthy_reset_threshold must be >= 1`);let a=Ea(o.restart);if(a!==Sa&&a!==ka&&a!==Ca)t.push(`${i}.restart.policy must be never, on_failure, or always`);if(t.push(..._g(`${i}.environment`,o.environment,e)),o.container){if(o.container.image==="")t.push(`${i}.container.image is required`);if(o.container.runtime!==""&&o.container.runtime!=="docker"&&o.container.runtime!=="podman")t.push(`${i}.container.runtime must be docker or podman`);for(let[c,l]of Object.entries(o.container.ports)){if(!o.ports.some((d)=>d.name===c))t.push(`${i}.container.ports.${c}: no matching service port`);if(l<Ga||l>Wa)t.push(`${i}.container.ports.${c}: invalid container port ${l}`)}}}return t}function DR(e,t,r){let n=[];if(t.health.type==="")return n;let o=t.health.type.toLowerCase();if(!Pg.includes(o)&&!r)n.push(`${e}.health.type must be http, tcp, process, or command`);if(o==="http"&&t.health.url==="")n.push(`${e}.health.url is required for http health checks`);if(o==="tcp"&&t.health.address===""&&t.ports.length===0)n.push(`${e}.health.address is required for tcp health checks without ports`);if(o==="command"&&yn({args:t.health.command.args,shell:!1}))n.push(`${e}.health.command is required for command health checks`);return n}var IR=["","user","service","service_account"];function MR(e,t,r){let n=Hi(t).toLowerCase();if(n===""||n==="user")return"";if(n==="service"||n==="service_account"){if(t.service_account==="")return`${e}.service_account is required for service identity`;if(!t.service_account.includes("@"))return`${e}.service_account must be an email`;return""}if(r)return"";return`${e}.type must be user or service_account`}function nu(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=Hi(n.identity).toLowerCase();if(!IR.includes(o))t.push({service:r,type:o})}return t}function OR(e){let t={},r=[],n=[],o=(i)=>{let s=t[i]??TR;if(s===Cg)return;if(s===kg){r.push(`dependency cycle: ${[...n,i].join(" \u2192 ")}`);return}t[i]=kg,n.push(i);let a=e.services[i];if(a)for(let c of a.dependencies){let l=zt(c);if(e.services[l])o(l)}n.pop(),t[i]=Cg};for(let i of Object.keys(e.services))o(i);return r}function _g(e,t,r){let n=[],o=(i,s)=>{for(let a of yg(s))if(!bg(a,r))n.push(`${e}.${i}: unresolvable reference \${${a}}`)};for(let[i,s]of Object.entries(t.vars))o(i,s);for(let[i,s]of Object.entries(t.defaults))o(i,s);return n}function $R(e){let t=[];for(let[r,n]of Object.entries(e.profiles))for(let o of n.services)if(!e.services[o])t.push(`profiles.${r} references unknown service "${o}"`);return t}function FR(e){let t=[];if(e.proxy.listen.host!==""){if(!WR(e.proxy.listen.host))t.push("proxy.listen.host must be an IP address or localhost");else if(!Cn(e.proxy.listen.host))t.push("proxy.listen.host must be a loopback address")}if(e.proxy.enabled&&e.proxy.listen.port===0)t.push("proxy.listen.port is required when proxy.enabled is true");if(e.proxy.listen.port!==0&&(e.proxy.listen.port<Ga||e.proxy.listen.port>Wa))t.push("proxy.listen.port is invalid");let r={};return e.proxy.routes.forEach((n,o)=>{let i=`proxy.routes[${o}]`;if(n.name==="")t.push(`${i}.name is required`);else if(r[n.name])t.push(`${i}: duplicate route name ${n.name}`);r[n.name]=!0,t.push(...NR(n,i,e)),t.push(...HR(n,i))}),t.push(...BR(e)),t}function NR(e,t,r){let n=[],o=e.upstream.url!=="",i=e.upstream.service??"";if(i===""){if(!o)n.push(`${t}.upstream requires either url or service`);return n}let s=r.services[i];if(!s)return n.push(`${t}.upstream.service references unknown service ${i}`),n;let a=e.upstream.port||"http";if(Jn(s.ports,a)===void 0)n.push(`${t}.upstream: service ${i} has no port named ${a}`);return n}function HR(e,t){let r=[];if(e.auth.type.toLowerCase()==="iap"){if(e.auth.audience.trim()==="")r.push(`${t}.auth.audience is required when auth.type is iap`);if(e.auth.identity.type.trim()==="")r.push(`${t}.auth.identity.type is required when auth.type is iap`)}r.push(...jR(e.auth,t));let n=e.auth.identity.type.toLowerCase();if(n==="service"||n==="service_account"||en({type:n,mode:"",service_account:""})){if((e.auth.identity.service_account||e.auth.service_account)==="")r.push(`${t}.auth.identity.service_account is required`)}return r}function jR(e,t){let r=(e.client_id??"").trim(),n=(e.client_secret??"").trim(),o=(e.credentials??"").trim();if(r===""&&n===""&&o==="")return[];if(e.type.toLowerCase()!=="iap")return[`${t}.auth.${r!==""?"client_id":n!==""?"client_secret":"credentials"} is only valid when auth.type is iap`];let i=[];if(r===""){if(n!=="")i.push(`${t}.auth.client_id is required when client_secret is set`);if(o!=="")i.push(`${t}.auth.credentials requires auth.client_id`);return i}if(n===""&&o==="")i.push(`${t}.auth.client_secret is required when client_id is set`);else if(n!==""&&tu(n).length>0&&!Sg(n))i.push(`${t}.auth.client_secret must be a literal or a single \${NAME} / \${env.NAME} reference`);let s=e.identity.type.toLowerCase();if(s==="service"||s==="service_account")i.push(`${t}.auth.client_id is only valid with identity.type user`);return i}function BR(e){let t=[],r=e.proxy.token_endpoint;if(!r.enabled)return t;let n=r.host||_a;if(!Cn(n))t.push("proxy.token_endpoint.host must be a loopback address");if(r.port!==0&&(r.port<Ga||r.port>Wa))t.push("proxy.token_endpoint.port is invalid");return t}function Ua(e,t,r){if(t.shell||r)return[];for(let n of t.args)if(mg.includes(n)||n.includes("|")||n.includes(";")||n.includes("&&"))return[`${e}.command contains shell metacharacters; set shell: true to run via a shell`];return[]}function UR(e,t){let r=[];for(let n of t)if(!fg.includes(n))r.push(`${e}.capabilities: unknown capability "${n}"`);return r}function WR(e){if(e==="localhost")return!0;return GR(e)}function GR(e){if(/^(\d{1,3}\.){3}\d{1,3}$/.test(e))return e.split(".").every((r)=>{let n=Number(r);return n>=0&&n<=255});return e.includes(":")}function Ui(e,t,r){try{return Wi(e,t,{candidateText:r}),[]}catch(n){if(n instanceof Ve){let o=`${n.kind}: `;return[n.message.startsWith(o)?n.message.slice(o.length):n.message]}return[n instanceof Error?n.message:String(n)]}}function nn(e,t){let{repoRoot:r,configPath:n}=Qn(e,t);return Wi(r,n)}function to(e,t){try{return nn(e,t)}catch(r){if(!ba(r,qn))throw r;let n=e===""?process.cwd():YR(e),o=Yn();return o.repoRoot=n,o.configPath=eo(n,Nt,lr),o}}function Wi(e,t,r){let n=Yn(),o=ja();Tg(t,n,o,r?.candidateText,"main"),n.repoRoot=e,n.configPath=t;let i=VR(t);if(Eg(i)===Nt)QR(i,n,o);n=XR(n,e,t,o),n=Fh(n);try{Oh(n,o)}catch(a){throw Pe(Y,"template merge failed",a)}Ih(n,o.provenance),Mh(n),n.provenance=o.provenance;let s=Zn(n);if(s.length>0)throw E(Y,s.join(`
|
|
2242
|
-
`));return n}function XR(e,t,r,n){let o=eo(cr(),"config.local.yaml"),i=eo(t,Nt,"config.local.yaml");for(let s of[o,i]){if(s===r||!Bi(s))continue;Tg(s,e,n,void 0,s===o?"home_local":"repo_local")}return e}function Tg(e,t,r,n,o="main"){let i=n!==void 0?Ag(n,e):ou(e),s=kn(i,"");if(s.length>0)throw
|
|
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 _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)=>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:_r(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:_r(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:_r(t.pre_start),post_start:_r(t.post_start)}}function Rh(e){let t=N(e)?e:{};return{command:_r(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?_r(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=_r(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?_r(t.pre_start):e.pre_start,post_start:t.post_start!==void 0?_r(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?_r(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 _(Y,"version is required");if(e.version===Uo)return e;throw _(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 _(Y,s.join(`
|
|
2242
|
+
`));return n}function XR(e,t,r,n){let o=eo(cr(),"config.local.yaml"),i=eo(t,Nt,"config.local.yaml");for(let s of[o,i]){if(s===r||!Bi(s))continue;Tg(s,e,n,void 0,s===o?"home_local":"repo_local")}return e}function Tg(e,t,r,n,o="main"){let i=n!==void 0?Ag(n,e):ou(e),s=kn(i,"");if(s.length>0)throw _(Y,Wo(s));Lh(t,i,r,{source:e,layer:o})}function QR(e,t,r){Rg(eo(e,"services"),(o,i,s)=>{let a=kn(i,`services.${o}`);if(a.length>0)throw _(Y,Wo(a));let c=t.services[o];t.services[o]=c?Ha(c,i):ji(i),Na(r.services,o,i),nn(r.provenance,i,s,"modular_service",`services.${o}`)}),Rg(eo(e,"profiles"),(o,i,s)=>{let a=kn(i,`profiles.${o}`);if(a.length>0)throw _(Y,Wo(a));t.profiles[o]=Oa(i),nn(r.provenance,i,s,"modular_profile",`profiles.${o}`)});let n=eo(e,"proxy","routes.yaml");if(Bi(n))ZR(n,t,r)}function ZR(e,t,r){let n=ou(e);if(N(n.proxy))nn(r.provenance,n.proxy,e,"modular_proxy","proxy");if(Array.isArray(n.routes))nn(r.provenance,n.routes,e,"modular_proxy","proxy.routes");if(N(n.proxy)){let o=kn({proxy:n.proxy},"");if(o.length>0)throw _(Y,Wo(o))}if(Array.isArray(n.routes))n.routes.forEach((o,i)=>{let s=kn(o,`proxy.routes.${i}`);if(s.length>0)throw _(Y,Wo(s))});if(N(n.proxy)){let o={...n.proxy};if(delete o.routes,zd(t.proxy,o),Array.isArray(n.proxy.routes))t.proxy.routes.push(...n.proxy.routes.map((i)=>Xn(i)))}if(Array.isArray(n.routes))t.proxy.routes.push(...n.routes.map((o)=>Xn(o)))}function Rg(e,t){let r;try{r=KR(e)}catch(n){if(eE(n))return;throw n}for(let n of r.sort()){let o=zR(n);if(o!==".yaml"&&o!==".yml")continue;let i=eo(e,n),s=ou(i);t(Eg(n,o),s,i)}}function ou(e){let t;try{t=qR(e,"utf8")}catch(r){throw Pe(Y,"unable to read config",r)}return Ag(t,e)}function Ag(e,t){let r;try{r=JR(e)}catch(n){throw Pe(Y,`invalid YAML in ${t}`,n)}if(r===null||r===void 0)return{};if(!N(r))throw _(Y,`unable to decode config: ${t} is not a mapping`);return r}function eE(e){return typeof e==="object"&&e!==null&&"code"in e&&e.code==="ENOENT"}ht();te();jr();import{existsSync as Va,readFileSync as rE}from"fs";import{homedir as Og}from"os";import{join as cu}from"path";var{spawn:lu}=globalThis.Bun;var nE="https://www.googleapis.com/auth/cloud-platform",$g=1500,oE=2000;function qo(){let e=globalThis;if(e.window&&typeof e.window.fetch!=="function")e.window.fetch=fetch}function du(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS||cu(Og(),".config","gcloud","application_default_credentials.json");if(!Va(e))return"";try{let t=JSON.parse(rE(e,"utf8"));return typeof t.quota_project_id==="string"?t.quota_project_id:""}catch{return""}}async function io(e){let t={gcloudInstalled:await uu("gcloud"),adcAvailable:!1,userEmail:"",projectID:"",projectSource:""};if(e!=="")t.projectID=e,t.projectSource="configuration";else{let r=lE("GOOGLE_CLOUD_PROJECT","GCLOUD_PROJECT","GOOGLE_PROJECT");if(r!=="")t.projectID=r,t.projectSource="environment variable";else if(t.gcloudInstalled){let n=await Mg("core/project");if(n!=="")t.projectID=n,t.projectSource="gcloud configuration"}}if(Ki())try{await Bg(iE(t),oE)}catch(r){t.adcAvailable=!1,t.error=Vo(r)}if(t.userEmail===""&&t.gcloudInstalled)t.userEmail=await Mg("core/account");return t}async function iE(e){qo();let{GoogleAuth:t}=await import("google-auth-library"),r=new t({scopes:[nE]});await r.getClient(),e.adcAvailable=!0;let n=await r.getProjectId().catch(()=>"");if(e.projectID===""&&n)e.projectID=n,e.projectSource="application default credentials";let o=await sE(r);if(o!=="")e.userEmail=o}async function sE(e){try{let t=await e.getCredentials();if(t.client_email)return t.client_email}catch{}try{let t=await e.getClient(),r=typeof t.credentials?.id_token==="string"?t.credentials.id_token:"";if(r!==""){let n=aE(r);if(n!=="")return n}}catch{return""}return""}function aE(e){let t=e.split(".");if(t.length<2||!t[1])return"";try{let r=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));return typeof r.email==="string"?r.email:""}catch{return""}}async function Fg(e){let t=await io(e);return dr({kind:"user",email:t.userEmail,project:t.projectID,projectSource:t.projectSource,adcAvailable:t.adcAvailable})}async function Ng(){if(await lu({cmd:["gcloud","auth","application-default","login"],stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited!==0)throw Te(gn,"application-default login failed","run `gcloud auth application-default login`")}async function Hg(){if(await lu({cmd:["gcloud","auth","application-default","revoke","--quiet"],stdout:"ignore",stderr:"pipe"}).exited!==0)throw Te(gn,"failed to revoke application-default credentials","run `gcloud auth application-default revoke`")}function Vo(e){let t=cE(e).toLowerCase();if(oo(t,["unauthorized_client"]))return Te(ya,"IAP OAuth client does not match the ADC refresh token","omit client_id to use ADC's default client, or login with an OAuth client that matches auth.client_id");if(oo(t,["unauth","invalid_grant","token has been expired","expired"]))return Te(gn,"credential expired or invalid","run `devctl auth login` or `gcloud auth application-default login`");if(oo(t,["api has not been used","has not been enabled","access not configured","service_disabled"]))return Te(Vn,"required Google API is not enabled","enable the API in the target service account project; devctl will not enable APIs automatically");if(oo(t,["permission","forbidden","iam"])){if(oo(t,["serviceaccounttoken","iamcredentials","token creator","getaccesstoken"]))return Te(va,"cannot impersonate service account","ask an administrator to grant roles/iam.serviceAccountTokenCreator on the target service account");return Te(Vn,"authorization failure","verify IAM permissions for the current user on this project")}if(t.includes("audience"))return Te(ya,"IAP audience is incorrect","set auth.audience on the proxy route to the IAP OAuth client ID");if(t.includes("iap"))return Pe(ya,"IAP authentication failure",e);if(t.includes("project"))return Te(Y,"wrong or missing Google project","set google.project_id in .devctl/config.yaml");if(oo(t,["timeout","network","connection refused","no such host"]))return Pe(lt,"network problem reaching Google Cloud",e);if(oo(t,["adc","default credentials","could not find"]))return Te(gn,"application default credentials unavailable","run `gcloud auth application-default login`");return Pe(gn,"Google authentication failed",e)}function cE(e){let t=[],r=new Set,n=(o,i)=>{if(o===null||o===void 0||i>8||r.has(o))return;if(typeof o==="string"){t.push(o);return}if(typeof o!=="object")return;r.add(o);let s=o;for(let a of["message","reason","status","code","error_description","permission","service"])n(s[a],i+1);for(let a of["error","errors","details","response","data","cause","metadata"]){let c=s[a];if(Array.isArray(c))c.forEach((l)=>n(l,i+1));else n(c,i+1)}};return n(e,0),t.length>0?t.join(" "):String(e)}function oo(e,t){return t.some((r)=>e.includes(r))}function lE(...e){for(let t of e){let r=process.env[t];if(r)return r}return""}async function uu(e){try{return(await jg(process.platform==="win32"?["where",e]:["which",e],$g)).code===0}catch{return!1}}function Ki(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS;if(e&&Va(e))return!0;if(Va(cu(Og(),".config","gcloud","application_default_credentials.json")))return!0;let t=process.env.APPDATA;return Boolean(t&&Va(cu(t,"gcloud","application_default_credentials.json")))}async function Mg(e){try{let t=await jg(["gcloud","config","get-value",e],$g);if(t.code!==0)return"";let r=t.stdout.trim();if(r===""||r==="(unset)")return"";return r}catch{return""}}async function jg(e,t){let r=lu({cmd:e,stdout:"pipe",stderr:"ignore",env:{...process.env,CLOUDSDK_CORE_DISABLE_PROMPTS:"1",CLOUDSDK_CORE_DISABLE_USAGE_REPORTING:"true"}}),n=r.stdout?new Response(r.stdout).text():Promise.resolve("");try{let[o,i]=await Bg(Promise.all([n,r.exited]),t);return{code:i??1,stdout:o}}catch{return r.kill(),{code:1,stdout:""}}}function Bg(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("timeout")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}import{createHash as PE}from"crypto";import{readFileSync as _E}from"fs";import{join as RE}from"path";import{GoogleAuth as Vi,Impersonated as EE}from"google-auth-library";var{spawn:hu}=globalThis.Bun;import{existsSync as Ya,readdirSync as dE,readFileSync as Ug,unlinkSync as uE}from"fs";import{join as qi}from"path";var gu="keychain-index.json",so="devctl.credentials",fu=".cred.json";function mu(e){return qi(kr(),`${Xa(e)}${fu}`)}function Ja(e){if(e==="file"||process.env.DEVCTL_HOME&&e!=="keychain")return new za;if(e==="keychain"||process.platform==="darwin"||process.platform==="win32"||process.platform==="linux")return new Wg(new za);return new za}class za{backend="file";async get(e){let t=hE(e);if(!t||t.accessToken==="")return;return t}async set(e,t){Cr(mu(e),`${JSON.stringify({...mE(t),key:e})}
|
|
2243
2243
|
`)}async delete(e){let t=mu(e);if(Ya(t))uE(t)}async list(){let e=kr();if(!Ya(e))return[];let t=[];for(let r of dE(e)){if(!r.endsWith(fu))continue;let n=r.slice(0,-fu.length),o=Gg(qi(kr(),r));if(!o)continue;t.push(Kg(o.key??n,o.record))}return t.sort((r,n)=>r.identity.localeCompare(n.identity))}}class Wg{backend="keychain";fallback;constructor(e){this.fallback=e}async get(e){let t=await gE(e);if(t)return t;return this.fallback.get(e)}async set(e,t){if(!await vE(e,t))await this.fallback.set(e,t);pE(e)}async delete(e){await yE(e),await this.fallback.delete(e),fE(e)}async list(){let e=new Set(vu()),t=await this.fallback.list(),r=new Map;for(let n of t)r.set(n.key,n);for(let n of e){let o=await this.get(n);if(o)r.set(n,Kg(n,o))}return[...r.values()].sort((n,o)=>n.identity.localeCompare(o.identity))}}function pE(e){let t=vu();if(!t.includes(e))Cr(qi(kr(),gu),`${JSON.stringify([...t,e])}
|
|
2244
2244
|
`)}function fE(e){let t=vu(),r=t.filter((n)=>n!==e);if(r.length!==t.length)Cr(qi(kr(),gu),`${JSON.stringify(r)}
|
|
2245
|
-
`)}function vu(){let e=qi(kr(),gu);if(!Ya(e))return[];try{let t=JSON.parse(Ug(e,"utf8"));return Array.isArray(t)?t.filter((r)=>typeof r==="string"):[]}catch{return[]}}function mE(e){return{...e,accessToken:""}}function hE(e){return Gg(mu(e))?.record}function Gg(e){if(!Ya(e))return;try{let t=JSON.parse(Ug(e,"utf8"));if(typeof t.identity!=="string")return;return{key:typeof t.key==="string"&&t.key!==""?t.key:void 0,record:{identity:t.identity,audience:typeof t.audience==="string"?t.audience:"",scopes:Array.isArray(t.scopes)?t.scopes.filter((r)=>typeof r==="string"):[],accessToken:typeof t.accessToken==="string"?t.accessToken:"",tokenType:typeof t.tokenType==="string"?t.tokenType:"Bearer",expiresAt:typeof t.expiresAt==="string"?t.expiresAt:""}}}catch{return}}function Kg(e,t){let r=Date.parse(t.expiresAt);return{key:e,identity:t.identity,audience:t.audience,scopes:t.scopes,expires_at:t.expiresAt,valid:Number.isFinite(r)&&r-Date.now()>0}}function Xa(e){return e.replace(/[^A-Za-z0-9._-]+/g,"_").slice(0,80)||"token"}async function gE(e){let t=await bE(e);if(!t)return;try{return JSON.parse(t)}catch{return}}async function vE(e,t){let r=JSON.stringify(t);if(process.platform==="darwin")return await zo(["security","delete-generic-password","-s",so,"-a",e]),await zo(["security","add-generic-password","-s",so,"-a",e,"-w",r,"-U"])===0;if(process.platform==="linux")return await xE(["secret-tool","store","--label",`devctl ${e}`,"service",so,"account",e],r)===0;if(process.platform==="win32"){let o=`Add-Type -AssemblyName System.Security; $bytes = [Convert]::FromBase64String('${Buffer.from(r,"utf8").toString("base64")}'); $enc = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $null, 'CurrentUser'); [IO.File]::WriteAllBytes($env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin', $enc)`;return await zo(["powershell","-NoProfile","-Command",o])===0}return!1}async function yE(e){if(process.platform==="darwin"){await zo(["security","delete-generic-password","-s",so,"-a",e]);return}if(process.platform==="linux"){await zo(["secret-tool","clear","service",so,"account",e]);return}if(process.platform==="win32")await zo(["powershell","-NoProfile","-Command",`Remove-Item -ErrorAction SilentlyContinue ($env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin')`])}async function bE(e){if(process.platform==="darwin"){let t=await pu(["security","find-generic-password","-s",so,"-a",e,"-w"]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="linux"){let t=await pu(["secret-tool","lookup","service",so,"account",e]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="win32"){let t=`$p = $env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin'; if (-not (Test-Path $p)) { exit 1 }; Add-Type -AssemblyName System.Security; $raw = [IO.File]::ReadAllBytes($p); $dec = [System.Security.Cryptography.ProtectedData]::Unprotect($raw, $null, 'CurrentUser'); [Text.Encoding]::UTF8.GetString($dec)`,r=await pu(["powershell","-NoProfile","-Command",t]);return r.code===0&&r.stdout!==""?r.stdout:void 0}return}var yu=3000;async function zo(e){try{return await hu({cmd:e,stdout:"ignore",stderr:"ignore",timeout:yu}).exited}catch{return 1}}async function xE(e,t){try{return await hu({cmd:e,stdin:new TextEncoder().encode(t),stdout:"ignore",stderr:"ignore",timeout:yu}).exited}catch{return 1}}async function pu(e){try{let t=hu({cmd:e,stdout:"pipe",stderr:"ignore",timeout:yu}),r=t.stdout?await new Response(t.stdout).text():"";return{code:await t.exited,stdout:r.trim()}}catch{return{code:1,stdout:""}}}te();te();var wE=3,SE=200;async function qg(e,t={}){let r=t.attempts??wE,n=t.backoffMs??SE,o=t.retry??kE,i=Error("retry exhausted");for(let s=0;s<r;s+=1)try{return await e()}catch(a){if(i=a,!o(a)||s===r-1)throw a;await CE(n*(s+1))}throw i}function kE(e){if(e instanceof Ve)return e.kind!==Y&&e.kind!==Vn;return!0}function CE(e){return new Promise((t)=>{setTimeout(t,e)})}var Qa={now:()=>new Date,isoNow:()=>new Date().toISOString(),unixMs:()=>Date.now()};var bu=300000,
|
|
2246
|
-
`)}function HE(e){return{identity:e.identity,audience:e.audience,scopes:e.scopes,accessToken:e.accessToken,tokenType:e.tokenType,expiresAt:e.expiresAt.toISOString()}}function jE(e){return{accessToken:e.accessToken,tokenType:e.tokenType||"Bearer",expiresAt:new Date(e.expiresAt),audience:e.audience,identity:e.identity,scopes:e.scopes}}function _n(){return[WE(),UE(),BE()]}function BE(){return{name:"user",accepts:(e,t)=>!e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(e.startsWith("sa:"))throw E(Fr,"user provider cannot mint service-account tokens");return zE(e,t,r)}}}function UE(){return{name:"service_account",accepts:(e,t)=>e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(!e.startsWith("sa:"))throw E(Fr,"service-account provider cannot mint user tokens");return GE(e,t,r)}}}function WE(){return{name:"iap",accepts:(e,t)=>t!=="",fetch:async(e,t,r,n)=>{if(t==="")throw E(Fr,"IAP audience is required");if(e.startsWith("sa:"))return KE(e,t,r);return qE(e,t,r,n)}}}async function GE(e,t,r){let n=e.slice(3);try{qo();let i=await new Vi({scopes:r.length>0?r:[Za]}).getClient(),s=new EE({sourceClient:i,targetPrincipal:n,targetScopes:r.length>0?r:[Za],lifetime:3600}),a=await s.getAccessToken(),c=typeof a==="string"?a:a?.token;if(!c)throw E(Fr,"empty impersonated token");return{accessToken:c,tokenType:"Bearer",expiresAt:zi(c,s),audience:t,identity:e,scopes:r}}catch(o){throw Vo(o)}}async function KE(e,t,r){let n=e.slice(3);try{qo();let i=await new Vi({scopes:[Za]}).getClient(),s=`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${encodeURIComponent(n)}:generateIdToken`,c=(await i.request({url:s,method:"POST",data:{audience:t,includeEmail:!0}})).data.token;if(!c)throw E(Fr,"empty impersonated IAP id token");return{accessToken:c,tokenType:"Bearer",expiresAt:zi(c),audience:t,identity:e,scopes:r}}catch(o){throw Vo(o)}}async function qE(e,t,r,n){try{if(qo(),n)return await VE(e,t,r,n);let s=await(await new Vi({scopes:r.length>0?r:[zg]}).getIdTokenClient(t)).idTokenProvider.fetchIdToken(t);return{accessToken:s,tokenType:"Bearer",expiresAt:zi(s),audience:t,identity:e,scopes:r}}catch(o){if(o instanceof Ve)throw o;throw Vo(o)}}async function VE(e,t,r,n){let o=n.refreshToken;if(!o){if(o=(await new Vi({scopes:r.length>0?r:[zg]}).getClient()).credentials.refresh_token??void 0,!o)throw Te(hn,"ADC has no refresh token for a custom IAP OAuth client","set auth.credentials (or proxy.credentials) to an authorized_user file whose refresh_token was issued by this client_id, or run `gcloud auth application-default login` with that client")}let s=await new TE({clientId:n.clientId,clientSecret:n.clientSecret,refreshToken:o}).fetchIdToken(t);if(!s)throw E(Fr,"empty IAP id token");return{accessToken:s,tokenType:"Bearer",expiresAt:zi(s),audience:t,identity:e,scopes:r}}async function zE(e,t,r){try{qo();let o=await new Vi({scopes:r.length>0?r:[Za]}).getClient(),i=await o.getAccessToken();if(!i.token)throw E(Fr,"empty access token");return{accessToken:i.token,tokenType:"Bearer",expiresAt:YE(o,i.token),audience:t,identity:e,scopes:r}}catch(n){throw Vo(n)}}function zi(e,t){if(t?.credentials?.expiry_date)return new Date(t.credentials.expiry_date);return JE(e)??new Date(Date.now()+AE)}function YE(e,t){return zi(t,e)}function JE(e){let t=e.split(".");if(t.length<2||!t[1])return;try{let r=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));if(typeof r.exp==="number")return new Date(r.exp*1000)}catch{return}return}function XE(e){if(e instanceof Ve)return e.kind!==Y&&e.kind!==Vn;let t=e instanceof Error?e.message.toLowerCase():String(e).toLowerCase();return t.includes("network")||t.includes("econnreset")||t.includes("etimedout")||t.includes("503")||t.includes("429")}te();jr();var Ji=4000,QE=8000;function Yo(e){let t=e?.tokens,r=()=>(t??=new ao(60000,_n(),void 0,Ja("file")),t);return{detectGoogle:io,hasCommand:uu,portAvailable:jo,hasLocalAdc:Ki,adcQuotaProject:du,mintToken:async(n,o,i)=>{await r().get(n,o,[],i)},probeServiceUsage:async(n,o)=>{let i=await Xi(r().get("user","",[]),Ji),s=`https://serviceusage.googleapis.com/v1/projects/${n}/services/${o}`,a=await fetch(s,{headers:{Authorization:`Bearer ${i.accessToken}`},signal:AbortSignal.timeout(Ji)});if(!a.ok)return!1;return(await a.json().catch(()=>{return}))?.state==="ENABLED"},containerRuntimeAvailable:async(n)=>{try{return await Bun.spawn({cmd:[n,"info"],stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited===0}catch{return!1}}}}var tc=Yo();function rc(e=Yo()){return{run:(t,r,n)=>ZE(t,e,r,n)}}async function ZE(e,t=tc,r,n){let o={checks:[],issues:0},i="Preparing diagnostics",s=()=>r?.({active:i,checks:[...o.checks]}),a=(p)=>{i=p,s()},c=(p)=>{if(o.checks.push(p),p.severity==="error")o.issues+=1;s()},l=[...new Set(Object.values(e.services).flatMap((p)=>p.container?[p.container.runtime||"docker"]:[]))];for(let p of l){a(`${p} container runtime`);let m=await t.hasCommand(p),h=m&&await(t.containerRuntimeAvailable??tc.containerRuntimeAvailable)(p);c(h?{name:`${p} container runtime`,severity:"ok",message:`${p} daemon reachable`}:{name:`${p} container runtime`,severity:"error",message:m?`${p} daemon is not reachable`:`${p} not found`,hint:`install and start ${p}`})}if(a("Google CLI installed"),await t.hasCommand("gcloud"))c({name:"Google CLI installed",severity:"ok",message:"gcloud found"});else c({name:"Google CLI installed",severity:Gi(e)?"error":"warn",message:"gcloud not installed",hint:"install the Google Cloud CLI from https://cloud.google.com/sdk/docs/install"});a("Google environment");let d=await t.detectGoogle(e.google.project_id);if(d.adcAvailable)c({name:"Google authentication available",severity:"ok",message:"Application Default Credentials found"});else c({name:"Google authentication available",severity:Gi(e)?"error":"warn",message:"ADC unavailable",hint:"run `gcloud auth application-default login`"});if(d.projectID!=="")c({name:"Project configured",severity:"ok",message:`${d.projectID} (source: ${d.projectSource})`});else c({name:"Project configured",severity:Gi(e)?"error":"warn",message:"no Google project configured",hint:"set google.project_id in .devctl/config.yaml"});if(Gi(e)||Object.values(e.services).some((p)=>p.capabilities.includes("google")||p.identity.type!=="")){a("Live Google access");let p=!0,m=(h)=>{if(p)c(h)};try{await Xi((async()=>{await tT(e,m,t,d.userEmail),await eT(e,m,t)})(),t.liveDeadlineMs??QE)}catch(h){m({name:"Live Google probes",severity:"warn",message:L(h),hint:"network or timeout \u2014 retry when online"})}finally{p=!1}}for(let p of e.doctor.tools){let m=p.command||p.name;if(a(`${p.name} installed`),await t.hasCommand(m))c({name:`${p.name} installed`,severity:"ok",message:`${m} found`});else c({name:`${p.name} installed`,severity:"error",message:`${m} not found`,hint:`install ${p.name} and ensure it is on PATH`})}if(a("Repository configuration"),n?.repositoryConfigError!==void 0)if(n.repositoryConfigError==="")c({name:"Repository configuration",severity:"ok",message:"valid"});else c({name:"Repository configuration",severity:"error",message:n.repositoryConfigError});else try{let p=Zn(e);if(p.length>0)c({name:"Repository configuration",severity:"error",message:p.join("; ")});else c({name:"Repository configuration",severity:"ok",message:"valid"})}catch(p){c({name:"Repository configuration",severity:"error",message:L(p)})}let f={};for(let[p,m]of Object.entries(e.services))for(let h of m.ports){if(h.auto)continue;let v=`Port ${h.value}`;if(a(v),f[h.value])c({name:v,severity:"error",message:`configured on both ${f[h.value]} and ${p}`});else{f[h.value]=p;let b=n?.services?.[p],g=Boolean(m.container&&b&&["STARTING","RUNNING","HEALTHY","UNHEALTHY"].includes(b.state??""));if(Boolean(b&&(b.pid>0||g)&&Object.values(b.ports).includes(h.value)))c({name:v,severity:"ok",message:`in use by running service ${p}`});else if(await t.portAvailable(h.value))c({name:v,severity:"ok",message:"available"});else c(await Jg(v,h.value,`services.${p}.ports`))}}if(e.proxy.listen.port>0){let p=`Port ${e.proxy.listen.port}`;if(a(p),n?.proxyRunning)c({name:p,severity:"ok",message:"in use by the running proxy"});else if(await t.portAvailable(e.proxy.listen.port))c({name:p,severity:"ok",message:"proxy listen port available"});else c(await Jg(p,e.proxy.listen.port,"proxy.listen.port"))}return i="Diagnostics complete",s(),o}async function eT(e,t,r){let n=e.google.project_id,o=r.hasLocalAdc??Ki;if(n===""||!o())return;let i=r.probeServiceUsage??tc.probeServiceUsage,s=[{name:"IAM Credentials API",service:"iamcredentials.googleapis.com"},{name:"Resource Manager API",service:"cloudresourcemanager.googleapis.com"},{name:"IAP API",service:"iap.googleapis.com"}];(await Promise.allSettled(s.map((c)=>Xi(i(n,c.service),Ji)))).forEach((c,l)=>{let d=s[l];if(!d)return;if(c.status==="fulfilled"){t({name:d.name,severity:c.value?"ok":"warn",message:c.value?"enabled":"not enabled or unreachable",hint:c.value?void 0:`enable ${d.service} in the Google Cloud console \u2014 doctor never auto-enables APIs`});return}t({name:d.name,severity:"warn",message:L(c.reason),hint:`enable ${d.service} in the Google Cloud console \u2014 doctor never auto-enables APIs`})})}async function tT(e,t,r,n){let o=Hr(e),i=e.proxy.routes.filter((u)=>u.auth.type.toLowerCase()==="iap"),s=i.filter((u)=>u.auth.audience.trim()!=="");for(let u of i)if(u.auth.audience.trim()==="")t({name:`IAP audience ${u.name}`,severity:"error",message:"missing audience",hint:"set auth.audience to the IAP OAuth client ID"});if(o.length===0&&s.length===0)return;if(!(r.hasLocalAdc??Ki)())return;let c=r.mintToken??tc.mintToken,l=o.map(async(u)=>{try{await Xi(c(`sa:${u}`,""),Ji),t({name:`Impersonate ${u}`,severity:"ok",message:"token minted"})}catch(f){let p=(r.adcQuotaProject??du)(),m=n||"the ADC principal",h=p===""?"unknown":p,v=Yg(`Impersonate ${u}`,f,`grant roles/iam.serviceAccountTokenCreator to ${m} on ${u}; ADC quota project: ${h}`);if(p!==""&&v.message.toLowerCase().includes("api is not enabled"))v.message=`IAM Credentials API is disabled in ADC quota project ${p}`,v.hint=`enable iamcredentials.googleapis.com in ${p}, or set the ADC quota project to ${e.google.project_id}`;else if(v.message.toLowerCase()==="google authentication failed")v.message=`access-token mint failed: ${m} \u2192 ${u}`,v.hint=`verify roles/iam.serviceAccountTokenCreator for ${m} on this service account; ADC quota project: ${h}`;t(v)}}),d=s.map(async(u)=>{let f=no(u.auth).kind===ro?`sa:${no(u.auth).serviceAccount}`:"user";try{let p=ec(u.auth);await Xi(c(f,u.auth.audience,p),Ji),t({name:`IAP ${u.name}`,severity:"ok",message:"id token minted"})}catch(p){t(Yg(`IAP ${u.name}`,p,"check IAP OAuth client ID and ADC"))}});await Promise.all([...l,...d])}function Yg(e,t,r){let n=L(t),o=n.toLowerCase();if(o.includes("timeout")||o.includes("network")||o.includes("econnrefused")||o.includes("enotfound"))return{name:e,severity:"warn",message:n,hint:"network or timeout \u2014 retry when online"};if(t instanceof Ve&&t.hint!==""){let i=`${t.kind}: `,s=t.message.startsWith(i)?t.message.slice(i.length):t.message;return{name:e,severity:"error",message:s,hint:t.hint}}return{name:e,severity:"error",message:n,hint:r}}function Xi(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("timeout")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}async function Jg(e,t,r){let n=await zn(t);if(!n)return{name:e,severity:"error",message:"already in use",hint:`stop the process using port ${t} or change ${r}`};if(n.pid===process.pid)return{name:e,severity:"error",message:`in use by this TUI (${n.command} pid ${n.pid})`,hint:"stop the proxy from the proxy screen (x), then rerun doctor"};return{name:e,severity:"error",message:`in use by ${n.command} (pid ${n.pid})`,hint:"enter stop that process after a confirmation",action:{kind:"free-port",holder:n}}}function Xg(e){let t=[`${er()} doctor`,""];for(let r of e.checks){let n=r.severity==="error"?"\u2717":r.severity==="warn"?"!":"\u2713";if(t.push(`${n} ${r.name}`),r.severity!=="ok"){if(t.push(` ${r.message}`),r.hint)t.push(` \u2192 ${r.hint}`)}}return t.push("",`${e.issues} issue(s) found.`),t.join(`
|
|
2245
|
+
`)}function vu(){let e=qi(kr(),gu);if(!Ya(e))return[];try{let t=JSON.parse(Ug(e,"utf8"));return Array.isArray(t)?t.filter((r)=>typeof r==="string"):[]}catch{return[]}}function mE(e){return{...e,accessToken:""}}function hE(e){return Gg(mu(e))?.record}function Gg(e){if(!Ya(e))return;try{let t=JSON.parse(Ug(e,"utf8"));if(typeof t.identity!=="string")return;return{key:typeof t.key==="string"&&t.key!==""?t.key:void 0,record:{identity:t.identity,audience:typeof t.audience==="string"?t.audience:"",scopes:Array.isArray(t.scopes)?t.scopes.filter((r)=>typeof r==="string"):[],accessToken:typeof t.accessToken==="string"?t.accessToken:"",tokenType:typeof t.tokenType==="string"?t.tokenType:"Bearer",expiresAt:typeof t.expiresAt==="string"?t.expiresAt:""}}}catch{return}}function Kg(e,t){let r=Date.parse(t.expiresAt);return{key:e,identity:t.identity,audience:t.audience,scopes:t.scopes,expires_at:t.expiresAt,valid:Number.isFinite(r)&&r-Date.now()>0}}function Xa(e){return e.replace(/[^A-Za-z0-9._-]+/g,"_").slice(0,80)||"token"}async function gE(e){let t=await bE(e);if(!t)return;try{return JSON.parse(t)}catch{return}}async function vE(e,t){let r=JSON.stringify(t);if(process.platform==="darwin")return await zo(["security","delete-generic-password","-s",so,"-a",e]),await zo(["security","add-generic-password","-s",so,"-a",e,"-w",r,"-U"])===0;if(process.platform==="linux")return await xE(["secret-tool","store","--label",`devctl ${e}`,"service",so,"account",e],r)===0;if(process.platform==="win32"){let o=`Add-Type -AssemblyName System.Security; $bytes = [Convert]::FromBase64String('${Buffer.from(r,"utf8").toString("base64")}'); $enc = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $null, 'CurrentUser'); [IO.File]::WriteAllBytes($env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin', $enc)`;return await zo(["powershell","-NoProfile","-Command",o])===0}return!1}async function yE(e){if(process.platform==="darwin"){await zo(["security","delete-generic-password","-s",so,"-a",e]);return}if(process.platform==="linux"){await zo(["secret-tool","clear","service",so,"account",e]);return}if(process.platform==="win32")await zo(["powershell","-NoProfile","-Command",`Remove-Item -ErrorAction SilentlyContinue ($env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin')`])}async function bE(e){if(process.platform==="darwin"){let t=await pu(["security","find-generic-password","-s",so,"-a",e,"-w"]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="linux"){let t=await pu(["secret-tool","lookup","service",so,"account",e]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="win32"){let t=`$p = $env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin'; if (-not (Test-Path $p)) { exit 1 }; Add-Type -AssemblyName System.Security; $raw = [IO.File]::ReadAllBytes($p); $dec = [System.Security.Cryptography.ProtectedData]::Unprotect($raw, $null, 'CurrentUser'); [Text.Encoding]::UTF8.GetString($dec)`,r=await pu(["powershell","-NoProfile","-Command",t]);return r.code===0&&r.stdout!==""?r.stdout:void 0}return}var yu=3000;async function zo(e){try{return await hu({cmd:e,stdout:"ignore",stderr:"ignore",timeout:yu}).exited}catch{return 1}}async function xE(e,t){try{return await hu({cmd:e,stdin:new TextEncoder().encode(t),stdout:"ignore",stderr:"ignore",timeout:yu}).exited}catch{return 1}}async function pu(e){try{let t=hu({cmd:e,stdout:"pipe",stderr:"ignore",timeout:yu}),r=t.stdout?await new Response(t.stdout).text():"";return{code:await t.exited,stdout:r.trim()}}catch{return{code:1,stdout:""}}}te();te();var wE=3,SE=200;async function qg(e,t={}){let r=t.attempts??wE,n=t.backoffMs??SE,o=t.retry??kE,i=Error("retry exhausted");for(let s=0;s<r;s+=1)try{return await e()}catch(a){if(i=a,!o(a)||s===r-1)throw a;await CE(n*(s+1))}throw i}function kE(e){if(e instanceof Ve)return e.kind!==Y&&e.kind!==Vn;return!0}function CE(e){return new Promise((t)=>{setTimeout(t,e)})}var Qa={now:()=>new Date,isoNow:()=>new Date().toISOString(),unixMs:()=>Date.now()};var bu=300000,TE=3000000,AE=3,LE=200,Za="https://www.googleapis.com/auth/cloud-platform",zg="https://www.googleapis.com/auth/userinfo.email",DE="https://oauth2.googleapis.com/token";function Vg(e,t,r,n){let o=n??"",i=`${e}|${t}|${r.join(",")}`;return o===""?i:`${i}|oauth:${o}`}function IE(e){let t;try{t=_E(e,"utf8")}catch{throw _(Y,`IAP credentials file not found: ${e}`)}let r;try{r=JSON.parse(t)}catch{throw _(Y,`IAP credentials file is not valid JSON: ${e}`)}if(typeof r!=="object"||r===null)throw _(Y,`IAP credentials file is malformed: ${e}`);let n=r,o=typeof n.refresh_token==="string"?n.refresh_token:"";if(o==="")throw _(Y,`IAP credentials file has no refresh_token: ${e}`);let i=typeof n.client_id==="string"?n.client_id:"";if(i==="")throw _(Y,`IAP credentials file has no client_id: ${e}`);return{clientId:i,clientSecret:typeof n.client_secret==="string"?n.client_secret:"",refreshToken:o}}function ME(e,t=process.env){let r=(e.client_id??"").trim();if(r==="")return;let n=(e.client_secret??"").trim(),{value:o,missing:i}=wg(n,t),s=(e.credentials??"").trim();if(s!==""){let a=IE(s);if(a.clientId!==r)throw _(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 _(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 _(Y,a?`IAP client_secret env ${a} is empty`:`IAP client_id ${r} requires client_secret`)}return{clientId:r,clientSecret:o}}function ec(e,t=process.env){let r=(e.client_id??"").trim();if(r==="")return;return{clientId:r,resolve:()=>{let n=ME(e,t);if(!n)throw _(Y,`IAP client_id ${r} requires client_secret`);return n}}}function OE(e,t=bu,r=Date.now()){if(e.accessToken==="")return!1;return e.expiresAt.getTime()-r>=t}function $E(e,t=bu,r=Date.now()){if(e.accessToken==="")return!0;let n=e.expiresAt.getTime()-r;return n>0&&n<t}class ao{cache=new Map;inflight=new Map;providers;thresholdMs;bus;store;clock;constructor(e,t,r,n,o=Qa){this.thresholdMs=e>0?e:bu,this.providers=[...t],this.bus=r,this.store=n??Ja(process.env.DEVCTL_CREDENTIAL_BACKEND==="file"?"file":void 0),this.clock=o}replaceProviders(e){this.providers.splice(0,this.providers.length,...e)}isValid(e){return OE(e,this.thresholdMs,this.clock.unixMs())}expiresSoon(e){return $E(e,this.thresholdMs,this.clock.unixMs())}invalidate(e){if(e===void 0||e===""){this.cache.clear(),this.clearStore();return}this.cache.delete(e),this.store.delete(e)}async listStatus(){return this.store.list()}storeBackend(){return this.store.backend}async get(e,t,r,n){let o=Vg(e,t,r,n?.clientId),i=this.cache.get(o)??await this.loadStored(o);if(i&&this.isValid(i))return this.cache.set(o,i),i;return this.refresh(e,t,r,n)}async refresh(e,t,r,n){let o=Vg(e,t,r,n?.clientId),i=this.inflight.get(o);if(i)return i;let s=this.refreshOnce(o,e,t,r,n);this.inflight.set(o,s);try{return await s}finally{this.inflight.delete(o)}}async refreshOnce(e,t,r,n,o){let i=o?.resolve(),s=this.providers.filter((c)=>!c.accepts||c.accepts(t,r,n,i));if(s.length===0)throw _(Pr,`no token provider accepts identity ${t}`);let a=_(Pr,"no token provider available");for(let c of s)try{let l=await qg(()=>c.fetch(t,r,n,i),{attempts:AE,backoffMs:LE,retry:QE});return this.cache.set(e,l),NE(e,l),await this.store.set(e,HE(l)),this.bus?.publish(Se(Mo,"",{identity:l.identity,audience:l.audience})),l}catch(l){a=l instanceof Error?l:Error(String(l))}throw this.bus?.publish(Se(Gn,"",{identity:t,audience:r,error:L(a)})),a}async loadStored(e){let t=await this.store.get(e);if(!t||t.accessToken==="")return;return jE(t)}async clearStore(){let e=await this.store.list();await Promise.all(e.map((t)=>this.store.delete(t.key)))}}function FE(e){let t=PE("sha256").update(e).digest("hex").slice(0,16);return RE(kr(),`${t}.json`)}function NE(e,t){let r={identity:t.identity,audience:t.audience,expires_at:t.expiresAt.toISOString(),scopes:t.scopes};Cr(FE(e),`${JSON.stringify(r)}
|
|
2246
|
+
`)}function HE(e){return{identity:e.identity,audience:e.audience,scopes:e.scopes,accessToken:e.accessToken,tokenType:e.tokenType,expiresAt:e.expiresAt.toISOString()}}function jE(e){return{accessToken:e.accessToken,tokenType:e.tokenType||"Bearer",expiresAt:new Date(e.expiresAt),audience:e.audience,identity:e.identity,scopes:e.scopes}}function _n(){return[WE(),UE(),BE()]}function BE(){return{name:"user",accepts:(e,t)=>!e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(e.startsWith("sa:"))throw _(Pr,"user provider cannot mint service-account tokens");return YE(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 _(Pr,"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 _(Pr,"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 _(Pr,"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 _(Pr,"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 i=await fetch(DE,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:zE(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 _(Pr,"IAP token endpoint returned a non-JSON response")}let c=a.id_token;if(!c)throw _(Pr,"empty IAP id token");return{accessToken:c,tokenType:"Bearer",expiresAt:zi(c),audience:t,identity:e,scopes:r}}function zE(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 YE(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 _(Pr,"empty access token");return{accessToken:i.token,tokenType:"Bearer",expiresAt:JE(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 XE(e)??new Date(Date.now()+TE)}function JE(e,t){return zi(t,e)}function XE(e){let t=e.split(".");if(t.length<2||!t[1])return;try{let r=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));if(typeof r.exp==="number")return new Date(r.exp*1000)}catch{return}return}function QE(e){if(e instanceof Ve)return e.kind!==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,ZE=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)=>eT(t,e,r,n)}}async function eT(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 rT(e,m,t,d.userEmail),await tT(e,m,t)})(),t.liveDeadlineMs??ZE)}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 tT(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 rT(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(`
|
|
2247
2247
|
`)+`
|
|
2248
|
-
`}At();class ov{start;constructor(e){this.start=e}execute(e){return this.start({...e,profile:e.profile?ur(e.profile):void 0,services:e.services?.map((t)=>wu(t))})}}class iv{stop;constructor(e){this.stop=e}execute(e){return this.stop(e.map((t)=>wu(t)))}}class sv{restart;constructor(e){this.restart=e}execute(e,t){return this.restart(e.map((r)=>wu(r)),t)}}class av{start;constructor(e){this.start=e}execute(e,t){return this.start.execute({profile:e,client_env:t})}}class cv{host;constructor(e){this.host=e}execute(){return this.host.startProxy()}}class lv{host;constructor(e){this.host=e}execute(){return this.host.stopProxy()}}class dv{host;constructor(e){this.host=e}execute(){return this.host.reload()}}class uv{host;constructor(e){this.host=e}execute(){return this.host.refreshIdentity({probeServiceAccounts:!0})}}class cc{runner;constructor(e){this.runner=e}execute(e,t,r){return this.runner.run(e,t,r)}}class lc{execute(e,t,r){return nc(e,t,r)}}class dc{execute(e,t){return oc(e,t)}}class pv{host;constructor(e){this.host=e}execute(){return this.host.snapshot()}}class uc{execute(e,t){return ac(e,t)}}function wu(e){return Qg(e)}function fv(e,t,r){let n=r??e,o=new ov((i)=>n.start(i));return{startService:o,stopService:new iv((i)=>n.stop(i)),restartService:new sv((i,s)=>n.restart(i,s)),startProfile:new av(o),startProxy:new cv(e),stopProxy:new lv(e),reloadConfig:new dv(e),refreshIdentity:new uv(e),runDoctor:new cc(t),getServiceStatus:new pv(e),getStartupPlan:new lc,getShutdownPlan:new dc,resolveStart:new uc}}import{createConnection as
|
|
2249
|
-
`);this.buf=r.pop()??"";for(let n of r){if(n.trim()==="")continue;this.onLine(n)}}),e.on("error",(t)=>this.rejectPending(t instanceof Error?t:Error(String(t)))),e.on("close",()=>this.rejectPending(Error("supervisor connection closed")))}onLine(e){let t;try{t=JSON.parse(e)}catch{return}if(t.event){for(let n of this.listeners)n(t.event);return}let r=t.id?this.pending.get(t.id):void 0;if(!r)return;if(this.pending.delete(t.id??""),clearTimeout(r.timer),t.error){r.reject(fh({error:t.error,kind:t.kind,hint:t.hint,service:t.service}));return}r.resolve(t.result)}onEvent(e){return this.listeners.push(e),()=>{let t=this.listeners.indexOf(e);if(t>=0)this.listeners.splice(t,1)}}call(e,t,r=
|
|
2250
|
-
`)})}close(){this.rejectPending(Error("supervisor connection closed")),this.socket.destroy()}rejectPending(e){for(let t of this.pending.values())clearTimeout(t.timer),t.reject(e);this.pending.clear()}}function wv(e,t){let r=ea(e),n=Date.now()+t;return new Promise((o,i)=>{let s=()=>{let a=
|
|
2251
|
-
`)}async function kv(e,t,r=""){let n=pc(e,t,r);if(!n)throw Te(lt,"no devctl configuration found","run `devctl setup`, create a .devctl/config.yaml in the repository root, or pass --repo");let o=await uo(n.repoRoot);return ns(o),{repoRoot:n.repoRoot,client:o}}async function Cv(e,t,r,n){let o=n?.allowMissingConfig===!0?to(e,t):
|
|
2252
|
-
`).find((i)=>i.startsWith("n"));if(r&&r.length>1)return r.slice(1).trim();let n=await os(["pwdx",String(e)]),o=n.indexOf(":");if(o>=0)return n.slice(o+1).trim();return""}async function os(e){try{let t=
|
|
2253
|
-
`)){let o=n.trim();if(o==="")continue;let[i,s,a]=o.split(/\s+/),c=Number(i),l=Number(s),d=Number(a);if(Number.isFinite(c)&&Number.isFinite(l)&&Number.isFinite(d))t.set(c,{pid:c,cpuPercent:l,memoryKB:d})}return t}function Uv(e,t){if(e.length===0||t==="")return!1;let r=e.join(" ");if(t.includes(r))return!0;let o=(e[0]??"").split(/[/\\]/).pop()??"";if(o!==""&&t.includes(o))return!0;let i=e[e.length-1]??"";return i!==""&&t.includes(i)}var{spawn:Wv}=globalThis.Bun;var
|
|
2254
|
-
`);i=a.pop()??"";for(let c of a)r(t,c.replace(/\r$/,""))}if(i!=="")r(t,i)}var
|
|
2248
|
+
`}At();class ov{start;constructor(e){this.start=e}execute(e){return this.start({...e,profile:e.profile?ur(e.profile):void 0,services:e.services?.map((t)=>wu(t))})}}class iv{stop;constructor(e){this.stop=e}execute(e){return this.stop(e.map((t)=>wu(t)))}}class sv{restart;constructor(e){this.restart=e}execute(e,t){return this.restart(e.map((r)=>wu(r)),t)}}class av{start;constructor(e){this.start=e}execute(e,t){return this.start.execute({profile:e,client_env:t})}}class cv{host;constructor(e){this.host=e}execute(){return this.host.startProxy()}}class lv{host;constructor(e){this.host=e}execute(){return this.host.stopProxy()}}class dv{host;constructor(e){this.host=e}execute(){return this.host.reload()}}class uv{host;constructor(e){this.host=e}execute(){return this.host.refreshIdentity({probeServiceAccounts:!0})}}class cc{runner;constructor(e){this.runner=e}execute(e,t,r){return this.runner.run(e,t,r)}}class lc{execute(e,t,r){return nc(e,t,r)}}class dc{execute(e,t){return oc(e,t)}}class pv{host;constructor(e){this.host=e}execute(){return this.host.snapshot()}}class uc{execute(e,t){return ac(e,t)}}function wu(e){return Qg(e)}function fv(e,t,r){let n=r??e,o=new ov((i)=>n.start(i));return{startService:o,stopService:new iv((i)=>n.stop(i)),restartService:new sv((i,s)=>n.restart(i,s)),startProfile:new av(o),startProxy:new cv(e),stopProxy:new lv(e),reloadConfig:new dv(e),refreshIdentity:new uv(e),runDoctor:new cc(t),getServiceStatus:new pv(e),getStartupPlan:new lc,getShutdownPlan:new dc,resolveStart:new uc}}import{createConnection as vT}from"net";var{spawn:yT}=globalThis.Bun;import{readdirSync as iT}from"fs";import{join as mv,resolve as Su,sep as ku}from"path";function sT(e){let t=mv(cr(),"state"),r;try{r=iT(t)}catch{return}let n=hv(Su(e)),o;for(let i of r){let s=nd(mv(t,i,"state.json"));if(!s)continue;let a=hv(Su(s.repo_root));if(!aT(a,n))continue;if(!o||a.length>o.length)o=a}return o}function pc(e,t,r=""){if(t!=="")return{repoRoot:Su(t),source:"explicit"};let n=e===""?process.cwd():e;try{let{repoRoot:i}=Qn(n,r);return{repoRoot:i,source:"config"}}catch{if(r!=="")return}let o=sT(n);return o?{repoRoot:o,source:"state-scan"}:void 0}function hv(e){let t=e.length>1&&e.endsWith(ku)?e.slice(0,-1):e;return process.platform==="win32"?t.toLowerCase():t}function aT(e,t){return t===e||t.startsWith(e.endsWith(ku)?e:`${e}${ku}`)}import{existsSync as vv,readFileSync as yv}from"fs";import{isAbsolute as cT,join as Cu}from"path";import{parse as lT}from"dotenv";te();var es=["process","profile","dotenv","generated","keychain","secret_manager","defaults","vars","runtime"],dT=/^projects\/[^/]+\/secrets\/[^/]+(?:\/versions\/[^/]+)?$/,uT=["process","defaults","vars","runtime"];function pT(){return{name:"dotenv",load:(e)=>{let t=gv(e.repoRoot,e.profile);if(e.workDir!=="")Object.assign(t,gv(e.workDir,e.profile));return t}}}function fT(e){let t=e?.environment.sources??[];if(t.length===0)return[...es];let r=new Set([...uT,...t]),n=new Set(es),o=t.filter((s)=>!n.has(s)),i=[];for(let s of es){if(s==="defaults")i.push(...o);if(r.has(s))i.push(s)}return i}async function ts(e,t){let r={},n=t.serviceCfg.working_dir;if(n!==""&&!cT(n))n=Cu(e,n);let o={repoRoot:e,profile:t.profile,service:t.service,serviceCfg:t.serviceCfg,workDir:n,cfg:t.cfg},i=mT(t),s=t.userEmail??"",a={process:t.includeProcess===!1?{}:t.clientEnv??Xo(),profile:Zi(t.profileEnv,t.cfg,i,s),dotenv:Zi(await pT().load(o),t.cfg,i,s),generated:{},keychain:t.sourceValues?.keychain??hT(o),secret_manager:t.sourceValues?.secret_manager??await gT(o,t.fetchSecret),defaults:Zi(t.serviceCfg.environment.defaults,t.cfg,i,s),vars:Zi(t.serviceCfg.environment.vars,t.cfg,i,s),runtime:t.runtime};for(let c of fT(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,Zi(await l.load(o),t.cfg,i,s))}for(let c of t.serviceCfg.environment.required)if((r[c]??"").trim()==="")throw _(Y,`service ${t.service} missing required environment variable ${c}`);return r}function mT(e){let t={};if(e.cfg)for(let[r,n]of Object.entries(e.cfg.services)){let o={};for(let i of n.ports)if(!i.auto)o[i.name]=i.value;t[r]=o}if(e.assignedPorts)t[e.service]=e.assignedPorts;return t}function Zi(e,t,r,n=""){if(!t||Object.keys(e).length===0)return e;return eu(e,t,r,n)}function hT(e){let t=e.cfg?.environment.sources??[];if(t.length>0&&!t.includes("keychain"))return{};let r={},n=new Set([...Object.keys(e.serviceCfg.environment.defaults),...Object.keys(e.serviceCfg.environment.vars),...e.serviceCfg.environment.required]);for(let o of n){let i=Cu(kr(),"env",o);if(!vv(i))continue;try{r[o]=yv(i,"utf8").replace(/\n$/,"")}catch(s){throw Pe(Y,`unable to read keychain env ${o}`,s)}}return r}async function gT(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(!dT.test(s))throw _(Y,`environment.secrets.${i} is not a Secret Manager resource`);if(!t)throw _(Y,"environment source secret_manager is not configured \u2014 set credentials or remove it from environment.sources");o[i]=await t(s)}return o}function gv(e,t){let r={},n=[".env",".env.development",".env.local"];if(t!=="")n.push(`.env.${t}`);for(let o of n){let i=Cu(e,o);if(!vv(i))continue;try{let s=lT(yv(i));Object.assign(r,s)}catch(s){throw Pe(Y,`unable to read ${i}`,s)}}return r}function Xo(){let e={};for(let[t,r]of Object.entries(process.env))if(r!==void 0)e[t]=r;return e}function rs(e,t,r,n,o,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 Qo(e){return{...e}}te();var bT=50,xT=8000,wT=15000,ST=200,kT=30000,bv=86400000,CT=new Set(["logs","logs_page","logs_stats"]);function PT(e){if(e.legacy)return"attached daemon predates the client/daemon compatibility handshake";return`attached daemon speaks RPC protocol ${e.daemonProtocol??"unknown"}; this client speaks ${Yi}`}function _T(e){if(e.compatible&&e.daemonVersion!==void 0&&e.daemonVersion!==jt)return`attached daemon is devctl ${e.daemonVersion}; this client is ${jt} (run \`devctl down\` then start again to update it)`;return}function Pu(e,t){if(!e.compat.compatible&&!CT.has(t))throw Te(lt,PT(e.compat),"run `devctl down` to stop it, then start again")}class xv{socket;buf="";pending=new Map;listeners=[];nextID=0;compat={compatible:!0,legacy:!1};session="";constructor(e){this.socket=e,e.on("data",(t)=>{this.buf+=t.toString("utf8");let r=this.buf.split(`
|
|
2249
|
+
`);this.buf=r.pop()??"";for(let n of r){if(n.trim()==="")continue;this.onLine(n)}}),e.on("error",(t)=>this.rejectPending(t instanceof Error?t:Error(String(t)))),e.on("close",()=>this.rejectPending(Error("supervisor connection closed")))}onLine(e){let t;try{t=JSON.parse(e)}catch{return}if(t.event){for(let n of this.listeners)n(t.event);return}let r=t.id?this.pending.get(t.id):void 0;if(!r)return;if(this.pending.delete(t.id??""),clearTimeout(r.timer),t.error){r.reject(fh({error:t.error,kind:t.kind,hint:t.hint,service:t.service}));return}r.resolve(t.result)}onEvent(e){return this.listeners.push(e),()=>{let t=this.listeners.indexOf(e);if(t>=0)this.listeners.splice(t,1)}}call(e,t,r=kT){this.nextID+=1;let n=String(this.nextID);return new Promise((o,i)=>{let s=setTimeout(()=>{this.pending.delete(n),i(Error(`${e} timed out after ${r}ms`))},r);this.pending.set(n,{resolve:o,reject:i,timer:s}),this.socket.write(JSON.stringify({id:n,method:e,params:t})+`
|
|
2250
|
+
`)})}close(){this.rejectPending(Error("supervisor connection closed")),this.socket.destroy()}rejectPending(e){for(let t of this.pending.values())clearTimeout(t.timer),t.reject(e);this.pending.clear()}}function wv(e,t){let r=ea(e),n=Date.now()+t;return new Promise((o,i)=>{let s=()=>{let a=vT(r);a.once("connect",()=>{let c=new xv(a);RT(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,bT)})};s()})}async function RT(e){let t;try{t=await e.call("ping",null,xT)}catch{return}let r=ET(t)?t:{};if(typeof r.session==="string")e.session=r.session;let n=typeof r.protocol==="number"?r.protocol:void 0,o=typeof r.version==="string"?r.version:void 0;if(n===void 0){e.compat={compatible:!1,legacy:!0,daemonVersion:o};return}e.compat={compatible:n===Yi,legacy:!1,daemonVersion:o,daemonProtocol:n}}function ET(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}async function uo(e){try{return await wv(e,ST)}catch{return}}function TT(e,t,r,n){return r?[e,...n]:[e,t,...n]}async function Sv(e,t){let r=await uo(e);if(r)return r;_m(e);let n=Ri(e),o=TT(process.execPath,process.argv[1]??"",Bun.isStandaloneExecutable===!0,["--config",t,"_supervisor","--repo",e]),i=yT({cmd:o,env:{...process.env},stdout:"ignore",stderr:Bun.file(n),stdin:"ignore",detached:!0});try{let s=await LT(e,wT,i.exited);return i.unref(),s}catch{throw await DT(i),Te(lt,"supervisor failed to start",`see ${n} for details`)}}var AT=1000;function LT(e,t,r){return new Promise((n,o)=>{let i=!1,s=(a)=>{if(i)return;i=!0,a()};r.then((a)=>{s(()=>o(Error(`supervisor exited (code ${a}) before accepting connections`)))}),wv(e,t).then((a)=>s(()=>n(a)),(a)=>s(()=>o(a)))})}async function DT(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,AT)})])}class fc{cfg;client;previousPersisted;constructor(e){this.cfg=e}async start(e){return await this.call("start",{...e,client_env:Xo()})}async stop(e){await this.call("stop",{services:e})}async restart(e,t){await this.call("restart",{services:e,cascade:t===!0,client_env:Xo()})}async runTask(e){return await this.call("run_task",{name:e,client_env:Xo()},bv)}async execService(e,t,r=!1){return await this.call("exec",{service:e,command:t,print_env:r,client_env:Xo()},bv)}async status(){return await this.call("status",null)}async refreshAuth(){return await this.call("auth_refresh",null)}async configSnapshot(){return await this.call("config_snapshot",null)}async logs(e){return(await this.call("logs",e)).events??[]}async logsPage(e){return await this.call("logs_page",e)}async logsStats(e){return await this.call("logs_stats",e)}async proxyStart(){await this.call("proxy_start",null)}async proxyStop(){await this.call("proxy_stop",null)}async mcpStart(e){await this.call("mcp_start",e??null)}async mcpStop(){await this.call("mcp_stop",null)}async mcpSetTools(e){return(await this.call("mcp_set_tools",{disabled:[...e]})).disabled_tools??[]}async reload(){return await this.call("reload",null)}async invalidateAuth(){await this.call("auth_invalidate",null)}onEvent(e){if(this.client)return this.client.onEvent(e);return()=>{return}}async shutdown(e){if(!this.client)return;let t=Math.max(5000,this.cfg.shutdown.grace_seconds*1000+2000);await this.client.call("shutdown",{stop_services:e.stopServices},t)}async close(e){if(!this.client)return;try{if(e?.shutdownSupervisor===!0&&e.detach!==!0)await this.shutdown({stopServices:!0})}finally{this.client.close()}}async call(e,t,r){if(!this.client)throw Pe(lt,"supervisor is not running",Error("no client"));return Pu(this.client,e),this.client.call(e,t,r)}}function ns(e){let t=e&&_T(e.compat);if(t)process.stderr.write(`warning: ${t}
|
|
2251
|
+
`)}async function kv(e,t,r=""){let n=pc(e,t,r);if(!n)throw Te(lt,"no devctl configuration found","run `devctl setup`, create a .devctl/config.yaml in the repository root, or pass --repo");let o=await uo(n.repoRoot);return ns(o),{repoRoot:n.repoRoot,client:o}}async function Cv(e,t,r,n){let o=n?.allowMissingConfig===!0?to(e,t):on(e,t),i=new fc(o);if(!r)return i.client=await uo(o.repoRoot),ns(i.client),i;return i.client=await Sv(o.repoRoot,o.configPath),ns(i.client),i}async function Pv(e){let t=new fc(Yn());return t.client=e,ns(t.client),t.cfg=await t.configSnapshot(),t}async function _v(e,t){let r=pc(e,"",t),n=r?await uo(r.repoRoot):void 0;if(!n)throw Te(lt,"supervisor is not running","run `devctl start` before `devctl attach`");return Pv(n)}async function Rv(e,t){let r=pc(e,"",t),n=r?await uo(r.repoRoot):void 0;if(n)return Pv(n);let o=on(e,t),i=new fc(o),s=mn(o.repoRoot);if(i.client=await Sv(o.repoRoot,o.configPath),ns(i.client),i.cfg=await i.configSnapshot(),s&&s.processes.length>0)i.previousPersisted=s;return i}import{realpathSync as MT}from"fs";var OT="https://api.github.com/repos/amr-m-abdelgawad/devctl/releases/latest";function $T(){let e=Bun.isStandaloneExecutable===!0,t=e?"":Mv(process.argv[1]??"");return Lv({scriptPath:t,execPath:Mv(process.execPath),standalone:e})}async function FT(e=fetch,t=$T()){try{let r=await e(OT,{headers:{Accept:"application/vnd.github+json"}});if(!r.ok)return Iv(t);let o=((await r.json()).tag_name??"").replace(/^v/,"");return{current:jt,latest:o,newer:o!==""&&Dv(o,jt)>0,hint:t.hint,kind:t.kind,command:t.command}}catch{return Iv(t)}}async function NT(e,t=Ov){return t(e)}async function Ov(e,t=!1){let r=Bun.spawn([...e],{stdout:t?"inherit":"pipe",stderr:t?"inherit":"pipe",stdin:"ignore"});if(t)return{code:await r.exited,stdout:"",stderr:""};let n=await new Response(r.stdout).text(),o=await new Response(r.stderr).text();return{code:await r.exited,stdout:n,stderr:o}}function $v(e=fetch){return{check:()=>FT(e),apply:(t,r)=>r===!0?Ov(t,!0):NT(t)}}function Iv(e){return{current:jt,latest:"",newer:!1,hint:e.hint,kind:e.kind,command:e.command}}function Mv(e){if(e==="")return"";try{return MT(e)}catch{return e}}function Fv(e){let t=e?.tokens??new ao(60000,_n(),void 0),r=e?.doctorHost??Yo({tokens:t}),n=$v(),o={loadTuiConfig:Do,saveTuiPreferences:Im,resolveTuiOverridePath:aa,userTuiConfigPath:sa,listSessions:sh,loadSessionEvents:ah,loadPath:Wi,validateConfigText:Ui,discover:Qn,configDiff:Pn,openTui:Rv,findDaemon:kv,tryDial:uo,assertMethodAllowed:Pu,readPersistedState:mn,bootstrapLogPath:Ri,exportsDir:Zs,resolveExportPath:oh,writeLogExport:Ed,openInFileManager:ih,freePort:wa,createStarterConfig:Ch,runSetup:(i,s,a)=>Ph(o,i,s,a),readTextFile:(i)=>jT(i,"utf8"),writeTextFile:(i,s)=>BT(i,s),fileExists:HT,load:on,loadOrEmpty:to,validate:Zn,detectGoogle:io,loginGoogle:Ng,logoutGoogle:Hg,refreshUserToken:async(i="user")=>t.refresh(i,"",[]),runDoctor:new cc(e?.doctorRunner??rc(r)),getStartupPlan:new lc,getShutdownPlan:new dc,resolveStart:new uc,formatDoctor:Xg,openController:Cv,openAttach:_v,checkUpdate:()=>n.check(),applyUpdate:(i,s)=>n.apply(i,s),formatUpdateStatus:Av};return o}import{existsSync as dL,unlinkSync as uL}from"fs";import{connect as UT}from"net";At();var WT=2000,GT=200,KT=300;function Nv(e){return{lookup(t){let r=t.toLowerCase();return["","http","tcp","command","process"].includes(r)||e.some((o)=>o.name.toLowerCase()===r)?{check:(o,i)=>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:WT,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 VT(e.url,s);if(a==="tcp")return YT(zT(e.address,r),s);if(a==="command")return JT(e.command.args,e.command.shell,n,o,s);if(a==="process"||a===""){if(t>0&&Qt(t))return{status:Bt,message:"process running"};return{status:Oe,message:"process not running"}}return{status:Oe,message:`unknown health type ${e.type}`}}async function VT(e,t){let r=new AbortController,n=setTimeout(()=>r.abort(),t);try{let o=await fetch(e,{signal:r.signal});if(o.status>=GT&&o.status<KT)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 zT(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 YT(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=UT({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 JT(e,t,r,n,o){if(e.length===0)return{status:Oe,message:"empty health command"};let i=t?process.platform==="win32"?["cmd.exe","/c",e.join(" ")]:["/bin/sh","-c",e.join(" ")]:e,s;try{s=Bun.spawn({cmd:i,cwd:r===""?void 0:r,env:n,stdout:"ignore",stderr:"ignore"})}catch(l){return{status:Oe,message:l instanceof Error?l.message:String(l)}}let a=setTimeout(()=>s.kill("SIGKILL"),o),c=await s.exited;if(clearTimeout(a),c===0)return{status:Bt,message:"ok"};return{status:Oe,message:`exit ${c}`}}te();var{spawn:Qv}=globalThis.Bun;var{spawn:XT}=globalThis.Bun;async function Hv(e,t){if(e<=0)return;try{process.kill(-e,t);return}catch{try{process.kill(e,t)}catch{return}}}async function jv(e){if(!Qt(e))return;let t=await os(["ps","-p",String(e),"-o","command="]),r=Date.now(),n=QT(await os(["ps","-p",String(e),"-o","etime="])),o=await ZT(e);return{pid:e,command:t.trim(),cwd:o,startTime:n===void 0?void 0:new Date(r-n).toISOString()}}function QT(e){let t=e.trim();if(t==="")return;let r=t.indexOf("-"),n=r>=0?t.slice(0,r):"0",i=(r>=0?t.slice(r+1):t).split(":");if(i.length!==2&&i.length!==3)return;let s=Number(n),a=i.length===3?Number(i[0]):0,c=Number(i.length===3?i[1]:i[0]),l=Number(i.at(-1));if(!Number.isInteger(s)||s<0||!Number.isInteger(a)||a<0||a>23||!Number.isInteger(c)||c<0||c>59||!Number.isInteger(l)||l<0||l>59)return;return(((s*24+a)*60+c)*60+l)*1000}async function ZT(e){let r=(await os(["lsof","-a","-p",String(e),"-d","cwd","-Fn"])).split(`
|
|
2252
|
+
`).find((i)=>i.startsWith("n"));if(r&&r.length>1)return r.slice(1).trim();let n=await os(["pwdx",String(e)]),o=n.indexOf(":");if(o>=0)return n.slice(o+1).trim();return""}async function os(e){try{let t=XT({cmd:e,stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,r}catch{return""}}async function Bv(e){let t=new Map;if(e.length===0)return t;let r=await os(["ps","-o","pid=,pcpu=,rss=","-p",e.join(",")]);for(let n of r.split(`
|
|
2253
|
+
`)){let o=n.trim();if(o==="")continue;let[i,s,a]=o.split(/\s+/),c=Number(i),l=Number(s),d=Number(a);if(Number.isFinite(c)&&Number.isFinite(l)&&Number.isFinite(d))t.set(c,{pid:c,cpuPercent:l,memoryKB:d})}return t}function Uv(e,t){if(e.length===0||t==="")return!1;let r=e.join(" ");if(t.includes(r))return!0;let o=(e[0]??"").split(/[/\\]/).pop()??"";if(o!==""&&t.includes(o))return!0;let i=e[e.length-1]??"";return i!==""&&t.includes(i)}var{spawn:Wv}=globalThis.Bun;var e0=1024,t0=3000;async function Gv(e,t){if(e<=0)return;try{await Wv({cmd:["taskkill","/PID",String(e),"/T","/F"],stdout:"ignore",stderr:"ignore"}).exited}catch{return}}async function Kv(e){let t=r0(od(e),e);if(t)return t;if(!Qt(e))return;let r=await _u(["powershell","-NoProfile","-Command",`Get-CimInstance Win32_Process -Filter "ProcessId=${e}" | Select-Object CommandLine,ExecutablePath,CreationDate | ConvertTo-Json -Compress`]),n=o0(r);if(n&&n.command!=="")return{pid:e,command:n.command,cwd:n.cwd,startTime:n.startTime};let o=await _u(["powershell","-NoProfile","-Command",`Get-Process -Id ${e} -ErrorAction SilentlyContinue | Select-Object Path,StartTime | ConvertTo-Json -Compress`]),i=n0(o);if(!i&&!n)return;return{pid:e,command:n?.command||i?.command||"",cwd:n?.cwd||i?.cwd||"",startTime:n?.startTime||i?.startTime}}function r0(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 n0(e){let t=e.trim();if(t==="")return;try{let r=JSON.parse(t),n=(r.Path||"").trim();if(n==="")return;return{command:n,cwd:gc(n),startTime:r.StartTime}}catch{let r=hc(t,"Path");if(r==="")return;return{command:r,cwd:gc(r)}}}function o0(e){let t=e.trim();if(t==="")return;try{let r=JSON.parse(t),n=(r.CommandLine||r.ExecutablePath||"").trim(),o=(r.ExecutablePath||"").trim(),i=gc(o);return{command:n,cwd:i,startTime:r.CreationDate||void 0}}catch{return{command:hc(t,"CommandLine")||hc(t,"ExecutablePath"),cwd:gc(hc(t,"ExecutablePath"))}}}async function qv(e){let t=new Map;if(e.length===0)return t;let r=e.filter((o)=>o>0).join(",");if(r==="")return t;let n=await _u(["powershell","-NoProfile","-Command",`${r}.Split(',') | ForEach-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue } | Select-Object Id,CPU,WorkingSet64 | ConvertTo-Json -Compress`]);return i0(n)}function i0(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)/e0;if(Number.isFinite(a)&&a>0&&Number.isFinite(c)&&Number.isFinite(l))t.set(a,{pid:a,cpuPercent:c,memoryKB:l})}}catch{return t}return t}function gc(e){if(e==="")return"";let t=e.replace(/\//g,"\\"),r=t.lastIndexOf("\\");return r>0?t.slice(0,r):""}function hc(e,t){let r=`${t}=`,n=e.split(/\r?\n/).find((o)=>o.startsWith(r));return n?n.slice(r.length).trim():""}async function _u(e){let t,r=setTimeout(()=>{try{t?.kill()}catch{}},t0);try{t=Wv({cmd:e,stdout:"pipe",stderr:"ignore"});let n=t.stdout?await new Response(t.stdout).text():"";return await t.exited,n}catch{return""}finally{clearTimeout(r)}}te();var{spawn:Ru}=globalThis.Bun;async function zv(e){await c0(e.runtime,e.containerName);let t=s0(e),r=await Zo(e.runtime,t,e.env);if(r.code!==0||r.stdout.trim()==="")throw _(He,`failed to start ${e.containerName}: ${r.stderr.trim()||"container runtime returned no id"}`);let n=r.stdout.trim().split(/\s+/)[0]??"";return Xv(e.runtime,e.containerName,n,e.onLine,e.onExit)}function s0(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 a0=new Set(["PATH","HOME","HOSTNAME","PWD","OLDPWD","SHLVL","_","TMPDIR"]);function Yv(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!a0.has(t)))}async function Jv(e,t,r,n){let o=await Zo(e,["inspect","--format","{{.Id}} {{.State.Running}}",t],process.env);if(o.code!==0)return;let[i,s]=o.stdout.trim().split(/\s+/);if(!i||s!=="true")return;return Xv(e,t,i,r,n)}function Xv(e,t,r,n,o){let i=!0,s=Ru({cmd:[e,"logs","--follow",r],stdout:"pipe",stderr:"pipe",stdin:"ignore"});Vv(s.stdout,"stdout",n),Vv(s.stderr,"stderr",n);let a=Ru({cmd:[e,"wait",r],stdout:"pipe",stderr:"pipe",stdin:"ignore"}),c=Promise.all([new Response(a.stdout).text(),a.exited]).then(async([l,d])=>{i=!1,s.kill();let u=Number.parseInt(l.trim(),10),f=Number.isFinite(u)?u:d??1,p=f===0?void 0:Error(`container exited with code ${f}`);return await Zo(e,["rm","--force",r],process.env),o?.(f,p),{code:f,err:p}});return{id:r,runtime:e,containerName:t,done:c,running:()=>i,stop:async(l)=>{let d=Math.max(1,Math.ceil(l/1000));await Zo(e,["stop","--time",String(d),r],process.env),await c}}}async function c0(e,t){let r=await Zo(e,["inspect","--format","{{.State.Running}}",t],process.env);if(r.code===0&&r.stdout.trim()==="false")await Zo(e,["rm","--force",t],process.env)}async function Zo(e,t,r){try{let n=Ru({cmd:[e,...t],env:{...process.env,...r},stdout:"pipe",stderr:"pipe",stdin:"ignore"}),[o,i,s]=await Promise.all([new Response(n.stdout).text(),new Response(n.stderr).text(),n.exited]);return{code:s??1,stdout:o,stderr:i}}catch(n){throw Pe(He,`unable to run ${e}`,n)}}async function Vv(e,t,r){if(!e||typeof e==="number"||!r)return;let n=e.getReader(),o=new TextDecoder,i="";for(;;){let s=await n.read();if(s.done)break;i+=o.decode(s.value,{stream:!0});let a=i.split(`
|
|
2254
|
+
`);i=a.pop()??"";for(let c of a)r(t,c.replace(/\r$/,""))}if(i!=="")r(t,i)}var l0=1e4,d0=2000,u0=500;class Eu{isRunning(e){let t=this.running.get(e);return t!==void 0&&is(t)}running=new Map;async start(e){let t=this.running.get(e.name);if(t&&is(t))return t;if(this.running.delete(e.name),e.args.length===0)throw _(He,"empty command");let r=e.shell?ty(e.args):e.args,n;try{n=Qv({cmd:r,cwd:e.workDir===""?void 0:e.workDir,env:e.env,stdout:e.captureStdout===!1?"ignore":"pipe",stderr:e.captureStderr===!1?"ignore":"pipe",stdin:"ignore",detached:process.platform!=="win32"})}catch(i){throw Pe(He,`failed to start ${e.name}`,i)}let o={name:e.name,pid:n.pid??0,startTime:new Date,workDir:e.workDir,args:[...e.args],proc:n,done:Promise.resolve({code:0})};return vc(n.stdout,"stdout",e.onLine),vc(n.stderr,"stderr",e.onLine),o.done=n.exited.then((i)=>{let s=typeof i==="number"?i:0,a=s===0?void 0:Error(`exited with code ${s}`);if(this.running.get(e.name)===o)this.running.delete(e.name);if(e.onExit)e.onExit(s,a);return{code:s,err:a}}),this.running.set(e.name,o),o}async runOnce(e){if(e.args.length===0)throw _(He,"empty command");let t;try{t=Qv({cmd:e.shell?ty(e.args):e.args,cwd:e.workDir||void 0,env:e.env,stdout:"pipe",stderr:"pipe",stdin:"ignore"})}catch(a){throw Pe(He,`failed to run ${e.name}`,a)}let r="",n="",o=(a,c)=>{if(a==="stdout")r+=`${c}
|
|
2255
2255
|
`;else n+=`${c}
|
|
2256
|
-
`;e.onLine?.(a,c)},i=[vc(t.stdout,"stdout",o),vc(t.stderr,"stderr",o)],s=await t.exited;return await Promise.all(i),{code:typeof s==="number"?s:0,stdout:r,stderr:n}}async startContainer(e){let t=this.running.get(e.name);if(t&&is(t))return t;let r=await zv({...e,env:Yv(e.env)}),n={name:e.name,pid:0,startTime:new Date,workDir:e.workDir,args:[...e.command],done:r.done,container:r};return r.done.finally(()=>{if(this.running.get(e.name)===n)this.running.delete(e.name)}),this.running.set(e.name,n),n}async adoptContainer(e){let t=this.running.get(e.name);if(t&&is(t))return t;let r=await Jv(e.runtime,e.containerName,e.onLine,e.onExit);if(!r)return;let n={name:e.name,pid:0,startTime:new Date,workDir:e.workDir,args:[],done:r.done,container:r};return r.done.finally(()=>{if(this.running.get(e.name)===n)this.running.delete(e.name)}),this.running.set(e.name,n),n}adopt(e){let t=this.running.get(e.name);if(t&&is(t))return t;if(!Qt(e.pid))throw
|
|
2257
|
-
`);i=c.pop()??"";for(let l of c)r(t,l.replace(/\r$/,""))}}async function ny(e,t){return Promise.race([e.then(()=>!0),new Promise((r)=>{setTimeout(()=>r(!1),t)})])}function f0(e){return new Promise((t)=>{setTimeout(t,e)})}import{existsSync as m0,readFileSync as h0,writeFileSync as g0}from"fs";var sy={exists:(e)=>m0(e),readText:(e)=>h0(e,"utf8"),writeText:(e,t)=>g0(e,t)};ht();var Tu=3,v0=3,y0=10,as={shouldRestart(e){if(!e.enabled||e.policy===Sa)return!1;if(e.retryCount>=e.maxRetries)return!1;if(e.policy===Ca)return!0;return e.policy===ka&&e.exitCode!==0},fromService(e,t,r){return{policy:Ea(e.restart),enabled:e.restart.enabled!==!1,exitCode:r,retryCount:t,maxRetries:e.restart.max_retries>0?e.restart.max_retries:Tu}}},Au={shouldRestartUnhealthy(e,t=v0){return e>=t},shouldResetRestartBudget(e,t=y0){return e>=t}},yc={timeoutMs(e,t){let r=e.startup.timeout_seconds;return r>0?r*1000:t},waitForHealthy(e){return e.startup.wait_for_healthy}};At();te();var b0=2,x0=2000,w0=3,S0=10;class Lu{host;processes;clock;actions;generation=new Map;healthTimers=new Map;restarts=new Map;unhealthyStreak=new Map;healthyStreak=new Map;restartTimers=new Map;constructor(e,t,r,n){this.host=e;this.processes=t;this.clock=r;this.actions=n}forget(e){this.clearHealthWatch(e),this.clearRestartTimer(e),this.restarts.delete(e),this.bumpGeneration(e),this.unhealthyStreak.delete(e),this.healthyStreak.delete(e)}dispose(){this.clearAllRestartTimers();for(let e of this.generation.keys())this.bumpGeneration(e);for(let e of this.healthTimers.keys())this.clearHealthWatch(e)}onExit(e,t,r,n){if(!this.isCurrentGeneration(e,t))return;this.clearHealthWatch(e);let o=this.host().runtimes.get(e);if(o?.state===dt)return;if(o&&(o.state===_r||o.state===Et)){this.host().setState(e,Et,Tt,0,""),this.host().bus.publish(Se(ca,e,{code:r}));return}let i=this.host().cfg.services[e],s=n?.message??`exited with code ${r}`,a=this.restarts.get(e)??0;if((i?as.shouldRestart(as.fromService(i,a,r)):!1)&&i){this.host().setState(e,fr,Tt,0,s),this.bumpRestartCount(e,a+1),this.host().persistState(),this.armRestart(e,i,t,a,()=>{let l=this.host().serviceProfile.get(e)??this.host().profile,d=this.host().serviceProfileEnv.get(e)??this.host().profileEnv;this.actions.startOne(e,l,d,!1).catch((u)=>this.host().log(e,"ERROR",L(u)))});return}this.host().fail(e,E("process_start",s))}startHealth(e,t,r,n,o,i,s){let a=this.healthTimers.get(e);if(a)clearInterval(a);if(t.health.type===""){this.setHealth(e,Bt,"no health check");return}let c=t.health.interval_seconds>0?t.health.interval_seconds*1000:x0,l=Date.parse(this.host().runtimes.get(e)?.startTime??"")||this.clock.unixMs(),d=()=>{(t.container&&t.health.type.toLowerCase()==="process"?Promise.resolve(this.processes.isRunning(e)?{status:Bt,message:"container running"}:{status:Oe,message:"container not running"}):Promise.resolve().then(()=>this.host().healthCheckers.lookup(t.health.type)?.check(t.health,{pid:r,ports:n,workDir:o,env:i})??{status:Oe,message:`unknown health type ${t.health.type}`})).catch((f)=>({status:Oe,message:L(f)})).then((f)=>{if(!this.isCurrentGeneration(e,s))return;let p=this.host().runtimes.get(e);if(p&&ay(p.state))return;if(f.status===Oe&&this.clock.unixMs()-l<t.health.start_period_seconds*1000){this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:"INFO",message:`health check still in start period: ${f.message}`,pid:r});return}this.setHealth(e,f.status,f.message),this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:k0(f.status),message:`health ${f.status} ${f.message}`,pid:r}),this.maybeRestartUnhealthy(e,t,f.status,s)})};d(),this.healthTimers.set(e,setInterval(d,c))}bumpRestartCount(e,t){this.restarts.set(e,t);let r=this.host().runtimes.get(e);if(r)r.restarts=t}resetRestartCount(e){this.healthyStreak.set(e,0),this.bumpRestartCount(e,0)}bumpGeneration(e){let t=(this.generation.get(e)??0)+1;return this.generation.set(e,t),this.healthyStreak.set(e,0),t}isCurrentGeneration(e,t){return this.generation.get(e)===t}scheduleRestart(e,t,r){this.clearRestartTimer(e);let n=setTimeout(()=>{this.restartTimers.delete(e),r()},t);this.restartTimers.set(e,n)}armRestart(e,t,r,n,o){let i=t.restart.backoff_seconds>0?t.restart.backoff_seconds:b0;this.scheduleRestart(e,i*2**n*1000,()=>{if(this.isCurrentGeneration(e,r))o()})}clearRestartTimer(e){let t=this.restartTimers.get(e);if(t)clearTimeout(t),this.restartTimers.delete(e)}clearAllRestartTimers(){for(let e of this.restartTimers.values())clearTimeout(e);this.restartTimers.clear()}setHealth(e,t,r){let n=this.host().runtimes.get(e);if(!n||ay(n.state))return;if(n.health=t,n.state===je||n.state===pr||n.state===Br)n.state=t===Bt?pr:t===Oe?Br:n.state;this.host().bus.publish(Se($m,e,{health:t,message:r}))}maybeRestartUnhealthy(e,t,r,n){if(r!==Oe){if(this.unhealthyStreak.set(e,0),r===Bt)this.maybeForgiveRestarts(e,t);return}this.healthyStreak.set(e,0);let o=t.restart.policy||(t.restart.enabled?"on_failure":"never");if(o!=="on_failure"&&o!=="always")return;if(this.restartTimers.has(e))return;let i=(this.unhealthyStreak.get(e)??0)+1;this.unhealthyStreak.set(e,i);let s=t.health.unhealthy_threshold>0?t.health.unhealthy_threshold:w0;if(!Au.shouldRestartUnhealthy(i,s))return;this.unhealthyStreak.set(e,0);let a=this.restarts.get(e)??0,c=t.restart.max_retries>0?t.restart.max_retries:Tu;if(a>=c){this.host().log(e,"WARN",`unhealthy after ${i} consecutive checks but the restart limit (${c}) has already been reached; not restarting`);return}this.bumpRestartCount(e,a+1),this.host().log(e,"WARN",`restarting after ${i} consecutive unhealthy checks (attempt ${a+1}/${c})`),this.armRestart(e,t,n,a,()=>{this.actions.restart([e],{auto:!0}).catch((l)=>this.host().log(e,"ERROR",L(l)))})}maybeForgiveRestarts(e,t){let r=(this.healthyStreak.get(e)??0)+1;this.healthyStreak.set(e,r);let n=t.health.healthy_reset_threshold>0?t.health.healthy_reset_threshold:S0;if(Au.shouldResetRestartBudget(r,n)&&(this.restarts.get(e)??0)>0)this.host().log(e,"INFO",`restart count reset after ${r} consecutive healthy checks`),this.bumpRestartCount(e,0)}clearHealthWatch(e){let t=this.healthTimers.get(e);if(t)clearInterval(t),this.healthTimers.delete(e)}}function ay(e){return e===fr||e===_r||e===Et||e===dt}function k0(e){return e===Oe?"WARN":e===Bt?"INFO":"DEBUG"}ht();te();jr();te();At();var Du=[_r,dt,fr,je,Et],C0={[ev]:[Et,on,je,dt,fr,_r],[Et]:[on,je,dt],[on]:[je,dt,Et,_r],[je]:Du,[pr]:Du,[Br]:Du,[_r]:[Et,dt],[dt]:[on,Et,je],[fr]:[on,je,dt,Et]};function cs(e,t){if(e===t)return!0;return(C0[e]??[]).includes(t)}function cy(e,t){if(!cs(e,t))throw E(Y,`illegal service lifecycle transition ${e} \u2192 ${t}`);return t}At();var P0=100,ly=30000;class Iu{processes;clock;session;health;constructor(e,t){this.processes=e;this.clock=t;this.health=new Lu(()=>this.host(),e,t,{startOne:(r,n,o,i)=>this.startOne(r,n,o,i),restart:(r,n)=>this.restart(r,n)})}bind(e){this.session=e}host(){if(!this.session)throw E(He,"service orchestrator is not bound");return this.session}planStart(e,t,r){return nc(e,t,r)}planStop(e,t,r=!1){return r?ic(e,t):oc(e,t)}applyLifecycle(e,t){return cy(e,t)}isLegalLifecycle(e,t){return cs(e,t)}async start(e){let t=this.host();if(e.detach===!0)t.detached=!0;let r=ac(t.cfg,{services:e.services,profile:e.profile?ur(e.profile):void 0,activeProfile:ur(t.profile)});if(r.profile)t.profile=r.profile,t.profileEnv=r.env;if(e.client_env)for(let a of r.services)t.clientEnv.set(a,e.client_env);if(e.auto!==!0)for(let a of r.services)this.health.resetRestartCount(a);let n=this.planStart(t.cfg,r.services,r.profile),o=await t.detectGoogle(t.cfg.google.project_id);n.blockers=Dg(t.cfg,n.waves.flat(),o.adcAvailable);let i=new Set(n.blockers.map((a)=>a.name));for(let a of n.blockers)await t.fail(a.name,E(He,a.message));if(t.cfg.proxy.enabled&&!t.proxySuppressed)await t.startProxy().catch((a)=>t.log("devctl","ERROR",L(a)));let s=[];for(let a of n.waves.flat()){if(i.has(a)||await t.claimIfAlreadyUp(a))continue;s.push(a)}for(let a of s)if(e.profile||!t.serviceProfile.has(a))t.serviceProfile.set(a,r.profile);if(s.length>0)await t.assignPendingPorts(s);for(let a of n.waves){let c=a.filter((l)=>s.includes(l));if(c.length>0){let l=await Promise.allSettled(c.map((u)=>this.startOne(u,r.profile,r.env,e.auto!==!0))),d=!1;for(let u of l)if(u.status==="rejected")d=!0,t.log("devctl","ERROR",L(u.reason));if(d)throw E(He,"one or more services failed to start")}try{await this.awaitWaveHealth(a,n.waves.flat())}catch(l){throw t.log("devctl","ERROR",L(l)),l}}return t.persistState(),n}async stop(e){let t=this.host(),r=e;if(r.length===0)r=[...t.runtimes.entries()].filter(([,s])=>s.state!==Et).map(([s])=>s);if(r.length===0)return;let n=r.filter((s)=>!t.cfg.services[s]&&t.runtimes.has(s)),o=r.filter((s)=>!t.cfg.services[s]&&!t.runtimes.has(s));if(o.length>0)throw E(Ho,`unknown service "${o[0]}"`);let i=r.filter((s)=>t.cfg.services[s]!==void 0);if(i.length>0)await this.runStopPlan(this.planStop(t.cfg,i),{resetRestartCounts:!0});if(n.length>0){await this.runStopPlan({profile:t.profile,steps:[],waves:n.map((s)=>[s])},{resetRestartCounts:!0});for(let s of n)t.forgetService(s)}}async restart(e,t){let r=this.host(),n=t?.cascade===!0,o=n?co(r.cfg,e):e,i=n?this.planStop(r.cfg,e):this.planStop(r.cfg,e,!0),s=t?.auto!==!0;await this.runStopPlan(i,{resetRestartCounts:s}),await this.start({services:o,client_env:t?.clientEnv,auto:t?.auto})}async runStopPlan(e,t){let r=this.host();for(let i of e.waves.flat())if(this.health.clearRestartTimer(i),this.health.bumpGeneration(i),t?.resetRestartCounts)this.health.resetRestartCount(i);let n=xn(r.cfg.shutdown)*1000,o=[];for(let i of e.waves){let s=await Promise.allSettled(i.map(async(a)=>{r.setState(a,_r,Tt,0,""),this.health.clearHealthWatch(a);try{await this.processes.stop(a,n)}catch(c){throw r.setState(a,dt,Tt,0,L(c)),r.bus.publish(Se(la,a,{error:L(c)})),c}try{await r.releasePorts(a)}catch(c){r.log(a,"WARN",L(c))}r.setState(a,Et,Tt,0,""),r.bus.publish(Se(ca,a,{}))}));for(let a=0;a<s.length;a+=1){let c=s[a];if(c?.status==="rejected"){let l=i[a]??"";o.push(l),r.log(l||"devctl","ERROR",L(c.reason))}}}if(r.persistState(),o.length>0)throw E(He,`failed to stop: ${o.join(", ")}`)}serviceIsActive(e){if(this.processes.isRunning(e))return!0;let t=this.host().runtimes.get(e);if(!t)return!1;if(t.state===fr||t.state===_r||t.state===Et||t.state===dt)return!1;if(t.health===Bt||t.state===pr)return!0;return t.state===on||t.state===je}async startOne(e,t,r,n=!1){let o=this.host();if(this.serviceIsActive(e))return;let i=o.cfg.services[e];if(!i)throw E(lt,`unknown service ${e}`);o.setState(e,on,Tt,0,"");let s=this.health.bumpGeneration(e),a=o.serviceProfile.get(e)??t,c=a!==""?lo(o.cfg,a):{...r};o.serviceProfileEnv.set(e,c);let l={},d={},u="",f;try{await o.prepareServiceIdentity(e,i),l=o.ports.get(e)??{};let p=await o.resolveServiceExecution(e,i,a,c,o.clientEnv.get(e),!i.container);if(d=p.env,u=p.workDir,n)await this.runTransient(`${e}:pre_start`,i.hooks.pre_start,i.shell,u,d);if(!this.health.isCurrentGeneration(e,s))return;let m=(v,b)=>{o.logs.append({timestamp:this.clock.isoNow(),service:e,source:v,stream:v,level:"",message:b,pid:f.pid})},h=(v,b)=>this.health.onExit(e,s,v,b);f=i.container?await this.processes.startContainer({name:e,runtime:i.container.runtime==="podman"?"podman":"docker",containerName:`${o.containerPrefix}${e.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,image:i.container.image,command:[...i.command.args],env:{...d,...i.container.env},ports:l,targetPorts:i.container.ports,volumes:i.container.volumes,workDir:u,onLine:m,onExit:h}):await this.processes.start({name:e,args:[...i.command.args],shell:i.shell||i.command.shell,workDir:u,env:d,graceMs:xn(o.cfg.shutdown)*1000,captureStdout:xh(i),captureStderr:wh(i),onLine:m,onExit:h})}catch(p){if(this.health.isCurrentGeneration(e,s))await o.fail(e,p);throw p}if(!this.health.isCurrentGeneration(e,s)){if(this.processes.get(e)===f)await this.processes.stop(e,xn(o.cfg.shutdown)*1000).catch(()=>{});return}if(o.processMeta.set(e,{command:[...i.command.args],cwd:u,startTime:f.startTime}),o.setState(e,je,Tt,f.pid,""),o.bus.publish(Se(Mm,e,{pid:f.pid})),n)try{await this.runTransient(`${e}:post_start`,i.hooks.post_start,i.shell,u,d)}catch(p){throw await o.fail(e,p),p}if(!this.health.isCurrentGeneration(e,s))return;if(this.health.startHealth(e,i,f.pid,l,u,d,s),o.persistState(),o.clearRestartRequired([e]),yc.waitForHealthy(i)){let p=yc.timeoutMs(i,ly);try{await this.waitHealthy(e,p)}catch(m){throw await o.fail(e,m),m}}}async awaitWaveHealth(e,t){for(let r of e){let n=this.host().cfg.services[r],o=t.some((s)=>(this.host().cfg.services[s]?.dependencies??[]).some((a)=>zt(a)===r&&gn(a)==="service_healthy"));if(!n||n.health.type===""||!o)continue;let i=yc.timeoutMs(n,ly);try{await this.waitHealthy(r,i)}catch(s){throw await this.host().fail(r,s),s}}}async waitHealthy(e,t){let r=this.clock.unixMs()+t;while(this.clock.unixMs()<r){let n=this.host().runtimes.get(e);if(n?.health===Bt)return;if(n?.state===dt)throw E(Td,`service ${e} failed while waiting for healthy`);await _0(P0)}throw E(Td,`service ${e} did not become healthy in time`)}async runTransient(e,t,r,n,o){if(yn(t))return{code:0,stdout:"",stderr:""};this.host().log(e,"INFO",`running ${t.args.join(" ")}`);let i=await this.processes.runOnce({name:e,args:[...t.args],shell:r||t.shell,workDir:n,env:o,graceMs:xn(this.host().cfg.shutdown)*1000,onLine:(s,a)=>this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:s,stream:s,level:"",message:a,pid:0})});if(i.code!==0)throw E(He,`${e} exited with code ${i.code}`);return i}}function _0(e){return new Promise((t)=>setTimeout(t,e))}te();function ls(e){return async(t)=>{let r=t.includes("/versions/")?t:`${t}/versions/latest`,n=await e(),o=await fetch(`https://secretmanager.googleapis.com/v1/${r}:access`,{headers:{Authorization:`Bearer ${n}`}});if(!o.ok)throw E(Y,`secret manager request failed for ${t}: HTTP ${o.status}`);let i=await o.json();if(!i.payload?.data)throw E(Y,`secret manager response for ${t} had no payload`);return Buffer.from(i.payload.data,"base64").toString("utf8")}}At();te();async function bc(e,t,r,n){let o="";if(e.proxy?.isRunning())o=`http://${e.proxy.address()}`;else if(e.cfg.proxy.enabled)o=`http://${bn(e.cfg.proxy.listen)}`;let i=rs(t,"127.0.0.1",n,o,e.cfg.project.name);if(i.DEVCTL_INTERNAL_TOKEN=e.internalTok,e.cfg.proxy.token_endpoint.enabled)i.DEVCTL_TOKEN_URL=e.boundTokenURL||`http://127.0.0.1:${e.tokenEP?.listenPort()||e.cfg.proxy.token_endpoint.port}/token`;try{let s=await ts(e.cfg.repoRoot,{service:t,profile:e.serviceProfile.get(t)??e.profile,serviceCfg:r,profileEnv:e.serviceProfileEnv.get(t)??e.profileEnv,assignedPorts:n,runtime:i,cfg:e.cfg,fetchSecret:ls(async()=>(await e.tokens.get("user","",[])).accessToken),pluginSources:e.registry?.environmentSources});return Qo(s)}catch(s){return e.log(t,"WARN",`could not fully reconstruct environment for adopted service's health check (${L(s)}); using a baseline environment`),Qo(i)}}function dy(e,t,r,n,o,i){if(e.procs.get(t)&&e.processAliveFn(e.procs.get(t)?.pid??0))return;if(!e.processAliveFn(r)||r===process.pid)return;let s=e.orchestrator.health.bumpGeneration(t);try{e.procs.adopt({name:t,pid:r,args:n,workDir:o,startTime:i,onExit:(a,c)=>{e.orchestrator.health.onExit(t,s,a,c)}})}catch(a){let c=a instanceof Error?a.message:String(a);e.log(t,"WARN",`adopt pid ${r} failed (${c}); tracking leftover in snapshot only`)}return e.processMeta.set(t,{command:n,cwd:o,startTime:i}),e.persistState(),s}async function uy(e,t){let r=e.cfg.services[t];if(e.orchestrator.serviceIsActive(t)){if(r&&!e.ports.has(t)){let a=await xa(r);if(a)e.ports.set(t,a)}return!0}if(!r)return!1;if(r.container){let a=e.orchestrator.health.bumpGeneration(t),c=r.container.runtime==="podman"?"podman":"docker",l=e.serviceWorkDir(r),d=await e.procs.adoptContainer({name:t,runtime:c,containerName:`devctl-${ar(e.cfg.repoRoot)}-${t.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:l,onLine:(p,m)=>e.logs.append({timestamp:e.clock.isoNow(),service:t,source:p,stream:p,level:"",message:m,pid:0}),onExit:(p,m)=>e.orchestrator.health.onExit(t,a,p,m)});if(!d)return!1;let u=e.ports.get(t)??Object.fromEntries(r.ports.filter((p)=>!p.auto).map((p)=>[p.name,p.value]));e.ports.set(t,u),e.processMeta.set(t,{command:[...r.command.args],cwd:l,startTime:d.startTime}),xc(e,t,wc(e,fn(e.cfg.repoRoot)?.processes.find((p)=>p.name===t))),e.setState(t,je,Tt,0,"");let f=await bc(e,t,r,u);return e.orchestrator.health.startHealth(t,r,0,u,l,f,a),e.persistState(),e.log(t,"INFO",`claimed running ${c} container ${d.container?.id??""}`),!0}let n=await xa(r);if(!n)return!1;let o=Object.values(n)[0],s=(o===void 0?void 0:await zn(o))?.pid??0;if(s>0&&s!==process.pid){let a=fn(e.cfg.repoRoot)?.processes.find((d)=>d.name===t&&d.pid===s);if(!a)return e.log(t,"WARN",`port ${o} is held by pid ${s} with no persisted record for ${t}; not adopting`),!1;let c=await e.inspectProcessFn(s);if(c!==void 0&&c.command!==""&&ss({args:[...r.command.args],workDir:e.serviceWorkDir(r),startTime:new Date(a.startTime)},c)){e.ports.set(t,n);let d=dy(e,t,s,[...r.command.args],e.serviceWorkDir(r),new Date(a.startTime))??e.orchestrator.health.bumpGeneration(t);xc(e,t,wc(e,a)),e.setState(t,je,Tt,s,""),e.log(t,"INFO",`already listening on ${Object.values(n).join(", ")}; not starting again`);let u=e.serviceWorkDir(r),f=await bc(e,t,r,n);return e.orchestrator.health.startHealth(t,r,s,n,u,f,d),!0}e.log(t,"WARN",`port ${o} is in use by an unrelated process (pid ${s}); not adopting`)}return!1}async function py(e){let t=fn(e.cfg.repoRoot);if(!t)return;R0(e,t.profile);let r=[];for(let[n,o]of Object.entries(e.cfg.services)){if(!o.container)continue;let i=t.processes.find((u)=>u.name===n),s=e.orchestrator.health.bumpGeneration(n),a=o.container.runtime==="podman"?"podman":"docker",c=await e.procs.adoptContainer({name:n,runtime:a,containerName:`devctl-${ar(e.cfg.repoRoot)}-${n.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:e.serviceWorkDir(o),onLine:(u,f)=>e.logs.append({timestamp:e.clock.isoNow(),service:n,source:u,stream:u,level:"",message:f,pid:0}),onExit:(u,f)=>e.orchestrator.health.onExit(n,s,u,f)});if(!c)continue;let l=i?.ports??Object.fromEntries(o.ports.filter((u)=>!u.auto).map((u)=>[u.name,u.value]));e.ports.set(n,l),e.processMeta.set(n,{command:[...o.command.args],cwd:e.serviceWorkDir(o),startTime:i?.startTime?new Date(i.startTime):c.startTime}),xc(e,n,wc(e,i)),e.setState(n,je,Tt,0,"");let d=await bc(e,n,o,l);e.orchestrator.health.startHealth(n,o,0,l,e.serviceWorkDir(o),d,s),e.log(n,"INFO",`adopted ${a} container ${c.container?.id??""}`),r.push(n)}for(let n of t.processes){if(!e.cfg.services[n.name]||e.cfg.services[n.name]?.container||n.pid<=0||n.pid===process.pid||!e.processAliveFn(n.pid))continue;let o=await e.inspectProcessFn(n.pid);if(!(o!==void 0&&o.command!==""&&ss({args:n.command,workDir:n.cwd,startTime:n.startTime?new Date(n.startTime):void 0},o))){if(Object.values(n.ports).length>0&&await xa({ports:Object.entries(n.ports).map(([l,d])=>({name:l,value:d,auto:!1}))})!==void 0)e.log(n.name,"WARN",`pid ${n.pid} does not match stored command; leftover listener not adopted`);else e.log(n.name,"WARN",`pid ${n.pid} is a different process; not adopting`);continue}let s=dy(e,n.name,n.pid,n.command,n.cwd,new Date(n.startTime||e.clock.unixMs()))??e.orchestrator.health.bumpGeneration(n.name);if(Object.keys(n.ports).length>0)e.ports.set(n.name,n.ports);xc(e,n.name,wc(e,n)),e.setState(n.name,je,Tt,n.pid,"");let a=e.cfg.services[n.name];if(a){let c=n.cwd||e.serviceWorkDir(a),l=await bc(e,n.name,a,n.ports);e.orchestrator.health.startHealth(n.name,a,n.pid,n.ports,c,l,s)}e.log(n.name,"INFO","adopted leftover process; stdout/stderr from before adopt are not captured"),r.push(n.name)}if(r.length>0)e.bus.publish(Se(Hm,"",{services:r,session_id:t.session_id})),e.log("devctl","INFO",`recovered session processes: ${r.join(", ")}`)}function R0(e,t){e.profile=t||e.profile,e.profileEnv=lo(e.cfg,e.profile)}function xc(e,t,r){e.serviceProfile.set(t,r),e.serviceProfileEnv.set(t,lo(e.cfg,r))}function wc(e,t){return t?.profile??e.profile}import{statSync as U0,watch as W0}from"fs";import{isAbsolute as G0,join as K0,resolve as q0}from"path";function fy(e,t){return t}function Mu(e,t){let r={},n=new Set,o=new Set([...Object.keys(e.services),...Object.keys(t.services)]);for(let s of o){let a=e.services[s],c=t.services[s],l=[];if(!a||!c)l.push("presence"),n.add(s);else{if(a.command.args.join("\x00")!==c.command.args.join("\x00")||a.command.shell!==c.command.shell||a.shell!==c.shell)l.push("command"),n.add(s);if(a.working_dir!==c.working_dir)l.push("working_dir"),n.add(s);if(JSON.stringify(a.environment)!==JSON.stringify(c.environment))l.push("environment"),n.add(s);if(JSON.stringify(a.ports)!==JSON.stringify(c.ports))l.push("ports"),n.add(s);if(JSON.stringify(a.identity)!==JSON.stringify(c.identity))l.push("identity"),n.add(s);if(JSON.stringify(a.health)!==JSON.stringify(c.health))l.push("health"),n.add(s);if(JSON.stringify(a.restart)!==JSON.stringify(c.restart))l.push("restart"),n.add(s);if(JSON.stringify(a.startup)!==JSON.stringify(c.startup))l.push("startup"),n.add(s);if(JSON.stringify(a.logs)!==JSON.stringify(c.logs))l.push("logs"),n.add(s);if(JSON.stringify(a.container)!==JSON.stringify(c.container))l.push("container"),n.add(s);if(JSON.stringify(a.watch)!==JSON.stringify(c.watch))l.push("watch"),n.add(s)}if(l.length>0)r[s]=l}let i=[];if(JSON.stringify(e.logs)!==JSON.stringify(t.logs))i.push("logs");if(JSON.stringify(e.auth)!==JSON.stringify(t.auth))i.push("auth");return{restart_required:[...n].sort(),changes:r,supervisor_restart_required:i}}At();te();import{pathToFileURL as F0}from"url";import{isAbsolute as N0,resolve as H0}from"path";import{createServer as yy,request as E0}from"http";import{request as T0}from"https";import{randomBytes as my}from"crypto";import{Readable as A0}from"stream";te();jr();var ds="x-devctl-request-id",L0="x-devctl-internal-token",hy=100,Ou=new Set(["connection","keep-alive","proxy-authenticate","proxy-authorization","te","trailers","transfer-encoding","upgrade"]),D0=new Set(["gzip","deflate","br"]);class $u{cfg;tokens;logs;bus;detector;resolvePort;middleware;server;running=!1;addr="";upgradedSockets=new Set;recentRequests=[];constructor(e,t,r,n,o,i=[],s){this.cfg=e,this.tokens=t,this.logs=r,this.bus=n,this.detector=o,this.middleware=i,this.resolvePort=s}upstreamBase(e){let t=e.upstream.service??"";if(t==="")return e.upstream.url;if(!this.resolvePort)throw E(Nr,`route ${e.name} addresses service ${t} but this proxy has no port resolver configured`);let r=this.resolvePort(t,e.upstream.port??"");if(r===void 0)throw E(Nr,`upstream service ${t} is not running`);return`http://127.0.0.1:${r}`}address(){return this.addr||bn(this.cfg.listen)}setMiddleware(e){this.middleware=e}isRunning(){return this.running}stats(){let e=this.recentRequests.reduce((t,r)=>t+(r.status>=400||r.error?1:0),0);return{total:this.recentRequests.length,errors:e,recent:[...this.recentRequests].reverse()}}recordRequest(e){if(this.recentRequests.push(e),this.recentRequests.length>hy)this.recentRequests.splice(0,this.recentRequests.length-hy)}start(){let e=this.cfg.listen.host||"127.0.0.1";if(!Cn(e))return Promise.reject(E(Nr,`refusing to bind proxy to ${e}`));if(this.cfg.listen.port===0)return Promise.reject(E(Nr,"proxy.listen.port is required when the proxy is enabled"));let t=this.cfg.listen.port;return new Promise((r,n)=>{this.server=yy((o,i)=>{this.serve(o,i)}),this.server.on("upgrade",(o,i,s)=>{this.serveUpgrade(o,i,s)}),this.server.on("error",(o)=>n(Pe(Nr,`unable to listen on ${e}:${t}`,o))),this.server.listen(t,e,()=>{this.running=!0,this.addr=`${e}:${t}`,this.bus?.publish(Se(Fm,"",{address:this.addr})),r()})})}stop(){let e=this.server;if(!e)return Promise.resolve();for(let t of this.upgradedSockets)t.destroy();return this.upgradedSockets.clear(),new Promise((t)=>{e.close(()=>{this.running=!1,this.bus?.publish(Se(Nm,"",{})),t()})})}async serveUpgrade(e,t,r){let n=Date.now(),o=e.headers[ds]?.toString()||my(8).toString("hex"),i=e.method??"GET",s=e.url??"/",a=this.detector?this.detector.redactText(s):s,c=vy(this.cfg.routes,e);if(!c){t.end(`HTTP/1.1 404 Not Found\r
|
|
2256
|
+
`;e.onLine?.(a,c)},i=[vc(t.stdout,"stdout",o),vc(t.stderr,"stderr",o)],s=await t.exited;return await Promise.all(i),{code:typeof s==="number"?s:0,stdout:r,stderr:n}}async startContainer(e){let t=this.running.get(e.name);if(t&&is(t))return t;let r=await zv({...e,env:Yv(e.env)}),n={name:e.name,pid:0,startTime:new Date,workDir:e.workDir,args:[...e.command],done:r.done,container:r};return r.done.finally(()=>{if(this.running.get(e.name)===n)this.running.delete(e.name)}),this.running.set(e.name,n),n}async adoptContainer(e){let t=this.running.get(e.name);if(t&&is(t))return t;let r=await Jv(e.runtime,e.containerName,e.onLine,e.onExit);if(!r)return;let n={name:e.name,pid:0,startTime:new Date,workDir:e.workDir,args:[],done:r.done,container:r};return r.done.finally(()=>{if(this.running.get(e.name)===n)this.running.delete(e.name)}),this.running.set(e.name,n),n}adopt(e){let t=this.running.get(e.name);if(t&&is(t))return t;if(!Qt(e.pid))throw _(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=f0(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:l0;if(r.container){await r.container.stop(n),this.running.delete(e);return}if(await Zv(r.pid,"SIGTERM"),!await ny(r.done,n)){if(await Zv(r.pid,"SIGKILL"),!await ny(r.done,d0))throw _(He,`process ${e} did not exit after SIGKILL`)}this.running.delete(e)}get(e){return this.running.get(e)}all(){return[...this.running.values()]}}function is(e){if(e.container)return e.container.running();if(e.proc)return e.proc.exitCode===null&&!e.proc.killed;return Qt(e.pid)}async function Zv(e,t){if(process.platform==="win32"){await Gv(e,t);return}await Hv(e,t)}async function oy(e){if(process.platform==="win32")return Kv(e);return jv(e)}async function iy(e){if(process.platform==="win32")return qv(e);return Bv(e)}var p0=2000;function ss(e,t){if(e.workDir!==""&&t.cwd!==""&&ry(e.workDir)!==ry(t.cwd))return!1;if(!Uv(e.args,t.command))return!1;let r=ey(e.startTime),n=ey(t.startTime);if(r!==void 0&&n!==void 0)return Math.abs(r-n)<=p0;return!0}function ey(e){if(e===void 0)return;if(e instanceof Date)return Number.isNaN(e.getTime())?void 0:e.getTime();let t=Date.parse(e);return Number.isNaN(t)?void 0:t}function ty(e){if(process.platform==="win32")return["cmd.exe","/c",e.join(" ")];return["/bin/sh","-c",e.join(" ")]}function ry(e){return e.replace(/\\/g,"/").replace(/\/+$/,"")}async function f0(e){while(Qt(e))await m0(u0);return 0}async function vc(e,t,r){if(!e||typeof e==="number"||!r)return;let n=e.getReader(),o=new TextDecoder,i="";for(;;){let{value:s,done:a}=await n.read();if(a){if(i!=="")r(t,i);return}i+=o.decode(s,{stream:!0});let c=i.split(`
|
|
2257
|
+
`);i=c.pop()??"";for(let l of c)r(t,l.replace(/\r$/,""))}}async function ny(e,t){return Promise.race([e.then(()=>!0),new Promise((r)=>{setTimeout(()=>r(!1),t)})])}function m0(e){return new Promise((t)=>{setTimeout(t,e)})}import{existsSync as h0,readFileSync as g0,writeFileSync as v0}from"fs";var sy={exists:(e)=>h0(e),readText:(e)=>g0(e,"utf8"),writeText:(e,t)=>v0(e,t)};ht();var Tu=3,y0=3,b0=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=y0){return e>=t},shouldResetRestartBudget(e,t=b0){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 x0=2,w0=2000,S0=3,k0=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===Rr||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,_("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:w0,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:C0(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:x0;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:S0;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:k0;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===Rr||e===Et||e===dt}function C0(e){return e===Oe?"WARN":e===Bt?"INFO":"DEBUG"}ht();te();jr();te();At();var Du=[Rr,dt,fr,je,Et],P0={[ev]:[Et,sn,je,dt,fr,Rr],[Et]:[sn,je,dt],[sn]:[je,dt,Et,Rr],[je]:Du,[pr]:Du,[Br]:Du,[Rr]:[Et,dt],[dt]:[sn,Et,je],[fr]:[sn,je,dt,Et]};function cs(e,t){if(e===t)return!0;return(P0[e]??[]).includes(t)}function cy(e,t){if(!cs(e,t))throw _(Y,`illegal service lifecycle transition ${e} \u2192 ${t}`);return t}At();var _0=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 _(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,_(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 _(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 _(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,Rr,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 _(He,`failed to stop: ${o.join(", ")}`)}serviceIsActive(e){if(this.processes.isRunning(e))return!0;let t=this.host().runtimes.get(e);if(!t)return!1;if(t.state===fr||t.state===Rr||t.state===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 _(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 _(Td,`service ${e} failed while waiting for healthy`);await R0(_0)}throw _(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 _(He,`${e} exited with code ${i.code}`);return i}}function R0(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 _(Y,`secret manager request failed for ${t}: HTTP ${o.status}`);let i=await o.json();if(!i.payload?.data)throw _(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;E0(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 E0(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 W0,watch as G0}from"fs";import{isAbsolute as K0,join as q0,resolve as V0}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 N0}from"url";import{isAbsolute as H0,resolve as j0}from"path";import{createServer as yy,request as T0}from"http";import{request as A0}from"https";import{randomBytes as my}from"crypto";import{Readable as L0}from"stream";te();jr();var ds="x-devctl-request-id",D0="x-devctl-internal-token",hy=100,Ou=new Set(["connection","keep-alive","proxy-authenticate","proxy-authorization","te","trailers","transfer-encoding","upgrade"]),I0=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 _(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 _(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(_(Nr,`refusing to bind proxy to ${e}`));if(this.cfg.listen.port===0)return Promise.reject(_(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
|
|
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=no(c.auth),d=Ko(l),u,f=!1,p=(h,v)=>{if(f)return;f=!0;let b=Date.now()-n;this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:c.name,identity:d,status:h,durationMs:b,error:v}),this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:h>=400||v?"ERROR":"INFO",message:`${i} ${s} route=${c.name} identity=${d} status=${h} duration=${b}ms upgrade=true${v?` error=${v}`:""}`,pid:0,request_id:o,identity:d}),this.bus?.publish(Se(dd,c.name,{status:h,request_id:o,duration:b,identity:d}))},m=()=>{t.destroy(),u?.destroy()};try{let h={};for(let[S,y]of Object.entries(e.headers)){let x=S.toLowerCase();if(Ou.has(x)&&x!=="connection"&&x!=="upgrade")continue;if(typeof y==="string")h[S]=y;else if(Array.isArray(y))h[S]=y.join(",")}let v=e.socket.remoteAddress??"",b=h.host??"",g=h["x-forwarded-for"];if(h["x-forwarded-for"]=g?`${g}, ${v}`:v,h["x-forwarded-host"]=b,h["x-forwarded-proto"]="http",delete h.host,h[ds]=o,this.middleware.length===0)await Sc(c,h,this.tokens);for(let S of this.middleware)await S.apply({route:c,headers:h,tokens:this.tokens,req:e,method:i,path:s,upgrade:!0});let
|
|
2261
|
+
`),this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:"",identity:"",status:404,durationMs:Date.now()-n});return}let l=no(c.auth),d=Ko(l),u,f=!1,p=(h,v)=>{if(f)return;f=!0;let b=Date.now()-n;this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:c.name,identity:d,status:h,durationMs:b,error:v}),this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:h>=400||v?"ERROR":"INFO",message:`${i} ${s} route=${c.name} identity=${d} status=${h} duration=${b}ms upgrade=true${v?` error=${v}`:""}`,pid:0,request_id:o,identity:d}),this.bus?.publish(Se(dd,c.name,{status:h,request_id:o,duration:b,identity:d}))},m=()=>{t.destroy(),u?.destroy()};try{let h={};for(let[S,y]of Object.entries(e.headers)){let x=S.toLowerCase();if(Ou.has(x)&&x!=="connection"&&x!=="upgrade")continue;if(typeof y==="string")h[S]=y;else if(Array.isArray(y))h[S]=y.join(",")}let v=e.socket.remoteAddress??"",b=h.host??"",g=h["x-forwarded-for"];if(h["x-forwarded-for"]=g?`${g}, ${v}`:v,h["x-forwarded-host"]=b,h["x-forwarded-proto"]="http",delete h.host,h[ds]=o,this.middleware.length===0)await Sc(c,h,this.tokens);for(let S of this.middleware)await S.apply({route:c,headers:h,tokens:this.tokens,req:e,method:i,path:s,upgrade:!0});let R=gy(this.upstreamBase(c),s),E=O0(R)(R,{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",m),y.once("error",m);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}`}async function I0(e,t){if(!e.body){t.end();return}let r=A0.fromWeb(e.body);await new Promise((n,o)=>{r.on("error",o),t.on("error",o),t.on("finish",n),r.pipe(t)})}function M0(e){return e.protocol==="https:"?T0:E0}function gy(e,t){let r=new URL(e),n=new URL(t||"/",r),o=new URL(r.href);if(o.pathname=O0(n.pathname),o.search=n.search,o.hash="",o.origin!==r.origin)throw E(Nr,"refusing to proxy to a different origin");return o}function O0(e){return(e.startsWith("/")?e:`/${e}`).replace(/^\/+/,"/")}function ei(e,t,r){e.statusCode=t,e.setHeader("content-type","text/plain; charset=utf-8"),e.setHeader("x-content-type-options","nosniff"),e.end(r)}function vy(e,t){let r=(t.headers.host??"").split(":")[0]??"",n=t.url??"/";return e.find((o)=>{let i=o.match.host===""||o.match.host===r,s=o.match.path===""||n.startsWith(o.match.path);return i&&s})}class Fu{host;port;secret;tokens;server;constructor(e,t,r,n){this.host=e;this.port=t;this.secret=r;this.tokens=n}listenPort(){let e=this.server?.address();if(typeof e==="object"&&e)return e.port;return this.port}start(){let e=this.host||"127.0.0.1";if(!Cn(e))return Promise.reject(E(Nr,`refusing to bind token endpoint to ${e}`));return new Promise((t,r)=>{this.server=yy((n,o)=>{this.serve(n,o)}),this.server.on("error",(n)=>r(Pe(Nr,"token endpoint listen failed",n))),this.server.listen(this.port,e,()=>t())})}stop(){let e=this.server;if(!e)return Promise.resolve();return new Promise((t)=>e.close(()=>t()))}async serve(e,t){if(!$0(e.socket.remoteAddress)){ei(t,403,"forbidden");return}if((e.headers[L0]??"")!==this.secret){ei(t,401,"unauthorized");return}if(e.url?.startsWith("/token")!==!0){ei(t,404,"not found");return}let r=new URL(e.url,"http://127.0.0.1"),n=r.searchParams.get("identity")??"user",o=r.searchParams.get("audience")??"";try{let i=await this.tokens.get(n,o,[]);t.setHeader("content-type","application/json"),t.end(JSON.stringify({access_token:i.accessToken,token_type:i.tokenType,expires_at:i.expiresAt.toISOString(),identity:i.identity}))}catch{ei(t,500,"token error")}}}function $0(e){if(!e)return!0;return e==="127.0.0.1"||e==="::1"||e==="::ffff:127.0.0.1"}jr();var by=1;class xy{tokenProviders=[];identityProviders=[];pluginIdentityProviders=[];environmentSources=[];healthChecks=[];logParsers=[];proxyMiddleware=[];loadErrors=[];pluginPaths=[];registerBuiltins(){this.tokenProviders.push(..._n()),this.identityProviders.push(su(),au()),this.logParsers.push(Wm()),this.proxyMiddleware.push(B0())}register(e){j0(e),po(this.tokenProviders,e.tokenProviders),po(this.identityProviders,e.identityProviders),po(this.pluginIdentityProviders,e.identityProviders),po(this.environmentSources,e.environmentSources),po(this.healthChecks,e.healthChecks),po(this.logParsers,e.logParsers),po(this.proxyMiddleware,e.proxyMiddleware)}}async function kc(e,t=process.cwd()){let r=new xy;r.registerBuiltins();for(let n of e){if(n.trim()==="")continue;try{let o=n.startsWith("file:")?n:N0(n)?n:H0(t,n),s=await import(o.startsWith("file:")?o:F0(o).href);if(s.sdkVersion!==by)throw Error(`plugin SDK version ${String(s.sdkVersion??"missing")} is incompatible; expected ${by}`);r.register(s),r.pluginPaths.push(o)}catch(o){r.loadErrors.push({path:n,message:o instanceof Error?o.message:String(o)})}}return r}function j0(e){if(!e||typeof e!=="object")throw Error("plugin module must export an object");ti("tokenProviders",e.tokenProviders,["fetch"]),ti("identityProviders",e.identityProviders,["accepts","resolve"]),ti("environmentSources",e.environmentSources,["load"]),ti("healthChecks",e.healthChecks,["check"]),ti("logParsers",e.logParsers,["parse"]),ti("proxyMiddleware",e.proxyMiddleware,["apply"])}function ti(e,t,r){if(t===void 0)return;if(!Array.isArray(t))throw Error(`${e} must be an array`);for(let[n,o]of t.entries()){if(!o||typeof o!=="object"||typeof o.name!=="string"||o.name==="")throw Error(`${e}[${n}] must have a name`);for(let i of r)if(typeof o[i]!=="function")throw Error(`${e}[${n}].${i} must be a function`)}}function B0(){return{name:"identity_inject",apply:async(e)=>{await Sc(e.route,e.headers,e.tokens)}}}function po(e,t){if(!t)return;e.push(...t)}var V0=200;function Nu(e){if(!e.registry)return;if(e.registry.tokenProviders.length>0)e.tokens.replaceProviders(e.registry.tokenProviders);e.logs.setParsers(e.registry.logParsers,e.registry.pluginPaths),e.proxy?.setMiddleware?.(e.registry.proxyMiddleware)}function Hu(e,t){let r=new Map;for(let n of e){let o=Y0(n,t);try{r.set(o,U0(o).mtimeMs)}catch{}}return r}async function z0(e,t,r){let n=t.plugins.map((a)=>a.path),o=JSON.stringify(r)!==JSON.stringify(n),i=Hu(n,t.repoRoot),s=[];for(let[a,c]of i){let l=e.pluginMtimes.get(a);if(l!==void 0&&l!==c){s.push("plugins");break}}if(o){let a=await kc(n,t.repoRoot);e.registry=a;for(let c of a.loadErrors)e.log("devctl","ERROR",`plugin ${c.path} skipped: ${c.message}`);Nu(e),e.log("devctl","INFO","plugin path list reloaded")}return e.pluginMtimes=i,s}function Y0(e,t){if(e.startsWith("file:"))return e;return G0(e)?e:q0(t,e)}function ju(e,t){let r=ru(t);if(r.length===0)return;let n=new Set((e?.healthChecks??[]).map((i)=>i.name.toLowerCase())),o=r.filter((i)=>!n.has(i.type.toLowerCase()));if(o.length>0)throw E(Y,`unknown health check type(s): ${o.map((i)=>`${i.service}.health.type=${i.type}`).join(", ")}`)}function Bu(e,t){let r=nu(t);if(r.length===0)return;let n=e?.pluginIdentityProviders??[],o=r.filter(({service:i})=>{let s=t.services[i];return!s||!n.some((a)=>a.accepts(s.identity))});if(o.length>0)throw E(Y,`unknown identity type(s): ${o.map((i)=>`${i.service}.identity.type=${i.type}`).join(", ")}`)}function Uu(e,t){let r=new Set(es),n=new Set((e?.environmentSources??[]).map((i)=>i.name)),o=t.environment.sources.filter((i)=>!r.has(i)&&!n.has(i));if(o.length>0)throw E(Y,`unknown environment source(s): ${o.join(", ")}`)}function J0(e,t,r){for(let n of Object.keys(r))if(!t[n]&&!e.runtimes.has(n))e.runtimes.set(n,Qi(n));for(let n of Object.keys(t)){if(r[n])continue;if(e.orchestrator.serviceIsActive(n)){let o=e.runtimes.get(n);if(o)o.orphaned=!0;e.log(n,"WARN","removed from configuration while still running; now orphaned \u2014 stop it explicitly to clean it up");continue}e.forgetService(n)}}function Wu(e){let t=K0(e.cfg.repoRoot,".devctl");if(!e.fs.exists(t))return;try{e.configWatcher=W0(t,{recursive:!0},()=>{if(e.watchTimer)clearTimeout(e.watchTimer);e.watchTimer=setTimeout(()=>{e.reload().catch((r)=>e.log("devctl","WARN",L(r)))},V0)})}catch{e.log("devctl","WARN","unable to watch .devctl for configuration changes")}}async function wy(e){let t;try{t=nn(e.cfg.repoRoot,e.cfg.configPath)}catch(l){throw e.bus.publish(Se(Ti,"",{error:L(l)})),e.log("devctl","ERROR",`configuration reload failed: ${L(l)}`),l}try{ju(e.registry,t),Bu(e.registry,t),Uu(e.registry,t)}catch(l){throw e.bus.publish(Se(Ti,"",{error:L(l)})),e.log("devctl","ERROR",`configuration reload failed: ${L(l)}`),l}if(e.setupMode){if(e.setupMode=!1,e.log("devctl","INFO",`configuration created at ${e.cfg.configPath}; leaving setup mode`),!e.configWatcher)Wu(e)}let r=Mu(e.cfg,t),n=JSON.stringify(e.cfg.proxy)!==JSON.stringify(t.proxy),o=JSON.stringify(e.cfg.secrets)!==JSON.stringify(t.secrets),i=e.cfg.plugins.map((l)=>l.path),s=e.cfg.services;e.cfg=fy(e.cfg,t),J0(e,s,t.services);let a=X0(e.restartRequired,r.restart_required,Object.keys(t.services));e.restartRequired=a,r.restart_required=a;let c=await z0(e,t,i);if(c.length>0)r.supervisor_restart_required=[...r.supervisor_restart_required??[],...c];if(e.syncServiceWatchers(),o)e.detector.update(t.secrets.extra_markers,t.secrets.extra_patterns),e.logs.setSecrets(t.secrets.extra_markers,t.secrets.extra_patterns);if(n){let l=e.proxy?.isRunning()??!1;if(await e.stopProxy(),l&&e.cfg.proxy.enabled)await e.startProxy();e.log("devctl","INFO","proxy configuration changed; proxy restarted")}if(e.bus.publish(Se(da,"",{restart_required:r.restart_required,changes:r.changes,supervisor_restart_required:r.supervisor_restart_required})),e.log("devctl","INFO",r.restart_required.length===0?"configuration reloaded":`configuration reloaded; restart required: ${r.restart_required.join(", ")}`),r.supervisor_restart_required)e.log("devctl","WARN",sc(r.supervisor_restart_required));return e.persistState(),e.refreshIdentity(),r}function X0(e,t,r){let n=new Set(r),o=new Set(e.filter((i)=>n.has(i)));for(let i of t)if(n.has(i))o.add(i);return[...o].sort()}import{watch as Z0}from"fs";import{join as ky,relative as eA}from"path";ht();function Sy(e,t){if(!e?.enabled)return!1;let r=t.replaceAll("\\","/").replace(/^\.\//,"");if(r===""||r.includes(".."))return!1;if((e.ignore.length>0?e.ignore:$i).some((o)=>Q0(o,r)))return!1;if(e.paths.length===0)return!1;return e.paths.some((o)=>{let i=o.replaceAll("\\","/").replace(/\/$/,"");return r===i||r.startsWith(`${i}/`)})}function Q0(e,t){let r=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replaceAll("**/","<<<DS>>>").replaceAll("**","<<<DS>>>").replaceAll("*","[^/]*").replaceAll("<<<DS>>>",".*");return new RegExp(`^${r}$`).test(t)}class Gu{host;entries=new Map;constructor(e){this.host=e}sync(e){let t=new Set;for(let[r,n]of Object.entries(e)){if(!n.watch.enabled||n.watch.paths.length===0){this.stop(r);continue}t.add(r);let o=JSON.stringify(n.watch);if(this.entries.get(r)?.signature===o)continue;this.stop(r),this.start(r,n.watch)}for(let r of[...this.entries.keys()])if(!t.has(r))this.stop(r)}watched(){return[...this.entries.keys()].sort()}close(){for(let e of[...this.entries.keys()])this.stop(e)}start(e,t){let r=[],n=this.host.repoRoot();for(let o of t.paths){let i=o.replaceAll("\\","/").replace(/\/$/,"");if(i===""||i==="."){this.host.log(e,"WARN","service.watch skipped a whole-repo path; list concrete directories");continue}let s=ky(n,i);try{let a=Z0(s,{recursive:!0},(c,l)=>{let d=l?ky(s,l.toString()):s,u=eA(n,d).replaceAll("\\","/");if(!Sy(t,u))return;this.schedule(e,t.debounce_ms)});r.push(a)}catch{this.host.log(e,"WARN",`unable to watch ${i}`)}}if(r.length===0)return;this.entries.set(e,{signature:JSON.stringify(t),watch:t,handles:r}),this.host.log(e,"INFO",`watching ${t.paths.join(", ")}`)}schedule(e,t){let r=this.entries.get(e);if(!r)return;if(r.timer)clearTimeout(r.timer);r.timer=setTimeout(()=>{if(r.timer=void 0,!this.host.isActive(e))return;this.host.log(e,"INFO","watch: restarting after file change"),this.host.restart(e).catch((n)=>{this.host.log(e,"WARN",n instanceof Error?n.message:String(n))})},t)}stop(e){let t=this.entries.get(e);if(!t)return;if(t.timer)clearTimeout(t.timer);for(let r of t.handles)r.close();this.entries.delete(e)}}import{isAbsolute as Ku,join as qu}from"path";class Vu{clientEnv=new Map;serviceProfile=new Map;serviceProfileEnv=new Map;profile="";profileEnv={};deps;constructor(e){this.deps=e}forget(e){this.clientEnv.delete(e),this.serviceProfile.delete(e),this.serviceProfileEnv.delete(e)}serviceWorkDir(e){if(e.working_dir!==""&&!Ku(e.working_dir))return qu(this.deps.cfg().repoRoot,e.working_dir);return e.working_dir}async resolveServiceExecution(e,t,r,n,o,i=!0){let s=this.deps.cfg(),a=this.deps.ports().get(e)??Object.fromEntries(t.ports.filter((p)=>!p.auto).map((p)=>[p.name,p.value])),c=this.deps.proxy(),l=c?.isRunning()?`http://${c.address()}`:s.proxy.enabled?`http://${bn(s.proxy.listen)}`:"",d=rs(e,"127.0.0.1",a,l,s.project.name);if(!t.container){if(d.DEVCTL_INTERNAL_TOKEN=this.deps.internalTok(),s.proxy.token_endpoint.enabled)d.DEVCTL_TOKEN_URL=this.deps.boundTokenURL()||`http://127.0.0.1:${this.deps.tokenEndpoint()?.listenPort()||s.proxy.token_endpoint.port}/token`}let u=await ts(s.repoRoot,{service:e,profile:r,serviceCfg:t,profileEnv:n,assignedPorts:a,runtime:d,cfg:s,fetchSecret:ls(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources(),clientEnv:o,includeProcess:i}),f=t.working_dir&&!Ku(t.working_dir)?qu(s.repoRoot,t.working_dir):t.working_dir;return{env:Qo(u),workDir:f}}async resolveTaskEnvironment(e,t,r){let n=this.deps.cfg(),o=await ts(n.repoRoot,{service:`task:${e}`,profile:this.profile,serviceCfg:t,profileEnv:this.profileEnv,assignedPorts:{},runtime:rs(`task:${e}`,"127.0.0.1",{},"",n.project.name),cfg:n,clientEnv:r,fetchSecret:ls(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources()}),i=t.working_dir&&!Ku(t.working_dir)?qu(n.repoRoot,t.working_dir):t.working_dir;return{env:Qo(o),workDir:i}}}jr();te();jr();At();import{cpus as dA,loadavg as uA,platform as pA,uptime as fA}from"os";import{readFileSync as tA}from"fs";import{freemem as rA,totalmem as nA}from"os";import{spawnSync as oA}from"child_process";var zu=1024,iA=2000,sA=/page size of (\d+) bytes/i,ri;function aA(e){let t=e.match(/^MemAvailable:\s+(\d+)/m);if(!t)return;let r=Number(t[1]);return Number.isFinite(r)&&r>=0?r:void 0}function cA(e){let t=e.match(sA),r=t?Number(t[1]):0;if(!Number.isFinite(r)||r<=0)return;let n=Cc(e,"free")+Cc(e,"inactive")+Cc(e,"speculative")+Cc(e,"purgeable");return Math.round(n*r/zu)}function Cc(e,t){let r=e.match(new RegExp(`Pages ${t}:\\s+(\\d+)`,"i")),n=r?Number(r[1]):0;return Number.isFinite(n)&&n>0?n:0}function lA(){if(process.platform==="linux")try{return aA(tA("/proc/meminfo","utf8"))}catch{return}if(process.platform==="darwin"){let e=Date.now();if(ri&&e-ri.at<iA)return ri.leftoverKB;let t=oA("vm_stat",{encoding:"utf8"});if(t.status!==0||!t.stdout)return ri?.leftoverKB;let r=cA(t.stdout);if(r===void 0)return ri?.leftoverKB;return ri={at:e,leftoverKB:r},r}return}function Cy(){let e=Math.max(0,Math.round(nA()/zu)),t=Math.max(0,Math.round(rA()/zu)),r=Math.max(0,Math.min(e,lA()??t));return{totalKB:e,unusedKB:t,leftoverKB:r}}function Py(e){return{user:"",project:e?.google.project_id??"",project_source:e?.google.project_id?"configuration":"",adc:!1,service_accounts:{},service_account_status:Object.fromEntries(e?Hr(e).map((t)=>[t,"unknown"]):[]),iap:e?.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap")??!1}}function Yu(){let e=uA(),t=Cy();return{platform:pA(),cpuCount:dA().length,loadAvg1:e[0]??0,loadAvg5:e[1]??0,loadAvg15:e[2]??0,memTotalKB:t.totalKB,memFreeKB:t.unusedKB,memAvailableKB:t.leftoverKB,hostUptimeSec:fA()}}function Ju(e,t){let r={},n={};for(let o of Hr(e)){let i=t.get(o)??"unknown";if(n[o]=i,i!=="unknown")r[o]=i==="available"}return{service_accounts:r,service_account_status:n}}function _y(e){let t={};for(let[o,i]of e.runtimes)t[o]={...i,ports:e.ports.get(o)??i.ports,profile:e.serviceProfile.get(o)??i.profile,env_source:e.clientEnv.has(o)?"client":"daemon"};let r=e.proxy?.stats(),n={requestTotal:r?.total??0,requestErrors:r?.errors??0,recentRequests:r?.recent??[]};return{session_id:e.sessionID,repo_root:e.cfg.repoRoot,profile:e.profile,services:t,proxy:{running:e.proxy?.isRunning()??!1,address:e.proxy?.address(),routes:e.cfg.proxy.routes.map((o)=>{let i=o.match.host||"*",s=o.match.path,a=s===""?i:`${i}${s.startsWith("/")?s:`/${s}`}`;return{name:o.name,identity:o.auth.identity.service_account||o.auth.identity.type||o.auth.type,upstream:o.upstream.url,auth:o.auth.type,match:a,client_id:o.auth.client_id.trim()||void 0}}),...n},mcp:{running:e.mcp?.isRunning()??!1,address:e.mcp?.isRunning()?`http://${e.mcp.address()}/mcp`:void 0,port:e.mcp?.isRunning()?e.mcp.listenPort():void 0,token:e.mcpToken,disabled_tools:[...e.mcpDisabledTools]},identity:{...e.identityCache,...Ju(e.cfg,e.serviceAccountStatus)},credentials:{backend:e.tokens.storeBackend(),entries:[...e.credentialEntries]},detached:e.detached,setup_mode:e.setupMode?!0:void 0,logs:e.logs.snapshot(),restart_required:[...e.restartRequired],system:Yu(),stats_series:e.statsSeries}}function Ry(e){let t=[`PROFILE: ${e.profile||"(none)"}`,"","SERVICE\tSTATUS\tHEALTH\tPID"];for(let[r,n]of Object.entries(e.services))t.push(`${r} ${Jo(n)} ${n.health} ${n.pid}`);return t.push("",`PROXY ${e.proxy.running?"RUNNING":"STOPPED"} ${e.proxy.address??""}`),t.push(`MCP ${e.mcp?.running?"RUNNING":"STOPPED"} ${e.mcp?.address??""}`),t.join(`
|
|
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 R=(g.headers.get("content-encoding")??"").split(",").map((y)=>y.trim().toLowerCase()).filter((y)=>y!==""),E=R.length>0&&R.every((y)=>I0.has(y));g.headers.forEach((y,x)=>{let w=x.toLowerCase();if(Ou.has(w))return;if(E&&(w==="content-encoding"||w==="content-length"))return;t.setHeader(x,this.detector?this.detector.redactText(y):y)}),await M0(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 _(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 M0(e,t){if(!e.body){t.end();return}let r=L0.fromWeb(e.body);await new Promise((n,o)=>{r.on("error",o),t.on("error",o),t.on("finish",n),r.pipe(t)})}function O0(e){return e.protocol==="https:"?A0:T0}function gy(e,t){let r=new URL(e),n=new URL(t||"/",r),o=new URL(r.href);if(o.pathname=$0(n.pathname),o.search=n.search,o.hash="",o.origin!==r.origin)throw _(Nr,"refusing to proxy to a different origin");return o}function $0(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(_(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(!F0(e.socket.remoteAddress)){ei(t,403,"forbidden");return}if((e.headers[D0]??"")!==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 F0(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(U0())}register(e){B0(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:H0(n)?n:j0(t,n),s=await import(o.startsWith("file:")?o:N0(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 B0(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 U0(){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 z0=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=J0(n,t);try{r.set(o,W0(o).mtimeMs)}catch{}}return r}async function Y0(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 J0(e,t){if(e.startsWith("file:"))return e;return K0(e)?e:V0(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 _(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 _(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 _(Y,`unknown environment source(s): ${o.join(", ")}`)}function X0(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=q0(e.cfg.repoRoot,".devctl");if(!e.fs.exists(t))return;try{e.configWatcher=G0(t,{recursive:!0},()=>{if(e.watchTimer)clearTimeout(e.watchTimer);e.watchTimer=setTimeout(()=>{e.reload().catch((r)=>e.log("devctl","WARN",L(r)))},z0)})}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),X0(e,s,t.services);let a=Q0(e.restartRequired,r.restart_required,Object.keys(t.services));e.restartRequired=a,r.restart_required=a;let c=await Y0(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 Q0(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 eA}from"fs";import{join as ky,relative as tA}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)=>Z0(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 Z0(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=eA(s,{recursive:!0},(c,l)=>{let d=l?ky(s,l.toString()):s,u=tA(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 uA,loadavg as pA,platform as fA,uptime as mA}from"os";import{readFileSync as rA}from"fs";import{freemem as nA,totalmem as oA}from"os";import{spawnSync as iA}from"child_process";var zu=1024,sA=2000,aA=/page size of (\d+) bytes/i,ri;function cA(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 lA(e){let t=e.match(aA),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 dA(){if(process.platform==="linux")try{return cA(rA("/proc/meminfo","utf8"))}catch{return}if(process.platform==="darwin"){let e=Date.now();if(ri&&e-ri.at<sA)return ri.leftoverKB;let t=iA("vm_stat",{encoding:"utf8"});if(t.status!==0||!t.stdout)return ri?.leftoverKB;let r=lA(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(oA()/zu)),t=Math.max(0,Math.round(nA()/zu)),r=Math.max(0,Math.min(e,dA()??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=pA(),t=Cy();return{platform:fA(),cpuCount:uA().length,loadAvg1:e[0]??0,loadAvg5:e[1]??0,loadAvg15:e[2]??0,memTotalKB:t.totalKB,memFreeKB:t.unusedKB,memAvailableKB:t.leftoverKB,hostUptimeSec:mA()}}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(`
|
|
2277
2277
|
`)+`
|
|
2278
|
-
`}var
|
|
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,proxy:()=>this.proxy.instance,tokenEndpoint:()=>this.proxy.tokenEndpoint,boundTokenURL:()=>this.proxy.boundTokenURL,internalTok:()=>this.internalTok,tokens:this.tokens,environmentSources:()=>this.registry?.environmentSources}),this.mcp=new ep({repoRoot:()=>this.cfg.repoRoot,createListener:t.createMcpListener,hostApi:()=>this.asMcpHost(),isKnownTool:t.isKnownTool,log:(r,n,o)=>this.log(r,n,o),persistState:()=>this.persistState()}),this.resources=new rp({clock:this.clock,runtimes:()=>this.runtimes}),this.identity=new Xu({cfg:()=>this.cfg,tokens:this.tokens,detectGoogle:(r)=>this.detectGoogleFn(r),clock:this.clock,bus:this.bus,logs:this.logs,persistState:()=>this.persistState(),fail:(r,n)=>this.fail(r,n),log:(r,n,o)=>this.log(r,n,o),identityProviders:()=>this.registry?.identityProviders}),this.rpc=new op({dispatch:(r,n)=>this.dispatch(r,n),subscribe:(r,n)=>this.bus.subscribe(r,n),log:(r,n,o)=>this.log(r,n,o),socketExists:(r)=>this.socketExistsFn(r),unlinkSocket:(r)=>this.unlinkSocketFn(r)}),this.setupMode=!this.fs.exists(e.configPath);for(let r of Object.keys(e.services))this.runtimes.set(r,Qi(r));this.orchestrator.bind(this.lifecycleSession()),this.commands=t.createCommands(this),this.serviceWatchers=new Gu({repoRoot:()=>this.cfg.repoRoot,log:(r,n,o)=>this.log(r,n,o),isActive:(r)=>this.orchestrator.serviceIsActive(r),restart:(r)=>this.restart([r],{auto:!0})})}get profile(){return this.env.profile}set profile(e){this.env.profile=e}get profileEnv(){return this.env.profileEnv}set profileEnv(e){this.env.profileEnv=e}get clientEnv(){return this.env.clientEnv}get serviceProfile(){return this.env.serviceProfile}get serviceProfileEnv(){return this.env.serviceProfileEnv}snapshotHost(){let e=this;return{get sessionID(){return e.sessionID},get cfg(){return e.cfg},get profile(){return e.profile},get runtimes(){return e.runtimes},get ports(){return e.ports},get serviceProfile(){return e.serviceProfile},get clientEnv(){return e.clientEnv},get proxy(){return e.proxy.instance},get mcp(){return e.mcp.instance},get mcpToken(){return e.mcp.token},get mcpDisabledTools(){return e.mcp.disabledTools},get identityCache(){return e.identity.identityCache},get serviceAccountStatus(){return e.identity.serviceAccountStatus},get credentialEntries(){return e.identity.credentialEntries},get detached(){return e.detached},get setupMode(){return e.setupMode},get restartRequired(){return e.restartRequired},get statsSeries(){return e.resources.series()},logs:{snapshot:()=>e.logs.snapshot()},tokens:{storeBackend:()=>e.tokens.storeBackend()}}}reloadHost(){let e=this;return{get cfg(){return e.cfg},set cfg(t){e.cfg=t},get setupMode(){return e.setupMode},set setupMode(t){e.setupMode=t},get configWatcher(){return e.configWatcher},set configWatcher(t){e.configWatcher=t},get watchTimer(){return e.watchTimer},set watchTimer(t){e.watchTimer=t},get restartRequired(){return e.restartRequired},set restartRequired(t){e.restartRequired=t},get fs(){return e.fs},get registry(){return e.registry},set registry(t){e.registry=t},get pluginMtimes(){return e.pluginMtimes},set pluginMtimes(t){e.pluginMtimes=t},get detector(){return e.detector},get bus(){return e.bus},get orchestrator(){return e.orchestrator},get runtimes(){return e.runtimes},get tokens(){return e.tokens},get logs(){return e.logs},get proxy(){return e.proxy.instance},persistState:()=>e.persistState(),log:(t,r,n)=>e.log(t,r,n),refreshIdentity:()=>e.refreshIdentity(),startProxy:()=>e.startProxy(),stopProxy:()=>e.stopProxy(),reload:()=>e.reload(),forgetService:(t)=>e.forgetService(t),syncServiceWatchers:()=>e.serviceWatchers.sync(e.cfg.services)}}recoverHost(){let e=this;return{get cfg(){return e.cfg},set cfg(t){e.cfg=t},get profile(){return e.profile},set profile(t){e.profile=t},get profileEnv(){return e.profileEnv},set profileEnv(t){e.profileEnv=t},get runtimes(){return e.runtimes},get ports(){return e.ports},get processMeta(){return e.processMeta},get serviceProfile(){return e.serviceProfile},get serviceProfileEnv(){return e.serviceProfileEnv},get orchestrator(){return e.orchestrator},get procs(){return e.procs},logs:{append:(t)=>e.logs.append(t)},get clock(){return e.clock},get tokens(){return e.tokens},get registry(){return e.registry},get proxy(){return e.proxy.instance},get tokenEP(){return e.proxy.tokenEndpoint},get boundTokenURL(){return e.proxy.boundTokenURL},get internalTok(){return e.internalTok},get bus(){return e.bus},inspectProcessFn:(t)=>e.inspectProcessFn(t),processAliveFn:(t)=>e.processAliveFn(t),serviceWorkDir:(t)=>e.serviceWorkDir(t),persistState:()=>e.persistState(),setState:(t,r,n,o,i)=>e.setState(t,r,n,o,i),log:(t,r,n)=>e.log(t,r,n)}}async run(){let e=ea(this.cfg.repoRoot);this.lock=this.acquireLockFn(this.cfg.repoRoot,e),this.rpc.removeStaleSocket(e),this.registry=await kc(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot),this.pluginMtimes=Hu(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot);for(let t of this.registry.loadErrors)this.log("devctl","ERROR",`plugin ${t.path} skipped: ${t.message}`);Nu(this.reloadHost()),ju(this.registry,this.cfg),Bu(this.registry,this.cfg),Uu(this.registry,this.cfg),await this.recoverSession(),this.serviceWatchers.sync(this.cfg.services),Wu(this.reloadHost()),this.persistState(),this.log("devctl","INFO",`supervisor started session=${this.sessionID}`),this.refreshIdentity(),this.resources.start(),await this.mcp.bootFromPreferences(),this.rpc.removeStaleSocket(e),await this.rpc.listen(e)}async dispatch(e,t){let r=np(t)?t:{};switch(e){case"ping":return{session:this.sessionID,version:jt,protocol:Yi};case"start":return this.commands.startService.execute({services:En(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0,detach:r.detach===!0,client_env:us(r.client_env)});case"stop":return await this.commands.stopService.execute(En(r.services)),null;case"restart":return await this.commands.restartService.execute(En(r.services),{cascade:r.cascade===!0,clientEnv:us(r.client_env)}),null;case"run_task":return this.runTask(typeof r.name==="string"?r.name:"",us(r.client_env)??{});case"exec":return this.execService(typeof r.service==="string"?r.service:"",En(r.command),us(r.client_env)??{},r.print_env===!0);case"auth_refresh":return await this.commands.refreshIdentity.execute(),this.snapshot().identity;case"status":return this.snapshot();case"logs":return await this.queryLogs({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:"",export:typeof r.export==="string"?r.export:""});case"logs_page":return await this.queryLogsPage({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:"",cursor:typeof r.cursor==="string"?r.cursor:void 0,direction:r.direction==="forward"?"forward":"backward",limit:typeof r.limit==="number"?r.limit:void 0});case"logs_stats":return await this.queryLogsFacets({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:""});case"proxy_start":return this.proxy.setSuppressed(!1),await this.commands.startProxy.execute(),null;case"proxy_stop":return this.proxy.setSuppressed(!0),await this.commands.stopProxy.execute(),null;case"mcp_start":{let o=typeof r.port==="number"?r.port:void 0;return await this.startMcp(o??Do(this.cfg.repoRoot).mcp_port),null}case"mcp_stop":return await this.stopMcp(),null;case"mcp_set_tools":{let o=Array.isArray(r.disabled)?r.disabled.filter((i)=>typeof i==="string"):[];return this.setMcpDisabledTools(o),{disabled_tools:[...this.mcp.disabledTools]}}case"reload":return this.commands.reloadConfig.execute();case"config_snapshot":return this.cfg;case"auth_invalidate":return this.tokens.invalidate(),null;case"shutdown":let n=typeof r.stop_services==="boolean"?r.stop_services:Ta(this.cfg.shutdown);return setTimeout(()=>{this.shutdown(n)},50),null;default:throw E(lt,`unknown method ${e}`)}}isDetached(){return this.detached}async start(e){return this.orchestrator.start(e)}lifecycleSession(){let e=this;return{get cfg(){return e.cfg},get profile(){return e.profile},set profile(t){e.profile=t},get profileEnv(){return e.profileEnv},set profileEnv(t){e.profileEnv=t},get detached(){return e.detached},set detached(t){e.detached=t},get proxySuppressed(){return e.proxy.isSuppressed},get runtimes(){return e.runtimes},get ports(){return e.ports},get clientEnv(){return e.clientEnv},get serviceProfile(){return e.serviceProfile},get serviceProfileEnv(){return e.serviceProfileEnv},healthCheckers:{lookup:(t)=>{let r=e.registry?.healthChecks.find((n)=>n.name.toLowerCase()===t.toLowerCase());return r?{check:async(n,o)=>{let i=await r.check(n,o);return{...i,status:i.status}}}:e.healthCheckers.lookup(t)}},logs:{append:(t)=>e.logs.append(t)},bus:e.bus,processMeta:e.processMeta,get containerPrefix(){return`devctl-${ar(e.cfg.repoRoot)}-`},prepareServiceIdentity:(t,r)=>e.identity.prepareServiceIdentity(t,r),resolveServiceExecution:(t,r,n,o,i,s)=>e.env.resolveServiceExecution(t,r,n,o,i,s),detectGoogle:(t)=>e.detectGoogleFn(t),startProxy:()=>e.startProxy(),fail:(t,r)=>e.fail(t,r),claimIfAlreadyUp:(t)=>e.claimIfAlreadyUp(t),assignPendingPorts:(t)=>e.assignPendingPorts(t),setState:(t,r,n,o,i)=>e.setState(t,r,n,o,i),persistState:()=>e.persistState(),log:(t,r,n)=>e.log(t,r,n),releasePorts:(t)=>e.releasePorts(t),forgetService:(t)=>e.forgetService(t),clearRestartRequired:(t)=>e.dropRestartRequired(t)}}async assignPendingPorts(e){try{let t=await hh(this.cfg,e,Object.fromEntries(this.ports));for(let[r,n]of Object.entries(t)){this.ports.set(r,n);let o=Object.entries(n).map(([i,s])=>`${i}=${s}`).join(", ");this.log(r,"INFO",`assigned ports: ${o||"none"}`)}}catch(t){if(t instanceof Ve&&t.service!=="")await this.fail(t.service,t);else this.log("devctl","ERROR",L(t));throw t}}async runTask(e,t){let r=this.cfg.tasks[e];if(!r)throw E(lt,`unknown task ${e}`);if(r.dependencies.length>0)await this.start({services:r.dependencies,client_env:t});let n={...vn(),command:r.command,shell:r.shell,working_dir:r.working_dir,dependencies:r.dependencies,environment:r.environment},{env:o,workDir:i}=await this.env.resolveTaskEnvironment(e,n,t),s=await this.runTransient(`task:${e}`,r.command,r.shell,i,o);return{task:e,...s}}async execService(e,t,r,n=!1){let o=this.cfg.services[e];if(!o)throw E(Ho,`unknown service ${e}`);let i=this.env.serviceProfile.get(e)??this.env.profile,s=i!==""?lo(this.cfg,i):this.env.profileEnv,{env:a,workDir:c}=await this.env.resolveServiceExecution(e,o,i,s,r,!o.container);if(n)return{service:e,code:0,stdout:"",stderr:"",environment:a};if(t.length===0)throw E(lt,"exec command is required");let l=await this.runTransient(`${e}:exec`,{args:t,shell:!1},!1,c,a);return{service:e,...l}}async runTransient(e,t,r,n,o){if(yn(t))return{code:0,stdout:"",stderr:""};this.log(e,"INFO",`running ${t.args.join(" ")}`);let i=await this.procs.runOnce({name:e,args:[...t.args],shell:r||t.shell,workDir:n,env:o,graceMs:xn(this.cfg.shutdown)*1000,onLine:(s,a)=>this.logs.append({timestamp:this.clock.isoNow(),service:e,source:s,stream:s,level:"",message:a,pid:0})});if(i.code!==0)throw E(He,`${e} exited with code ${i.code}`);return i}serviceWorkDir(e){return this.env.serviceWorkDir(e)}async claimIfAlreadyUp(e){return uy(this.recoverHost(),e)}async recoverSession(){return py(this.recoverHost())}async stop(e){return this.orchestrator.stop(e)}forgetService(e){this.orchestrator.health.forget(e),this.runtimes.delete(e),this.ports.delete(e),this.processMeta.delete(e),this.env.forget(e)}async restart(e,t){let r=t?.cascade===!0?co(this.cfg,e):e;await this.orchestrator.restart(e,t),this.dropRestartRequired(r)}dropRestartRequired(e){if(e.length===0)return;let t=new Set(e);this.restartRequired=this.restartRequired.filter((r)=>!t.has(r))}async startProxy(){return this.proxy.start()}async stopProxy(){return this.proxy.stop()}async startMcp(e){return this.mcp.start(e)}setMcpDisabledTools(e){this.mcp.setDisabledTools(e)}async stopMcp(){return this.mcp.stop()}asMcpHost(){return{status:()=>this.commands.getServiceStatus.execute(),logsPage:(e)=>this.queryLogsPage(e),config:()=>this.cfg,validateConfigText:(e)=>Ui(this.cfg.repoRoot,this.cfg.configPath,e),start:(e)=>this.commands.startService.execute(e),stop:(e)=>this.commands.stopService.execute(e),restart:(e,t)=>this.commands.restartService.execute(e,{cascade:t}),reload:()=>this.commands.reloadConfig.execute(),doctor:async()=>{for(let e of Hr(this.cfg))await this.identity.probeServiceAccount(e);return this.commands.runDoctor.execute(this.cfg)},exec:(e,t,r)=>this.execService(e,t,void 0,r),runTask:(e)=>this.runTask(e,{}),startProxy:()=>this.startProxy(),stopProxy:()=>this.stopProxy()}}async reload(){return wy(this.reloadHost())}async shutdown(e){if(this.shuttingDown)return;if(this.shuttingDown=!0,this.persistState(),e)await this.stop([]);if(this.orchestrator.health.dispose(),await this.stopProxy(),await this.stopMcp(),this.configWatcher?.close(),this.serviceWatchers.close(),this.watchTimer)clearTimeout(this.watchTimer);this.resources.stop(),this.rpc.close(),this.lock?.release(),await this.logs.close()}snapshot(){return _y(this.snapshotHost())}async refreshIdentity(e){return this.identity.refreshIdentity(e)}async queryLogs(e){let t=await this.logs.query({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until});if(e.export)await this.logs.exportTo(e.export,{services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until});return{events:t}}async queryLogsPage(e){return this.logs.queryPage({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until},{cursor:e.cursor,direction:e.direction,limit:e.limit})}async queryLogsFacets(e){return this.logs.queryFacets({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until})}subscribe(e){return this.bus.subscribe(e)}formatStatus(){return Ry(this.snapshot())}setState(e,t,r,n,o){let i=this.runtimes.get(e)??Qi(e);if(!cs(i.state,t))this.log(e,"WARN",`unusual lifecycle ${i.state} \u2192 ${t}`);i.state=t,i.health=r,i.pid=n,i.last_error=o;let s=t===je||t===pr||t===Br;if(i.startTime=s?this.processMeta.get(e)?.startTime.toISOString():void 0,!s)i.cpuPercent=void 0,i.memoryKB=void 0;this.runtimes.set(e,i),this.bus.publish(Se(Om,e,{state:t,health:r,pid:n}))}async releasePorts(e){let t=this.ports.get(e);if(!t)return;let r=this.cfg.services[e];if(r?.container){this.ports.delete(e);return}let n=this.processMeta.get(e);for(let o of Object.values(t)){let i=await zn(o);if(!i||i.pid===process.pid)continue;if(r){let s=await this.inspectProcessFn(i.pid);if(!(s!==void 0&&s.command!==""&&ss({args:n?.command??[...r.command.args],workDir:n?.cwd??this.serviceWorkDir(r),startTime:n?.startTime},s))){this.log(e,"WARN",`port ${o} is held by pid ${i.pid}, which does not match ${e}; leaving it running`);continue}}try{await wa(i),this.log(e,"INFO",`released port ${o} (pid ${i.pid})`)}catch(s){this.log(e,"WARN",L(s))}}this.ports.delete(e)}async fail(e,t){this.orchestrator.health.clearHealthWatch(e),this.orchestrator.health.clearRestartTimer(e),this.orchestrator.health.bumpGeneration(e),this.setState(e,dt,Tt,0,L(t));try{await this.procs.stop(e,xn(this.cfg.shutdown)*1000)}catch(r){this.log(e,"WARN",L(r))}this.bus.publish(Se(la,e,{error:L(t)})),this.log(e,"ERROR",L(t)),this.persistState()}log(e,t,r){this.logs.append({timestamp:this.clock.isoNow(),service:e,source:"devctl",level:t,message:r,pid:0})}persistState(){let e=[];for(let t of this.procs.all()){let r=this.processMeta.get(t.name);e.push({name:t.name,pid:t.pid,command:r?.command??t.args,cwd:r?.cwd??t.workDir,startTime:(r?.startTime??t.startTime).toISOString(),ports:this.ports.get(t.name)??{},profile:this.serviceProfile.get(t.name)??this.profile})}Rm(this.cfg.repoRoot,{session_id:this.sessionID,repo_root:this.cfg.repoRoot,profile:this.profile,processes:e})}}var Fy=500,_A=1e4,RA=2000,EA=new URL("./log-worker.ts",import.meta.url);class Ny{worker;bus;pending=new Map;nextId=1;stats={total:0,errors:0,counts:{}};dead=!1;readySettled=!1;ready;resolveReady=()=>{return};rejectReady=()=>{return};constructor(e,t,r={}){this.bus=t,this.ready=new Promise((n,o)=>{this.resolveReady=n,this.rejectReady=o}),this.worker=new Worker(r.script??EA,{name:"devctl-logs"}),this.worker.addEventListener("message",(n)=>{this.onMessage(n.data)}),this.worker.addEventListener("error",(n)=>{this.markDead(Error(n.message||"log worker failed"))}),this.post({type:"init",config:e})}waitUntilReady(e=Fy){if(this.dead)return Promise.reject(Error("log worker is not running"));if(this.readySettled)return Promise.resolve();return new Promise((t,r)=>{let n=setTimeout(()=>{this.markDead(Error("log worker init timed out"))},e);this.ready.then(()=>{clearTimeout(n),t()},(o)=>{clearTimeout(n),r(o)})})}append(e){if(this.dead)return;try{this.post({type:"append",event:e})}catch{}}async query(e){let t=await this.rpc({type:"query",filter:e});if(!Array.isArray(t))throw Error("log worker query returned a non-array");return t}async queryPage(e,t){let r=await this.rpc({type:"queryPage",filter:e,page:t});if(!TA(r))throw Error("log worker queryPage returned an unexpected payload");return r}async queryFacets(e){let t=await this.rpc({type:"queryFacets",filter:e});if(!AA(t))throw Error("log worker queryFacets returned an unexpected payload");return t}snapshot(){return this.stats}async exportTo(e,t){await this.rpc({type:"exportTo",path:e,filter:t})}setParsers(e,t){if(this.dead)return;this.post({type:"setPluginPaths",paths:[...t??[]]})}setSecrets(e,t){if(this.dead)return;this.post({type:"setSecrets",extraMarkers:e,extraPatterns:t})}async close(){if(this.dead)return;try{await this.rpc({type:"close"},RA)}catch{}finally{this.markDead(Error("log worker closed"))}}rpc(e,t=_A){this.assertAlive();let r=this.nextId;this.nextId+=1;let n={...e,id:r};return new Promise((o,i)=>{let s=setTimeout(()=>{this.markDead(Error(`log worker timed out (${e.type})`))},t);this.pending.set(r,{resolve:o,reject:i,timer:s});try{this.post(n)}catch(a){clearTimeout(s),this.pending.delete(r),i(a instanceof Error?a:Error(String(a)))}})}post(e){this.assertAlive(),this.worker.postMessage(e)}onMessage(e){if(e.type==="ready"){this.settleReady();return}if(e.type==="appended"){this.stats=e.stats,this.bus?.publish(Se(Io,e.event.service,{event:e.event,level:e.event.level}));return}let t=this.pending.get(e.id);if(!t)return;if(this.pending.delete(e.id),clearTimeout(t.timer),e.type==="error"){t.reject(Error(e.error));return}t.resolve(e.result)}settleReady(){if(this.readySettled||this.dead)return;this.readySettled=!0,this.resolveReady()}assertAlive(){if(this.dead)throw Error("log worker is not running")}markDead(e){if(this.dead)return;if(this.dead=!0,!this.readySettled)this.readySettled=!0,this.rejectReady(e);this.rejectAll(e);try{this.worker.terminate()}catch{}}rejectAll(e){for(let t of this.pending.values())clearTimeout(t.timer),t.reject(e);this.pending.clear()}}function TA(e){if(e===null||Array.isArray(e)||!("events"in e))return!1;return Array.isArray(e.events)}function AA(e){return e!==null&&!Array.isArray(e)&&"byService"in e}function $y(e,t,r){return nh(new Rd(e.max,t,r,e.persist,e.directory,e.sessionID,e.retentionDays,e.maxSessionLogs))}async function Hy(e,t,r,n={}){if(n.standalone??Bun.isStandaloneExecutable===!0)return{logs:$y(e,t,r),usingWorker:!1};try{let i=new Ny(e,t,{script:n.script});return await i.waitUntilReady(n.initTimeoutMs??Fy),{logs:i,usingWorker:!0}}catch{return{logs:$y(e,t,r),usingWorker:!1}}}ni();import{createServer as tL}from"http";te();fs();var rL="2025-03-26",cb="2.0",ob=-32700,ib=-32600,nL=-32601,sb=-32602,oL=-32603;function iL(e){return e!==""&&Cn(e)}class lp{server;running=!1;addr="";boundPort=0;opts;constructor(e){this.opts=e}disabled(){return this.opts.disabledTools?.()??[]}address(){return this.addr}listenPort(){return this.boundPort}isRunning(){return this.running}emit(e,t){this.opts.onEvent?.(e,t)}start(){if(!iL(this.opts.host))return Promise.reject(E(lt,`refusing to bind MCP to ${this.opts.host}`));return new Promise((e,t)=>{this.server=tL((r,n)=>{this.serve(r,n)}),this.server.on("error",(r)=>t(Pe(lt,`unable to listen on ${this.opts.host}:${this.opts.port}`,r))),this.server.listen(this.opts.port,this.opts.host,()=>{let r=this.server?.address();this.boundPort=typeof r==="object"&&r?r.port:this.opts.port,this.addr=`${this.opts.host}:${this.boundPort}`,this.running=!0,this.emit("INFO",`listening on ${this.addr}`),e()})})}stop(){let e=this.server,t=this.running;if(!e)return this.running=!1,Promise.resolve();return new Promise((r)=>{e.close(()=>{if(this.running=!1,this.addr="",this.boundPort=0,this.server=void 0,t)this.emit("INFO","stopped");r()})})}async serve(e,t){if(e.method==="OPTIONS"){t.writeHead(204,gs()),t.end();return}if(!this.authorized(e)){this.emit("WARN",`rejected unauthorized request from ${ab(e)}`),ii(t,401,{error:"unauthorized"});return}if(aL(e)!=="/mcp"){ii(t,404,{error:"not found"});return}if(e.method==="GET"||e.method==="DELETE"){t.writeHead(405,{Allow:"POST, OPTIONS",...gs()}),t.end();return}if(e.method!=="POST"){t.writeHead(405,{Allow:"POST, OPTIONS",...gs()}),t.end();return}let n;try{n=await cL(e)}catch(a){ii(t,400,ms(null,ob,a instanceof Error?a.message:"invalid body"));return}let o;try{o=JSON.parse(n)}catch{ii(t,400,ms(null,ob,"parse error"));return}let i=ab(e);if(Array.isArray(o)){let a=[];for(let c of o){let l=await this.dispatch(c,i);if(l!==void 0)a.push(l)}ii(t,200,a);return}let s=await this.dispatch(o,i);if(s===void 0){t.writeHead(202,gs()),t.end();return}ii(t,200,s)}authorized(e){if(this.opts.token==="")return!0;return(e.headers.authorization??"")===`Bearer ${this.opts.token}`}async dispatch(e,t){if(!Ec(e))return ms(null,ib,"invalid request");let r=e,n=r.id??null,o=r.id===void 0;if(r.method===void 0||r.method==="")return o?void 0:ms(n,ib,"missing method");try{let i=await this.handleMethod(r.method,Ec(r.params)?r.params:{},t);if(o)return;return{jsonrpc:cb,id:n,result:i}}catch(i){if(o)return;let s=i instanceof hs?i.code:oL,a=i instanceof Error?i.message:"internal error";return this.emit("WARN",`rpc error method=${r.method} addr=${t}: ${a}`),ms(n,s,a)}}async handleMethod(e,t,r){switch(e){case"initialize":{let n=sL(t);return this.emit("INFO",`client connected addr=${r}${n?` (${n})`:""}`),{protocolVersion:rL,capabilities:{tools:{},resources:{}},serverInfo:{name:"devctl",version:jt}}}case"notifications/initialized":case"ping":return{};case"tools/list":return{tools:Jy(this.disabled()).map((n)=>({name:n.name,description:n.description,inputSchema:n.inputSchema}))};case"tools/call":return this.callTool(t);case"resources/list":return{resources:ap.map((n)=>({uri:n,name:n.replace("devctl://",""),mimeType:"application/json"}))};case"resources/read":return this.readResource(t);default:throw new hs(nL,`method not found: ${e}`)}}async callTool(e){let t=typeof e.name==="string"?e.name:"";if(t==="")throw new hs(sb,"tool name is required");if(Rc(t)&&!An(t,this.disabled())){let o=`tool ${t} is disabled in devctl's MCP settings`;return this.emit("WARN",`tool call rejected name=${t}: disabled`),{content:[{type:"text",text:o}],isError:!0}}let r=Ec(e.arguments)?e.arguments:{},n=Date.now();try{let o=await tb(this.opts.hostApi,t,r);return this.emit("INFO",`tool call name=${t} duration=${Date.now()-n}ms`),{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){let i=o instanceof Error?o.message:"tool failed";return this.emit("WARN",`tool call failed name=${t} duration=${Date.now()-n}ms: ${i}`),{content:[{type:"text",text:i}],isError:!0}}}async readResource(e){let t=typeof e.uri==="string"?e.uri:"";if(!rb(t))throw new hs(sb,`unknown resource ${t}`);let r=await nb(this.opts.hostApi,t);return this.emit("INFO",`resource read uri=${t}`),{contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}}}function sL(e){let t=e.clientInfo;if(!Ec(t))return"";let r=typeof t.name==="string"?t.name:"",n=typeof t.version==="string"?t.version:"";if(r==="")return"";return n===""?r:`${r} ${n}`}function ab(e){return e.socket.remoteAddress??"unknown"}class hs extends Error{code;constructor(e,t){super(t);this.code=e}}function gs(){return{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Headers":"Authorization, Content-Type, Accept, MCP-Protocol-Version, Mcp-Session-Id","Access-Control-Allow-Methods":"POST, OPTIONS"}}function ii(e,t,r){let n=JSON.stringify(r);e.writeHead(t,{"Content-Type":"application/json","Content-Length":Buffer.byteLength(n),...gs()}),e.end(n)}function ms(e,t,r){return{jsonrpc:cb,id:e,error:{code:t,message:r}}}function aL(e){let t=e.url??"/",r=t.indexOf("?");return r>=0?t.slice(0,r):t}function cL(e){return new Promise((t,r)=>{let n=[];e.on("data",(o)=>{n.push(o)}),e.on("end",()=>{t(Buffer.concat(n).toString("utf8"))}),e.on("error",r)})}function Ec(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}fs();var uL=(e)=>new lp({host:"127.0.0.1",...e});async function pL(e,t={}){let r=t.clock??Qa,n=t.fs??sy,o=t.processes??new Eu,i=t.bus??new ud(2048),s=t.tokens??new ao(e.auth.refresh_threshold_seconds*1000,_n(),i,void 0,r),a=new Iu(o,r),c=rd(),l=new mr(e.secrets.extra_markers,e.secrets.extra_patterns),d=Bun.isStandaloneExecutable===!0,{logs:u,usingWorker:f}=await Hy({max:e.logs.max_memory_events,persist:e.logs.persistence.enabled,directory:e.logs.persistence.directory,sessionID:c,retentionDays:e.logs.persistence.retention_days,maxSessionLogs:e.logs.persistence.max_session_logs,extraMarkers:e.secrets.extra_markers,extraPatterns:e.secrets.extra_patterns},i,l,{standalone:d});if(!f&&!d)u.append({timestamp:r.isoNow(),service:"devctl",source:"devctl",level:"WARN",message:"log worker failed to start; using in-process log store",pid:0});return{supervisor:new ip(e,{healthCheckers:t.healthCheckers??Nv([]),detectGoogle:t.detectGoogle??io,tokens:s,inspectProcess:oy,processAlive:Qt,acquireLock:Em,socketExists:lL,unlinkSocket:dL,procs:o,orchestrator:a,clock:r,fs:n,bus:i,logs:u,detector:l,sessionID:c,createMcpListener:t.createMcpListener??uL,isKnownTool:Rc,createCommands:(m)=>fv(m,rc(Yo({tokens:s})),a)}),orchestrator:a,clock:r,fs:n}}async function lb(e,t){let r=to(e,t),{supervisor:n}=await pL(r),o=!1,i=()=>{if(o)return;o=!0,n.shutdown(Ta(r.shutdown)).finally(()=>process.exit(0))};process.on("SIGINT",i),process.on("SIGTERM",i),await n.run()}ht();te();Bf();import{Command as GO}from"commander";var Jk=["zsh","bash","fish"];function Xk(e){if(e==="zsh")return`#compdef devctl
|
|
2278
|
+
`}var hA=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(vA(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 gA(this.deps.tokens.refresh(`sa:${e}`,"",[]),hA),t="available"}catch{t="unavailable"}return this.accounts.set(e,t),t}}function gA(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 vA(e){return e.capabilities.some((t)=>["google_api","iap","service_identity"].includes(t))}te();te();Zu();var SA=600,kA=65535;async function Iy(e,t){let r=Number.isInteger(t)&&(t??0)>0?t:fo(e);for(let n=0;n<SA;n+=1){let o=r+n;if(o>kA)break;if(await jo(o))return o}throw _(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 CA=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(),CA)}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 PA}from"net";var _A=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=PA((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=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 _(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 _(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 _(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 _(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 _(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,RA=1e4,EA=2000,TA=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??TA,{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(!AA(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(!LA(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"},EA)}catch{}finally{this.markDead(Error("log worker closed"))}}rpc(e,t=RA){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 AA(e){if(e===null||Array.isArray(e)||!("events"in e))return!1;return Array.isArray(e.events)}function LA(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 rL}from"http";te();fs();var nL="2025-03-26",cb="2.0",ob=-32700,ib=-32600,oL=-32601,sb=-32602,iL=-32603;function sL(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(!sL(this.opts.host))return Promise.reject(_(lt,`refusing to bind MCP to ${this.opts.host}`));return new Promise((e,t)=>{this.server=rL((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(cL(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 lL(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:iL,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=aL(t);return this.emit("INFO",`client connected addr=${r}${n?` (${n})`:""}`),{protocolVersion:nL,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(oL,`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 aL(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 cL(e){let t=e.url??"/",r=t.indexOf("?");return r>=0?t.slice(0,r):t}function lL(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 pL=(e)=>new lp({host:"127.0.0.1",...e});async function fL(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:dL,unlinkSocket:uL,procs:o,orchestrator:a,clock:r,fs:n,bus:i,logs:u,detector:l,sessionID:c,createMcpListener:t.createMcpListener??pL,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 fL(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 KO}from"commander";var Jk=["zsh","bash","fish"];function Xk(e){if(e==="zsh")return`#compdef devctl
|
|
2281
2281
|
_devctl() {
|
|
2282
2282
|
local -a opts
|
|
2283
2283
|
local line="\${words[*]}"
|
|
@@ -2306,7 +2306,7 @@ complete -c devctl -f -a "(__devctl_complete)"
|
|
|
2306
2306
|
`)}),r.command("login").action(async()=>{await t.loginGoogle()}),r.command("logout").action(async()=>{await t.logoutGoogle()}),r.command("refresh").option("--json","machine-readable output").action(async(n)=>{let o=await t.openController("",xe(e),!1);try{let i=await t.refreshUserToken("user"),s=o.client?await o.refreshAuth():void 0;if(n.json){T(JSON.stringify({identity:i.identity,expires_at:i.expiresAt.toISOString(),service_account_status:s?.service_account_status??{}},null,2)+`
|
|
2307
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();hf();import{mkdirSync as
|
|
2309
|
+
`)}finally{await o.close()}})}te();At();hf();import{mkdirSync as FO}from"fs";import{basename as NO,dirname as HO}from"path";import{stringify as jO}from"yaml";Ma();function Zk(e,t){e.command("reload").description("reload configuration").action(async()=>{let r=await t.openController("",xe(e),!0);try{let n=await r.reload();if(n.restart_required.length===0)T(`configuration reloaded
|
|
2310
2310
|
`);else T(`configuration reloaded; restart required: ${n.restart_required.join(", ")}
|
|
2311
2311
|
`);if(n.supervisor_restart_required&&n.supervisor_restart_required.length>0)T(`note: ${sc(n.supervisor_restart_required)}
|
|
2312
2312
|
`)}finally{await r.close()}})}function eC(e,t){let r=e.command("config");r.command("validate").option("--json").action((n)=>{try{let o=t.load("",xe(e)),i=t.validate(o);if(n.json){T(JSON.stringify({valid:i.length===0,issues:i},null,2)+`
|
|
@@ -2318,12 +2318,12 @@ complete -c devctl -f -a "(__devctl_complete)"
|
|
|
2318
2318
|
`),T(` winner: ${s.layer} (${s.source})
|
|
2319
2319
|
`);for(let a of s.shadowed)T(` shadowed: ${a.layer} (${a.source})
|
|
2320
2320
|
`)}}),r.command("show").option("--json").action((n)=>{let o=t.load("",xe(e));if(n.json){T(JSON.stringify(o,null,2)+`
|
|
2321
|
-
`);return}T(
|
|
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}=jd("",xe(e)),c=wl(i,NO(s)),l=t.validateConfigText(s,a,c.yaml);if(T(Sl(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
|
-
`)})}ni();At();import{setTimeout as
|
|
2325
|
+
`);return}if(t.fileExists(a))throw Error(`configuration already exists at ${a}; not overwriting`);FO(HO(a),{recursive:!0}),t.writeTextFile(a,c.yaml),T(`wrote ${a}
|
|
2326
|
+
`)})}ni();At();import{setTimeout as BO}from"timers/promises";function rC(e,t){e.command("exec").argument("<service>","service whose execution context to use").argument("[command...]","command and arguments").option("--print-env","print the resolved environment instead of running a command").option("--reveal","show secret values with --print-env").option("--json","machine-readable output").action(async(r,n,o)=>{if(!o.printEnv&&n.length===0)throw Error("exec command is required (or use --print-env)");let i=await t.openController("",xe(e),!0);try{let s=await i.execService(r,n,o.printEnv===!0);if(s.environment){let a=o.reveal?s.environment:new mr(i.cfg.secrets.extra_markers,i.cfg.secrets.extra_patterns).redactMap(s.environment);if(o.json)T(JSON.stringify({...s,environment:a},null,2)+`
|
|
2327
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
2329
|
`);else{if(s.stdout)T(s.stdout);if(s.stderr)process.stderr.write(s.stderr)}}finally{await i.close()}})}function nC(e,t){e.command("run").argument("<task>","task to run").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",xe(e),!0);try{let i=await o.runTask(r);if(n.json)T(JSON.stringify(i,null,2)+`
|
|
@@ -2345,12 +2345,12 @@ 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
|
|
2348
|
+
`)}finally{o?.close()}}var UO=2000;function aC(e,t){e.command("status").option("--repo <path>","target a repository directly, even without a loadable configuration").option("--json","machine-readable output").option("--watch","keep refreshing until interrupted").action(async(r)=>{if(!r.watch){await tC(t,e,r);return}let n=new AbortController,o=()=>n.abort();process.on("SIGINT",o),process.on("SIGTERM",o);try{while(!n.signal.aborted){T(`--- ${new Date().toISOString()} ---
|
|
2349
2349
|
`),await tC(t,e,r),T(`
|
|
2350
|
-
`);try{await
|
|
2351
|
-
`);return}let i=await
|
|
2350
|
+
`);try{await BO(UO,void 0,{signal:n.signal})}catch{break}}}finally{process.off("SIGINT",o),process.off("SIGTERM",o)}})}function cC(e,t){e.command("down").description("stop the daemon (and, by default, its services)").option("--repo <path>","target a repository directly, even without a loadable configuration").option("--keep-services","stop only the daemon; its services keep running, detached").action(async(r)=>{let{repoRoot:n,client:o}=await t.findDaemon("",r.repo??"",xe(e));if(!o){T(`no supervisor is running for ${n}
|
|
2351
|
+
`);return}let i=await GO(o);try{let s=r.keepServices!==!0;await o.call("shutdown",{stop_services:s},i)}finally{o.close()}await WO(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
|
|
2353
|
+
`)})}async function WO(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 GO(e){try{let r=await e.call("config_snapshot",null),n=typeof r.shutdown?.grace_seconds==="number"?r.shutdown.grace_seconds:0;return Math.max(5000,n*1000+2000)}catch{return 30000}}function lC(e,t){e.command("attach").action(async()=>{let r=await t.openAttach("",xe(e)),{runTuiWithController:n}=await Promise.resolve().then(() => (Bf(),Yk));await n(t,r)})}import{setTimeout as dC}from"timers/promises";function Wf(e){return`${e.timestamp.slice(11,19)} ${e.service.padEnd(10)} ${String(e.level).padEnd(6)} ${e.message}
|
|
2354
2354
|
`}async function uC(e,t,r,n=1000){let o=await e(void 0);o.events.forEach(t);let i=o.nextCursor;while(!r.aborted){try{await dC(n,void 0,{signal:r})}catch{return}o=await e(i),o.events.forEach(t),i=o.nextCursor}}function pC(e,t){let r=e.command("logs").argument("[services...]").enablePositionalOptions();r.option("--level <level>","minimum level").option("--search <text>","substring or regex search").option("--regex","treat search as regular expression").option("--source <source>","filter by source").option("--since <timestamp>","only events at or after this ISO timestamp").option("--until <timestamp>","only events at or before this ISO timestamp").option("--output <path>","export path").option("--json","machine-readable output").option("-f, --follow","keep printing new matching events until interrupted").option("--all","print the full matching history instead of the latest page").action(async(n,o)=>{let i=await t.openController("",xe(e),!0);try{let s=o.output?t.resolveExportPath(o.output):void 0;if(o.follow&&!s){let c=new AbortController,l=()=>c.abort();process.on("SIGINT",l),process.on("SIGTERM",l);try{await uC((d)=>i.logsPage({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,since:o.since,until:o.until,cursor:d,direction:"forward"}),(d)=>T(o.json?JSON.stringify(d)+`
|
|
2355
2355
|
`:Wf(d)),c.signal)}finally{process.off("SIGINT",l),process.off("SIGTERM",l)}return}if(s||o.all===!0){let c=await i.logs({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,since:o.since,until:o.until,export:s});if(s){T(`exported ${s}
|
|
2356
2356
|
`);return}if(o.json){T(JSON.stringify(c,null,2)+`
|
|
@@ -2377,11 +2377,11 @@ PROXY ${i.proxy.running?"RUNNING":"STOPPED"} ${i.proxy.address??""}
|
|
|
2377
2377
|
`);return}if(T(`install ${n.hint}
|
|
2378
2378
|
`),r.check||!n.command)return;if(T(`installing via ${n.kind}\u2026
|
|
2379
2379
|
`),(await t.applyUpdate(n.command,!0)).code!==0){process.exitCode=lh;return}T(`updated to ${n.latest}; ${mc}
|
|
2380
|
-
`)})}function
|
|
2381
|
-
`)}),r.action(async()=>{let n=r.opts();await Hf(e,n.config??"")}),oC(r,e),iC(r,e),sC(r,e),nC(r,e),rC(r,e),aC(r,e),cC(r,e),pC(r,e),fC(r,e),
|
|
2382
|
-
`);else T(t.formatDoctor(o));if(o.issues>0)process.exitCode=2})}function
|
|
2380
|
+
`)})}function qO(e,t){let r=new KO;return r.name("devctl").description("Local development orchestrator").version(er(),"-V, --version","print version").option("--config <path>","path to config file or .devctl directory").enablePositionalOptions(),r.command("version").description("print version").action(()=>{T(`${er()}
|
|
2381
|
+
`)}),r.action(async()=>{let n=r.opts();await Hf(e,n.config??"")}),oC(r,e),iC(r,e),sC(r,e),nC(r,e),rC(r,e),aC(r,e),cC(r,e),pC(r,e),fC(r,e),VO(r,e),zO(r,e),Qk(r,e),mC(r,e),hC(r,e),eC(r,e),Zk(r,e),lC(r,e),YO(r,e),gC(r,e),JO(r,t),r}function VO(e,t){e.command("doctor").option("--json","machine-readable output").action(async(r)=>{let n=t.load("",xe(e)),o=await t.runDoctor.execute(n);if(r.json)T(JSON.stringify(o,null,2)+`
|
|
2382
|
+
`);else T(t.formatDoctor(o));if(o.issues>0)process.exitCode=2})}function zO(e,t){e.command("setup").option("--force","overwrite an existing configuration").action(async(r)=>{await t.runSetup("",xe(e),r.force===!0)})}function YO(e,t){e.command("completion").argument("[shell]","zsh, bash, or fish").description("print a shell completion script").action((r)=>{T(Xk(r||"zsh"))}),e.command("__complete",{hidden:!0}).argument("[line...]").description("internal completion helper").action((r)=>{let n=r.join(" "),o=n===""?"devctl ":n;try{let i=t.load("",xe(e));T(Uf(o,i).join(`
|
|
2383
2383
|
`)+`
|
|
2384
2384
|
`)}catch{T(Uf(o,Yn()).join(`
|
|
2385
2385
|
`)+`
|
|
2386
|
-
`)}})}function
|
|
2386
|
+
`)}})}function JO(e,t){e.command("_supervisor").option("--repo <path>","repository root").action(async(r)=>{await t(r.repo??"",xe(e))})}async function vC(e,t){process.stdout.on("error",(r)=>{if(r.code==="EPIPE")process.exit(0)});try{await qO(e,t).parseAsync(process.argv)}catch(r){process.stderr.write(L(r)+`
|
|
2387
2387
|
`),process.exit(uh(r))}}Js();await vC(Fv(),lb);
|