@amr-m-abdelgawad/devctl 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/devctl.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
 
4
- const { closeSync, openSync, readSync } = require("node:fs");
5
- const { resolve } = require("node:path");
6
- const { spawn } = require("node:child_process");
4
+ const { closeSync, openSync, readSync } = require("fs");
5
+ const { resolve } = require("path");
6
+ const { spawn } = require("child_process");
7
7
 
8
8
  const SUPPORTED_TARGETS = new Set([
9
9
  "darwin-arm64",
@@ -13,12 +13,16 @@ const SUPPORTED_TARGETS = new Set([
13
13
  "win32-x64",
14
14
  ]);
15
15
 
16
- function targetFor(platform = process.platform, arch = process.arch) {
17
- return `${platform}-${arch}`;
16
+ function targetFor(platform, arch) {
17
+ const p = platform !== undefined ? platform : process.platform;
18
+ const a = arch !== undefined ? arch : process.arch;
19
+ return `${p}-${a}`;
18
20
  }
19
21
 
20
- function assertSupportedTarget(platform = process.platform, arch = process.arch) {
21
- const target = targetFor(platform, arch);
22
+ function assertSupportedTarget(platform, arch) {
23
+ const p = platform !== undefined ? platform : process.platform;
24
+ const a = arch !== undefined ? arch : process.arch;
25
+ const target = targetFor(p, a);
22
26
  if (!SUPPORTED_TARGETS.has(target)) {
23
27
  throw new Error(
24
28
  `devctl does not currently support ${target}. Supported targets: ${Array.from(SUPPORTED_TARGETS).join(", ")}.`,
@@ -27,13 +31,13 @@ function assertSupportedTarget(platform = process.platform, arch = process.arch)
27
31
  return target;
28
32
  }
29
33
 
30
- function resolveBunExecutable(resolveModule = require.resolve) {
34
+ function resolveBunExecutable(resolveModule) {
35
+ const res = resolveModule !== undefined ? resolveModule : require.resolve;
31
36
  try {
32
- return resolveModule("bun/bin/bun.exe", { paths: [__dirname] });
37
+ return res("bun/bin/bun.exe", { paths: [__dirname] });
33
38
  } catch (cause) {
34
39
  throw new Error(
35
40
  "The bundled Bun runtime is missing. Reinstall @amr-m-abdelgawad/devctl without --ignore-scripts and try again.",
36
- { cause },
37
41
  );
38
42
  }
39
43
  }
@@ -43,36 +47,52 @@ function readFilePrefix(filePath) {
43
47
  try {
44
48
  const prefix = Buffer.alloc(512);
45
49
  const bytesRead = readSync(descriptor, prefix, 0, prefix.length, 0);
46
- return prefix.subarray(0, bytesRead);
50
+ return prefix.slice(0, bytesRead);
47
51
  } finally {
48
52
  closeSync(descriptor);
49
53
  }
50
54
  }
51
55
 
52
- function assertInstalledBun(bunPath, readFile = readFilePrefix) {
53
- const prefix = readFile(bunPath).toString("utf8");
54
- if (prefix.includes("Bun's postinstall script was not run")) {
56
+ function assertInstalledBun(bunPath, readFile, platform) {
57
+ const read = readFile !== undefined ? readFile : readFilePrefix;
58
+ const targetPlatform = platform !== undefined ? platform : process.platform;
59
+ let prefix;
60
+ try {
61
+ prefix = read(bunPath);
62
+ } catch (_) {
63
+ return;
64
+ }
65
+ const text = prefix.toString("utf8");
66
+ if (text.includes("Bun's postinstall script was not run")) {
55
67
  throw new Error(
56
68
  "The bundled Bun runtime was not installed because npm lifecycle scripts were disabled. " +
57
69
  "Reinstall without --ignore-scripts, then run devctl again.",
58
70
  );
59
71
  }
72
+ if (targetPlatform !== "win32" && prefix.length >= 2 && prefix[0] === 0x4d && prefix[1] === 0x5a) {
73
+ throw new Error(
74
+ `devctl was installed for Windows (detected Windows binary at ${bunPath}), but is running inside ${targetPlatform}.\n` +
75
+ "When running inside WSL or Linux, install Node.js natively in WSL and run: npm install --global @amr-m-abdelgawad/devctl\n" +
76
+ "To run devctl in Windows, use PowerShell or Command Prompt instead.",
77
+ );
78
+ }
60
79
  }
61
80
 
62
- function launch(options = {}) {
63
- const platform = options.platform ?? process.platform;
64
- const arch = options.arch ?? process.arch;
65
- const argv = options.argv ?? process.argv.slice(2);
66
- const env = options.env ?? process.env;
67
- const cwd = options.cwd ?? process.cwd();
68
- const spawnChild = options.spawnChild ?? spawn;
69
- const resolveModule = options.resolveModule ?? require.resolve;
70
- const readFile = options.readFile ?? readFilePrefix;
71
- const entrypoint = options.entrypoint ?? resolve(__dirname, "../dist/devctl.js");
81
+ function launch(options) {
82
+ const opts = options !== undefined ? options : {};
83
+ const platform = opts.platform !== undefined ? opts.platform : process.platform;
84
+ const arch = opts.arch !== undefined ? opts.arch : process.arch;
85
+ const argv = opts.argv !== undefined ? opts.argv : process.argv.slice(2);
86
+ const env = opts.env !== undefined ? opts.env : process.env;
87
+ const cwd = opts.cwd !== undefined ? opts.cwd : process.cwd();
88
+ const spawnChild = opts.spawnChild !== undefined ? opts.spawnChild : spawn;
89
+ const resolveModule = opts.resolveModule !== undefined ? opts.resolveModule : require.resolve;
90
+ const readFile = opts.readFile !== undefined ? opts.readFile : readFilePrefix;
91
+ const entrypoint = opts.entrypoint !== undefined ? opts.entrypoint : resolve(__dirname, "../dist/devctl.js");
72
92
 
73
93
  assertSupportedTarget(platform, arch);
74
94
  const bunPath = resolveBunExecutable(resolveModule);
75
- assertInstalledBun(bunPath, readFile);
95
+ assertInstalledBun(bunPath, readFile, platform);
76
96
 
77
97
  return spawnChild(bunPath, [entrypoint, ...argv], {
78
98
  cwd,
package/dist/devctl.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
  var Zw=Object.defineProperty;var eS=(e)=>e;function tS(e,t){this[e]=eS.bind(null,t)}var Ls=(e,t)=>{for(var n in t)Zw(e,n,{get:t[n],enumerable:!0,configurable:!0,set:tS.bind(t,n)})};var D=(e,t)=>()=>(e&&(t=e(e=0)),t);var nS=import.meta.require;var eo=D(()=>{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"});function Ef(e,t=""){if(t===Rf)return!0;return e.includes(Rf)||e.startsWith(rS)}function Ds(){if(Pf)return;Pf=!0;let e=process.emitWarning.bind(process);process.emitWarning=(t,...n)=>{let r=iS(n[0]),o=oS(t);if(Ef(o,r))return;e(t,...n)}}function Tf(){let e=process.stderr.write.bind(process.stderr);return process.stderr.write=(t,n,r)=>{let o=sS(t);if(Ef(o)){let i=typeof n==="function"?n:typeof r==="function"?r:void 0;if(typeof i==="function")i();return!0}if(typeof n==="function")return e(t,n);return e(t,n,r)},()=>{process.stderr.write=e}}function oS(e){if(typeof e==="string")return e;if(e instanceof Error)return e.message;return String(e)}function iS(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&"type"in e&&typeof e.type==="string")return e.type;return""}function sS(e){if(typeof e==="string")return e;if(e instanceof Uint8Array)return new TextDecoder().decode(e);return""}var Rf="MetadataLookupWarning",rS="received unexpected error =",Pf=!1;var vl=D(()=>{eo()});function L(e,t){return new Oe(e,t)}function fe(e,t,n){let r=n instanceof Error?n:Error(String(n));return new Oe(e,t,{cause:r})}function ke(e,t,n,r=""){return new Oe(e,t,{hint:n,service:r})}function Ms(e,t){return e instanceof Oe&&e.kind===t}function Df(e){if(e instanceof Oe)return e.exitCode();return 1}function Of(e){if(e instanceof Oe)return{error:F(e),kind:e.kind,hint:e.hint,service:e.service};return{error:F(e)}}function Mf(e){if(typeof e==="string")return L("general",e);if(e.kind)return new Oe(e.kind,e.error.replace(`${e.kind}: `,""),{hint:e.hint,service:e.service});return L("general",e.error)}function F(e){if(e instanceof Oe){if(e.hint!=="")return`${Af(e)} \u2014 ${e.hint}`;return Af(e)}if(e instanceof Error)return e.message;return String(e)}function Af(e){let t=`${e.kind}: `;if(e.message.startsWith(t)){let n=e.message.slice(t.length),r=e.causeError?`: ${e.causeError.message}`:"";if(r!==""&&n.endsWith(r))return n.slice(0,n.length-r.length);return n}return e.message}var ee="configuration",ar="configuration_missing",pi="service_not_found",Lf="dependency",Ke="process_start",to="authentication",cr="authorization",hn="token",Os="impersonation",yl="iap",$n="proxy",xl="health_check",Je="general",Oe;var Pe=D(()=>{Oe=class Oe extends Error{kind;hint;service;causeError;constructor(e,t,n){super(n?.cause?`${e}: ${t}: ${n.cause.message}`:`${e}: ${t}`);this.name="DevctlError",this.kind=e,this.hint=n?.hint??"",this.service=n?.service??"",this.causeError=n?.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}}}});import{spawnSync as aS}from"child_process";import{createHash as cS,randomBytes as Sl}from"crypto";import{copyFileSync as lS,existsSync as In,mkdirSync as dS,readdirSync as uS,readFileSync as kl,renameSync as Cl,statSync as pS,unlinkSync as bl,writeFileSync as fS}from"fs";import{homedir as gS}from"os";import{basename as mS,dirname as wl,join as dt,resolve as hS}from"path";function Ft(){let e=process.env.DEVCTL_HOME;if(e&&e!=="")return e;return dt(gS(),".devctl")}function Yt(e){dS(e,{recursive:!0,mode:vS})}function vn(e){let t=hS(e),n=process.platform==="win32"?t.toLowerCase():t;return cS("sha256").update(n).digest("hex").slice(0,xS)}function no(e){let t=dt(Ft(),"state",vn(e)),n=dt(Ft(),"sessions",vn(e));if(!In(t)&&In(n)){Yt(wl(t));try{Cl(n,t)}catch{Yt(t);for(let r of["state.json","devctl.lock","devctl.sock"]){let o=dt(n,r);if(In(o))lS(o,dt(t,r))}}}return Yt(t),t}function $s(){let e=dt(Ft(),"logs");return Yt(e),e}function lr(){let e=dt(Ft(),"exports");return Yt(e),e}function Wt(){let e=dt(Ft(),"credentials");return Yt(e),e}function Is(e,t=process.platform){if(t==="win32")return`\\\\.\\pipe\\devctl-${vn(e)}`;return dt(no(e),"devctl.sock")}function bS(e){return dt(no(e),"devctl.lock")}function $f(e){return dt(no(e),"state.json")}function dr(e){return dt(no(e),"bootstrap.log")}function Ff(e){let t=dr(e);if(!In(t))return;let n=no(e),r=dt(n,`${If}${_l()}.log`);try{Cl(t,r)}catch{return}SS(n)}function SS(e){let t;try{t=uS(e).filter((r)=>r.startsWith(If)&&r.endsWith(".log"))}catch{return}let n=t.map((r)=>{let o=dt(e,r);try{return{path:o,mtime:pS(o).mtimeMs}}catch{return}}).filter((r)=>r!==void 0).sort((r,o)=>o.mtime-r.mtime);for(let r=wS;r<n.length;r++)try{bl(n[r].path)}catch{}}function Gt(e,t){Yt(wl(e));let n=dt(wl(e),`.${mS(e)}.tmp-${process.pid}-${Sl(4).toString("hex")}`);fS(n,t,{mode:yS}),Cl(n,e)}function _l(e=new Date){return`${e.toISOString().slice(0,19).replace(/:/g,"-")+"Z"}-${Sl(3).toString("hex")}`}function Fs(e){let t=e.indexOf("Z-"),n=t>=0?e.slice(0,t+1):e,r=n.indexOf("T");if(r<0||!n.endsWith("Z"))return;let o=n.slice(0,r),i=n.slice(r+1,-1).replace(/-/g,":"),s=new Date(`${o}T${i}Z`);return Number.isNaN(s.getTime())?void 0:s}function ur(e){return Rl($f(e))}function Rl(e){if(!In(e))return;try{let t=JSON.parse(kl(e,"utf8"));if(Array.isArray(t.processes)&&typeof t.repo_root==="string")return t;return}catch{return}}function Nf(e,t){Gt($f(e),`${JSON.stringify(t,null,2)}
4
4
  `)}function Hf(e,t){let n=bS(e);if(In(n)){let r=kS(n);if(r&&Pt(r.pid))throw Error(`supervisor already running (pid ${r.pid})`);try{bl(n)}catch{}}return Gt(n,JSON.stringify({pid:process.pid,socket:t})),{release:()=>{try{bl(n)}catch{}}}}function kS(e){if(!In(e))return;try{let t=JSON.parse(kl(e,"utf8"));if(typeof t.pid==="number"&&typeof t.socket==="string")return{pid:t.pid,socket:t.socket}}catch{return}return}function Pt(e){if(e<=0)return!1;if(process.platform==="win32")return CS(e);try{return process.kill(e,0),!0}catch{return!1}}function Pl(e){try{let t=aS("cmd.exe",["/d","/c",`tasklist /FO CSV /NH /FI "PID eq ${e}"`],{encoding:"buffer",windowsHide:!0,timeout:5000});return _S(t.stdout)}catch{return""}}function CS(e){try{return process.kill(e,0),!0}catch{}let t=Pl(e).toLowerCase();if(t===""||t.includes("no tasks")||t.includes("no matching"))return!1;return t.includes(String(e))}function _S(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 El(){return Sl(24).toString("hex")}function RS(e){return dt(no(e),"mcp-token")}function jf(e){let t=RS(e);if(In(t)){let r=kl(t,"utf8").trim();if(r!=="")return r}let n=El();return Gt(t,n),n}var vS=448,yS=384,xS=16,wS=5,If="bootstrap-";var ze=()=>{};function ro(){return{args:[],shell:!1}}function Al(){return{vars:{},required:[],defaults:{}}}function Ll(){return{type:"",url:"",address:"",command:ro(),interval_seconds:0,timeout_seconds:0,start_period_seconds:0,unhealthy_threshold:3,healthy_reset_threshold:10}}function wt(e){return typeof e==="string"?e:e.service}function Fn(e){return typeof e==="string"?"service_started":e.condition||"service_started"}function Hs(e){return Fn(e)==="service_healthy"?`${wt(e)} (healthy)`:wt(e)}function Dl(){return{type:"",mode:"",service_account:"",config:{}}}function Nn(){return{extends:"",description:"",command:ro(),shell:!1,working_dir:"",dependencies:[],ports:[],environment:Al(),health:Ll(),identity:Dl(),logs:{stdout:!1,stderr:!1},restart:{policy:"",max_retries:0,backoff_seconds:0},startup:{wait_for_healthy:!1,timeout_seconds:0},capabilities:[],proxy:[],container:void 0,hooks:{pre_start:ro(),post_start:ro()}}}function pr(){return{version:1,project:{name:""},google:{project_id:"",region:""},profiles:{},templates:{},services:{},tasks:{},proxy:{enabled:!1,listen:{host:"127.0.0.1",port:0},token_endpoint:{enabled:!1,host:"",port:0},routes:[]},logs:{max_memory_events:50000,persistence:{enabled:!0,directory:"~/.devctl/logs",retention_days:14,max_session_logs:0}},auth:{refresh_threshold_seconds:300},shutdown:{grace_seconds:10},ui:{theme:"system",keymap:{}},secrets:{extra_markers:[],extra_patterns:[]},doctor:{tools:[]},plugins:[],environment:{sources:[],secrets:{}},provenance:{},repoRoot:"",configPath:""}}function io(e){return e.args.length===0||e.args.length===1&&(e.args[0]??"").trim()===""}function fi(e){if(e.type!=="")return e.type;return e.mode}function yn(e){let t=fi(e).toLowerCase();return t==="service"||t==="service_account"}function Uf(e){let t=fi(e).toLowerCase();return t==="user"||t===""}function Kf(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 fr(e){let t=e.host===""?"127.0.0.1":e.host;if(e.port===0)return t;return`${t}:${e.port}`}function js(e){if(e.refresh_threshold_seconds<=0)return 300;return e.refresh_threshold_seconds}function Bs(e){if(e.stop_services_on_exit===void 0)return!0;return e.stop_services_on_exit}function gi(e){if(e.grace_seconds<=0)return 10;return e.grace_seconds}function qf(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stdout}function Vf(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stderr}function Ol(e,t){return e.find((n)=>n.name===t)}function Jf(e){return e[0]}var oo=1,Bf="never",Wf="on_failure",Gf="always",Ns=8080,Tl="127.0.0.1";function I(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function $l(e){return I(e)?new Set(Object.keys(e)):new Set}function K(e){if(typeof e==="string")return e;if(typeof e==="number"||typeof e==="boolean")return String(e);return""}function xe(e){if(typeof e==="number")return e;if(typeof e==="string"){let t=Number(e);return Number.isNaN(t)?0:t}return 0}function it(e){return e===!0}function Tt(e){if(!Array.isArray(e))return[];return e.map((t)=>K(t))}function Il(e){if(!Array.isArray(e))return[];return e.map((t)=>I(t)?{service:K(t.service),condition:K(t.condition)||"service_started"}:K(t))}function Hn(e){if(!I(e))return{};let t={};for(let[n,r]of Object.entries(e))t[n]=K(r);return t}function Ut(e){if(typeof e==="string")return{args:e.split(/\s+/).filter((t)=>t!==""),shell:!1};if(Array.isArray(e))return{args:e.map((t)=>K(t)),shell:!1};return ro()}function Fl(e){if(e===void 0||e===null)return[];if(typeof e==="string"||typeof e==="number")return[Ml(e,"http")];if(Array.isArray(e))return e.map((t,n)=>Ml(t,`port_${n}`));if(I(e))return Object.entries(e).map(([t,n])=>Ml(n,t));return[]}function Ml(e,t){if(typeof e==="string"&&e.toLowerCase()==="auto")return{name:t,value:0,auto:!0};return{name:t,value:xe(e),auto:!1}}function zf(e){if(!I(e))return Al();let t={},n=[],r={};for(let[o,i]of Object.entries(e))if(o==="required")n=Tt(i);else if(o==="defaults")r=Hn(i);else t[o]=K(i);return{vars:t,required:n,defaults:r}}function PS(e){if(!I(e))return Dl();return{type:K(e.type),mode:K(e.mode),service_account:K(e.service_account),config:I(e.config)?{...e.config}:{}}}function ES(e){if(!I(e))return Ll();return{type:K(e.type),url:K(e.url),address:K(e.address),command:Ut(e.command),interval_seconds:xe(e.interval_seconds),timeout_seconds:xe(e.timeout_seconds),start_period_seconds:xe(e.start_period_seconds),unhealthy_threshold:e.unhealthy_threshold===void 0?3:xe(e.unhealthy_threshold),healthy_reset_threshold:e.healthy_reset_threshold===void 0?10:xe(e.healthy_reset_threshold)}}function TS(e){if(!I(e))return{policy:"",max_retries:0,backoff_seconds:0};return{enabled:e.enabled===void 0?void 0:it(e.enabled),policy:K(e.policy),max_retries:xe(e.max_retries),backoff_seconds:xe(e.backoff_seconds)}}function AS(e){if(!I(e))return{wait_for_healthy:!1,timeout_seconds:0};return{wait_for_healthy:it(e.wait_for_healthy),timeout_seconds:xe(e.timeout_seconds)}}function mi(e){if(!I(e))return Nn();let t=I(e.logs)?e.logs:{};return{extends:K(e.extends),description:K(e.description),command:Ut(e.command),shell:it(e.shell),working_dir:K(e.working_dir),dependencies:Il(e.dependencies),ports:Fl(e.ports),environment:zf(e.environment),health:ES(e.health),identity:PS(e.identity),logs:{stdout:it(t.stdout),stderr:it(t.stderr)},restart:TS(e.restart),startup:AS(e.startup),capabilities:Tt(e.capabilities),proxy:Hl(e.proxy),container:Nl(e.container),hooks:LS(e.hooks)}}function LS(e){let t=I(e)?e:{};return{pre_start:Ut(t.pre_start),post_start:Ut(t.post_start)}}function Yf(e){let t=I(e)?e:{};return{command:Ut(t.command),shell:it(t.shell),working_dir:K(t.working_dir),dependencies:Tt(t.dependencies),environment:zf(t.environment)}}function Nl(e){if(!I(e))return;let t={};if(I(e.ports))for(let[n,r]of Object.entries(e.ports))t[n]=xe(r);return{image:K(e.image),runtime:K(e.runtime),ports:t,env:Hn(e.env),volumes:Tt(e.volumes)}}function Hl(e){if(e===void 0||e===null)return[];if(Array.isArray(e))return e.map((t)=>gr(t));if(I(e))return[gr(e)];return[]}function Ws(e){if(!I(e))return{services:[],environment:{}};return{services:Tt(e.services),environment:Hn(e.environment)}}function DS(e){if(typeof e==="string")return{type:e,service_account:""};if(!I(e))return{type:"",service_account:""};return{type:K(e.type),service_account:K(e.service_account)}}function OS(e){if(!I(e))return{type:"",identity:{type:"",service_account:""},audience:"",service_account:""};return{type:K(e.type),identity:DS(e.identity),audience:K(e.audience),service_account:K(e.service_account)}}function gr(e){if(!I(e))return{name:"",match:{host:"",path:""},upstream:{url:""},auth:{type:"",identity:{type:"",service_account:""},audience:"",service_account:""}};let t=I(e.match)?e.match:{},n=I(e.upstream)?e.upstream:{};return{name:K(e.name),match:{host:K(t.host),path:K(t.path)},upstream:{url:K(n.url)},auth:OS(e.auth)}}var jl=()=>{};import{existsSync as MS,statSync as Qf}from"fs";import{basename as Xf,dirname as Gs,isAbsolute as $S,join as Us,resolve as Zf}from"path";function jn(e,t){if(t!=="")return IS(t);let n=e===""?process.cwd():e;n=$S(n)?n:Zf(n);for(;;){let r=Us(n,St,Kt);if(hi(r))return{repoRoot:n,configPath:r};let o=Us(n,"devctl.yaml");if(hi(o))return{repoRoot:n,configPath:o};let i=Gs(n);if(i===n)break;n=i}throw ke(ar,"no devctl configuration found","run `devctl setup` or create a .devctl/config.yaml in the repository root")}function IS(e){let t=Zf(e),n;try{n=Qf(t)}catch(o){throw fe(ar,"config path not found",o)}if(n.isDirectory()){if(Xf(t)===St)return{repoRoot:Gs(t),configPath:Us(t,Kt)};return{repoRoot:t,configPath:Us(t,Kt)}}let r=Gs(t);if(Xf(r)===St)r=Gs(r);return{repoRoot:r,configPath:t}}function hi(e){return MS(e)&&!Qf(e).isDirectory()}var St=".devctl",Kt="config.yaml";var vi=D(()=>{Pe()});function Vs(){return{services:{},templates:{},provenance:{}}}function Bn(e,t,n,r,o=""){if(I(t)&&Object.keys(t).length>0){for(let[i,s]of Object.entries(t))Bn(e,s,n,r,o===""?i:`${o}.${i}`);return}if(o==="")return;(e[o]??=[]).push({source:n,layer:r})}function Ks(e,t,n){let r=$l(n);if(I(n))for(let i of FS){let s=n[i];if(I(s))for(let a of Object.keys(s))r.add(`${i}.${a}`)}let o=e[t];e[t]=o?new Set([...o,...r]):r}function eg(e,t,n=Vs(),r={source:"unknown",layer:"unknown"}){if(Bn(n.provenance,t,r.source,r.layer),t.version!==void 0)e.version=xe(t.version);if(I(t.project)&&t.project.name!==void 0)e.project.name=K(t.project.name);if(I(t.google)){if(t.google.project_id!==void 0)e.google.project_id=K(t.google.project_id);if(t.google.region!==void 0)e.google.region=K(t.google.region)}if(I(t.profiles))for(let[o,i]of Object.entries(t.profiles)){let s=e.profiles[o];e.profiles[o]=s?HS(s,i):Ws(i)}if(I(t.templates))for(let[o,i]of Object.entries(t.templates)){let s=e.templates[o];e.templates[o]=s?qs(s,i):mi(i),Ks(n.templates,o,i)}if(I(t.services))for(let[o,i]of Object.entries(t.services)){let s=e.services[o];e.services[o]=s?qs(s,i):mi(i),Ks(n.services,o,i)}if(I(t.tasks))for(let[o,i]of Object.entries(t.tasks))e.tasks[o]=e.tasks[o]?jS(e.tasks[o],i):Yf(i);if(I(t.proxy))NS(e.proxy,t.proxy);if(I(t.logs)){if(t.logs.max_memory_events!==void 0)e.logs.max_memory_events=xe(t.logs.max_memory_events);if(I(t.logs.persistence)){let o=t.logs.persistence;if(o.enabled!==void 0)e.logs.persistence.enabled=it(o.enabled);if(o.directory!==void 0)e.logs.persistence.directory=K(o.directory);if(o.retention_days!==void 0)e.logs.persistence.retention_days=xe(o.retention_days);if(o.max_session_logs!==void 0)e.logs.persistence.max_session_logs=xe(o.max_session_logs)}}if(I(t.auth)&&t.auth.refresh_threshold_seconds!==void 0)e.auth.refresh_threshold_seconds=xe(t.auth.refresh_threshold_seconds);if(I(t.shutdown)){if(t.shutdown.stop_services_on_exit!==void 0)e.shutdown.stop_services_on_exit=it(t.shutdown.stop_services_on_exit);if(t.shutdown.grace_seconds!==void 0)e.shutdown.grace_seconds=xe(t.shutdown.grace_seconds)}if(I(t.ui)){if(t.ui.theme!==void 0)e.ui.theme=K(t.ui.theme);if(t.ui.keymap!==void 0)e.ui.keymap=Hn(t.ui.keymap)}if(I(t.secrets)){if(t.secrets.extra_markers!==void 0)e.secrets.extra_markers=Tt(t.secrets.extra_markers);if(t.secrets.extra_patterns!==void 0)e.secrets.extra_patterns=Tt(t.secrets.extra_patterns)}if(I(t.doctor)&&Array.isArray(t.doctor.tools))e.doctor.tools=t.doctor.tools.filter(I).map((o)=>({name:K(o.name),command:K(o.command)}));if(Array.isArray(t.plugins))e.plugins=t.plugins.filter(I).map((o)=>({path:K(o.path)})).filter((o)=>o.path!=="");if(I(t.environment)){if(Array.isArray(t.environment.sources))e.environment.sources=Tt(t.environment.sources);if(I(t.environment.secrets))e.environment.secrets={...e.environment.secrets,...Hn(t.environment.secrets)}}}function NS(e,t){if(t.enabled!==void 0)e.enabled=it(t.enabled);if(I(t.listen)){if(t.listen.host!==void 0)e.listen.host=K(t.listen.host);if(t.listen.port!==void 0)e.listen.port=xe(t.listen.port)}if(I(t.token_endpoint)){let n=t.token_endpoint;if(n.enabled!==void 0)e.token_endpoint.enabled=it(n.enabled);if(n.host!==void 0)e.token_endpoint.host=K(n.host);if(n.port!==void 0)e.token_endpoint.port=xe(n.port)}if(Array.isArray(t.routes))e.routes=t.routes.map((n)=>gr(n))}function HS(e,t){if(!I(t))return e;return{services:t.services!==void 0?Tt(t.services):e.services,environment:t.environment!==void 0?{...e.environment,...Hn(t.environment)}:e.environment}}function jS(e,t){if(!I(t))return e;return{command:t.command!==void 0?Ut(t.command):e.command,shell:t.shell!==void 0?it(t.shell):e.shell,working_dir:t.working_dir!==void 0?K(t.working_dir):e.working_dir,dependencies:t.dependencies!==void 0?Tt(t.dependencies):e.dependencies,environment:tg(e.environment,t.environment)}}function qs(e,t){if(!I(t))return e;let n=$l(t),r={...e,environment:tg(e.environment,t.environment),health:WS(e.health,t.health),identity:GS(e.identity,t.identity),logs:US(e.logs,t.logs),restart:KS(e.restart,t.restart),startup:qS(e.startup,t.startup),container:VS(e.container,t.container),hooks:BS(e.hooks,t.hooks)};if(n.has("extends"))r.extends=K(t.extends);if(n.has("description"))r.description=K(t.description);if(n.has("command"))r.command=Ut(t.command);if(n.has("shell"))r.shell=it(t.shell);if(n.has("working_dir"))r.working_dir=K(t.working_dir);if(n.has("dependencies"))r.dependencies=Il(t.dependencies);if(n.has("ports"))r.ports=Fl(t.ports);if(n.has("capabilities"))r.capabilities=Tt(t.capabilities);if(n.has("proxy"))r.proxy=Hl(t.proxy);return r}function BS(e,t){if(!I(t))return e;return{pre_start:t.pre_start!==void 0?Ut(t.pre_start):e.pre_start,post_start:t.post_start!==void 0?Ut(t.post_start):e.post_start}}function tg(e,t){if(!I(t))return e;let n={...e.vars},r={...e.defaults},o=e.required;for(let[i,s]of Object.entries(t))if(i==="required")o=Tt(s);else if(i==="defaults")Object.assign(r,Hn(s));else n[i]=K(s);return{vars:n,required:o,defaults:r}}function WS(e,t){if(!I(t))return e;return{type:t.type!==void 0?K(t.type):e.type,url:t.url!==void 0?K(t.url):e.url,address:t.address!==void 0?K(t.address):e.address,command:t.command!==void 0?Ut(t.command):e.command,interval_seconds:t.interval_seconds!==void 0?xe(t.interval_seconds):e.interval_seconds,timeout_seconds:t.timeout_seconds!==void 0?xe(t.timeout_seconds):e.timeout_seconds,start_period_seconds:t.start_period_seconds!==void 0?xe(t.start_period_seconds):e.start_period_seconds,unhealthy_threshold:t.unhealthy_threshold!==void 0?xe(t.unhealthy_threshold):e.unhealthy_threshold,healthy_reset_threshold:t.healthy_reset_threshold!==void 0?xe(t.healthy_reset_threshold):e.healthy_reset_threshold}}function GS(e,t){if(!I(t))return e;return{type:t.type!==void 0?K(t.type):e.type,mode:t.mode!==void 0?K(t.mode):e.mode,service_account:t.service_account!==void 0?K(t.service_account):e.service_account,config:t.config!==void 0&&I(t.config)?{...e.config??{},...t.config}:e.config}}function US(e,t){if(!I(t))return e;return{stdout:t.stdout!==void 0?it(t.stdout):e.stdout,stderr:t.stderr!==void 0?it(t.stderr):e.stderr}}function KS(e,t){if(!I(t))return e;return{enabled:t.enabled!==void 0?it(t.enabled):e.enabled,policy:t.policy!==void 0?K(t.policy):e.policy,max_retries:t.max_retries!==void 0?xe(t.max_retries):e.max_retries,backoff_seconds:t.backoff_seconds!==void 0?xe(t.backoff_seconds):e.backoff_seconds}}function qS(e,t){if(!I(t))return e;return{wait_for_healthy:t.wait_for_healthy!==void 0?it(t.wait_for_healthy):e.wait_for_healthy,timeout_seconds:t.timeout_seconds!==void 0?xe(t.timeout_seconds):e.timeout_seconds}}function VS(e,t){if(!I(t))return e;let n=Nl(t);if(!n)return e;return{image:t.image!==void 0?n.image:e?.image??"",runtime:t.runtime!==void 0?n.runtime:e?.runtime??"",ports:t.ports!==void 0?{...e?.ports??{},...n.ports}:e?.ports??{},env:t.env!==void 0?{...e?.env??{},...n.env}:e?.env??{},volumes:t.volumes!==void 0?n.volumes:e?.volumes??[]}}function ng(e,t){let n=Object.keys(e.services).sort();for(let r of n){let o=e.services[r]?.proxy??[];if(o.length===0)continue;o.forEach((i,s)=>{let c={name:o.length===1?r:`${r}-${s+1}`,match:i.match,upstream:i.upstream,auth:i.auth},l=e.proxy.routes.length;if(e.proxy.routes.push(c),t)Bn(t,c,`synthesized from services.${r}.proxy`,"synthesized",`proxy.routes.${l}`)})}}function rg(e,t=Vs()){if(Object.keys(e.templates).length===0)return;let n={};for(let[r,o]of Object.entries(e.services))n[r]=og(e,o,t.services[r]??new Set,t,{});e.services=n}function og(e,t,n,r,o){if(t.extends==="")return t;if(o[t.extends])throw Error(`template cycle involving "${t.extends}"`);let i=e.templates[t.extends]??Nn();if(!e.templates[t.extends])throw Error(`service extends unknown template "${t.extends}"`);let s={...o,[t.extends]:!0},a=r.templates[t.extends]??new Set,c=og(e,i,a,r,s),l=JS(c,t,n);return l.extends=t.extends,l}function JS(e,t,n){let r={...e};if(n.has("extends"))r.extends=t.extends;if(n.has("description"))r.description=t.description;if(n.has("command"))r.command=t.command;if(n.has("shell"))r.shell=t.shell;if(n.has("working_dir"))r.working_dir=t.working_dir;if(n.has("dependencies"))r.dependencies=t.dependencies;if(n.has("ports"))r.ports=t.ports;if(n.has("capabilities"))r.capabilities=t.capabilities;if(n.has("proxy"))r.proxy=t.proxy;if(n.has("container")){let o=t.container,i=e.container;r.container=o&&i?{image:n.has("container.image")?o.image:i.image,runtime:n.has("container.runtime")?o.runtime:i.runtime,ports:n.has("container.ports")?o.ports:i.ports,env:n.has("container.env")?o.env:i.env,volumes:n.has("container.volumes")?o.volumes:i.volumes}:o??i}return r.hooks={pre_start:n.has("hooks.pre_start")?t.hooks.pre_start:e.hooks.pre_start,post_start:n.has("hooks.post_start")?t.hooks.post_start:e.hooks.post_start},r.health={type:n.has("health.type")?t.health.type:e.health.type,url:n.has("health.url")?t.health.url:e.health.url,address:n.has("health.address")?t.health.address:e.health.address,command:n.has("health.command")?t.health.command:e.health.command,interval_seconds:n.has("health.interval_seconds")?t.health.interval_seconds:e.health.interval_seconds,timeout_seconds:n.has("health.timeout_seconds")?t.health.timeout_seconds:e.health.timeout_seconds,start_period_seconds:n.has("health.start_period_seconds")?t.health.start_period_seconds:e.health.start_period_seconds,unhealthy_threshold:n.has("health.unhealthy_threshold")?t.health.unhealthy_threshold:e.health.unhealthy_threshold,healthy_reset_threshold:n.has("health.healthy_reset_threshold")?t.health.healthy_reset_threshold:e.health.healthy_reset_threshold},r.identity={type:n.has("identity.type")?t.identity.type:e.identity.type,mode:n.has("identity.mode")?t.identity.mode:e.identity.mode,service_account:n.has("identity.service_account")?t.identity.service_account:e.identity.service_account,config:n.has("identity.config")?t.identity.config:e.identity.config},r.logs={stdout:n.has("logs.stdout")?t.logs.stdout:e.logs.stdout,stderr:n.has("logs.stderr")?t.logs.stderr:e.logs.stderr},r.restart={enabled:n.has("restart.enabled")?t.restart.enabled:e.restart.enabled,policy:n.has("restart.policy")?t.restart.policy:e.restart.policy,max_retries:n.has("restart.max_retries")?t.restart.max_retries:e.restart.max_retries,backoff_seconds:n.has("restart.backoff_seconds")?t.restart.backoff_seconds:e.restart.backoff_seconds},r.startup={wait_for_healthy:n.has("startup.wait_for_healthy")?t.startup.wait_for_healthy:e.startup.wait_for_healthy,timeout_seconds:n.has("startup.timeout_seconds")?t.startup.timeout_seconds:e.startup.timeout_seconds},r.environment={vars:{...e.environment.vars,...t.environment.vars},defaults:{...e.environment.defaults,...t.environment.defaults},required:n.has("environment.required")?t.environment.required:e.environment.required},r}var FS;var ig=D(()=>{jl();FS=["environment","restart","startup","health","logs","identity","container","hooks"]});function Bl(e){if(e.version===0)throw L(ee,"version is required");if(e.version===oo)return e;throw L(ee,`unsupported config version ${e.version} (expected ${oo}); no migration is available`)}var Wl=D(()=>{Pe()});var sg,ag,cg,lg,dg,ug,pg,fg,gg,Gl,Ul,Kl,ql,mg,hg,vg,yg,xg,bg,wg,Sg,Vl,kg,Cg,_g,Rg,Pg,Eg,Tg,Ag,Lg,Dg,Og,Mg;var Jl=D(()=>{sg=["version","project","google","profiles","templates","services","tasks","proxy","logs","auth","shutdown","ui","secrets","doctor","plugins","environment"],ag=["extends","description","command","shell","working_dir","dependencies","ports","environment","health","identity","logs","restart","startup","capabilities","proxy","container","hooks"],cg=["type","url","address","command","interval_seconds","timeout_seconds","start_period_seconds","unhealthy_threshold","healthy_reset_threshold"],lg=["service","condition"],dg=["type","mode","service_account","config"],ug=["enabled","policy","max_retries","backoff_seconds"],pg=["wait_for_healthy","timeout_seconds"],fg=["enabled","listen","token_endpoint","routes"],gg=["host","port"],Gl=["name","match","upstream","auth"],Ul=["host","path"],Kl=["url"],ql=["type","identity","audience","service_account"],mg=["max_memory_events","persistence"],hg=["enabled","directory","retention_days","max_session_logs"],vg=["refresh_threshold_seconds"],yg=["stop_services_on_exit","grace_seconds"],xg=["theme","keymap"],bg=["name"],wg=["project_id","region"],Sg=["services","environment"],Vl=["required","defaults"],kg=["stdout","stderr"],Cg=["extra_markers","extra_patterns"],_g=["tools"],Rg=["name","command"],Pg=["enabled","host","port"],Eg=["path"],Tg=["sources","secrets"],Ag=["image","runtime","ports","env","volumes"],Lg=["pre_start","post_start"],Dg=["command","shell","working_dir","dependencies","environment"],Og=["google","google_api","iap","network","service_identity","local_http"],Mg=["|","||","&&",";",">",">>","<","&"]});function Wn(e,t){if(Array.isArray(e))return e.flatMap((o,i)=>Wn(o,$g(t,String(i))));if(!nk(e))return[];let n=XS(t),r=[];for(let o of Object.keys(e)){let i=$g(t,o);if(n.length>0&&!n.includes(o)&&!YS(t))r.push(i);r.push(...Wn(e[o],i))}return r}function YS(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 XS(e){switch(e){case"":return sg;case"project":return bg;case"google":return wg;case"proxy":return fg;case"proxy.listen":return gg;case"proxy.token_endpoint":return Pg;case"logs":return mg;case"logs.persistence":return hg;case"auth":return vg;case"shutdown":return yg;case"ui":return xg;case"secrets":return Cg;case"doctor":return _g;case"environment":return Tg;default:return QS(e)}}function QS(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return[];if(e.startsWith("services.")||e.startsWith("templates.")){if(e.includes(".dependencies."))return lg;return ZS(e)}if(e.startsWith("profiles.")){if(e.split(".").length===2)return Sg}if(e.startsWith("tasks.")){let t=e.split(".");if(t.length===2)return Dg;if(t[2]==="environment")return Vl}if(e.includes("routes"))return tk(e);if(e.startsWith("doctor.tools")&&e.split(".").length>=3)return Rg;if(e==="plugins")return[];if(e.startsWith("plugins.")&&e.split(".").length===2)return Eg;return[]}function ZS(e){let t=e.split(".");if(t.length===2)return ag;if(t.length>=3)switch(t[2]){case"health":return cg;case"identity":return dg;case"restart":return ug;case"startup":return pg;case"logs":return kg;case"environment":return Vl;case"proxy":return ek(t);case"container":return Ag;case"hooks":return Lg;default:return[]}return[]}function ek(e){let t=e.slice(3),n=t[0]!==void 0&&/^\d+$/.test(t[0])?1:0,r=t[n]??"";if(r==="match")return Ul;if(r==="upstream")return Kl;if(r==="auth")return ql;return Gl}function tk(e){if(e==="proxy.routes")return[];if(e.endsWith(".match"))return Ul;if(e.endsWith(".upstream"))return Kl;if(e.endsWith(".auth"))return ql;if(e.includes("proxy.routes")&&e.split(".").length===zS+1)return Gl;return[]}function $g(e,t){if(e==="")return t;return`${e}.${t}`}function nk(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function so(e){return`unknown fields: ${e.join(", ")}`}var zS=2;var Ig=D(()=>{Jl()});function Fg(e,t,n){let r=e,o="";for(;;){let i=r.indexOf("${");if(i<0)return o+r;o+=r.slice(0,i);let s=r.slice(i).indexOf("}");if(s<0)throw Error(`unclosed environment reference in "${e}"`);let a=r.slice(i+2,i+s);o+=rk(a,t,n),r=r.slice(i+s+1)}}function rk(e,t,n){let r=e.split(".");if(r.length<3||r[0]!=="services")throw Error(`unsupported reference \${${e}}`);let o=r[1]??"",i=t.services[o];if(!i)throw Error(`reference \${${e}}: unknown service`);let s=n[o];if(r[2]==="port"){if(s){if(s.http!==void 0)return String(s.http);let c=Object.values(s)[0];if(c!==void 0)return String(c)}let a=Jf(i.ports);if(a&&!a.auto)return String(a.value)}if(r[2]==="ports"){let a=r[3];if(!a)throw Error(`reference \${${e}}: missing port name`);if(s&&s[a]!==void 0)return String(s[a]);let c=Ol(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 zl(e,t,n){let r={};for(let[o,i]of Object.entries(e))r[o]=Fg(i,t,n);return r}function Ng(e){let t=[],n=e;for(;;){let r=n.indexOf("${");if(r<0)return t;let o=n.slice(r).indexOf("}");if(o<0)return t;t.push(n.slice(r+2,r+o)),n=n.slice(r+o+1)}}function Hg(e,t){let n=e.split(".");if(n.length<2)return!1;if(n[0]!=="services"||n.length<3)return!1;let r=t.services[n[1]??""];if(!r)return!1;if(n[2]==="port"||n[2]==="ports"){if(n.length===3)return r.ports.length>0;if(n.length>=4){let o=n[3]??"";if(!Number.isNaN(Number.parseInt(o,10)))return!0;return Ol(r.ports,o)!==void 0}}return!0}var Yl=()=>{};import{existsSync as ok}from"fs";import{isAbsolute as ik,resolve as sk}from"path";import{fileURLToPath as ak}from"url";function Xl(e){let t=[];for(let[n,r]of Object.entries(e.services)){let o=r.health.type;if(o!==""&&!Wg.includes(o.toLowerCase()))t.push({service:n,type:o})}return t}function Xt(e){let t=[];if(e.version===0)t.push("version is required");else if(e.version!==oo)t.push(`unsupported config version ${e.version} (expected ${oo}); no migration is available`);if(Object.keys(e.services).length===0)t.push("at least one service must be defined");t.push(...dk(e)),t.push(...lk(e)),t.push(...gk(e)),t.push(...mk(e)),t.push(...hk(e));for(let[n,r]of e.plugins.entries())if(r.path==="")t.push(`plugins.${n}.path is required`);else try{let o=r.path.startsWith("file:")?ak(r.path):ik(r.path)?r.path:sk(e.repoRoot,r.path);if(!ok(o))t.push(`plugins.${n}.path does not exist: ${r.path}`)}catch{t.push(`plugins.${n}.path is invalid: ${r.path}`)}if(e.logs.max_memory_events<0)t.push("logs.max_memory_events must be >= 0");return t}function lk(e){let t=[];for(let[n,r]of Object.entries(e.tasks)){let o=`tasks.${n}`;if(io(r.command))t.push(`${o}.command is required`);t.push(...Js(o,r.command,r.shell));for(let i of r.dependencies)if(!e.services[i])t.push(`${o}.dependencies: unknown service "${i}"`);t.push(...Gg(`${o}.environment`,r.environment,e))}return t}function dk(e){let t=[],n={};for(let[r,o]of Object.entries(e.services)){let i=`services.${r}`;if(io(o.command)&&!o.container)t.push(`${i}.command is required`);t.push(...Js(i,o.command,o.shell)),t.push(...Js(`${i}.hooks.pre_start`,o.hooks.pre_start,o.shell)),t.push(...Js(`${i}.hooks.post_start`,o.hooks.post_start,o.shell)),t.push(...yk(i,o.capabilities));for(let c of o.dependencies){let l=wt(c);if(!e.services[l])t.push(`${i}.dependencies: unknown service "${l}"`);if(l===r)t.push(`${i}.dependencies: service cannot depend on itself`);if(!["service_started","service_healthy"].includes(Fn(c)))t.push(`${i}.dependencies: condition must be service_started or service_healthy`);if(Fn(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<Ys||c.value>zs)t.push(`${i}.ports.${c.name}: invalid port ${c.value}`);else if(n[c.value])t.push(`duplicate port ${c.value} used by ${n[c.value]} and ${r}`);else n[c.value]=r;let s=fk(`${i}.identity`,o.identity,e.plugins.length>0);if(s!=="")t.push(s);if(t.push(...uk(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=Kf(o.restart);if(a!==Bf&&a!==Wf&&a!==Gf)t.push(`${i}.restart.policy must be never, on_failure, or always`);if(t.push(...Gg(`${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<Ys||l>zs)t.push(`${i}.container.ports.${c}: invalid container port ${l}`)}}}return t}function uk(e,t,n){let r=[];if(t.health.type==="")return r;let o=t.health.type.toLowerCase();if(!Wg.includes(o)&&!n)r.push(`${e}.health.type must be http, tcp, process, or command`);if(o==="http"&&t.health.url==="")r.push(`${e}.health.url is required for http health checks`);if(o==="tcp"&&t.health.address===""&&t.ports.length===0)r.push(`${e}.health.address is required for tcp health checks without ports`);if(o==="command"&&io({args:t.health.command.args,shell:!1}))r.push(`${e}.health.command is required for command health checks`);return r}function fk(e,t,n){let r=fi(t).toLowerCase();if(r===""||r==="user")return"";if(r==="service"||r==="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(n)return"";return`${e}.type must be user or service_account`}function Ql(e){let t=[];for(let[n,r]of Object.entries(e.services)){let o=fi(r.identity).toLowerCase();if(!pk.includes(o))t.push({service:n,type:o})}return t}function gk(e){let t={},n=[],r=[],o=(i)=>{let s=t[i]??ck;if(s===Bg)return;if(s===jg){n.push(`dependency cycle: ${[...r,i].join(" \u2192 ")}`);return}t[i]=jg,r.push(i);let a=e.services[i];if(a)for(let c of a.dependencies){let l=wt(c);if(e.services[l])o(l)}r.pop(),t[i]=Bg};for(let i of Object.keys(e.services))o(i);return n}function Gg(e,t,n){let r=[],o=(i,s)=>{for(let a of Ng(s))if(!Hg(a,n))r.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 r}function mk(e){let t=[];for(let[n,r]of Object.entries(e.profiles))for(let o of r.services)if(!e.services[o])t.push(`profiles.${n} references unknown service "${o}"`);return t}function hk(e){let t=[];if(e.proxy.listen.host!==""&&!xk(e.proxy.listen.host))t.push("proxy.listen.host must be an IP address or localhost");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<Ys||e.proxy.listen.port>zs))t.push("proxy.listen.port is invalid");let n={};return e.proxy.routes.forEach((r,o)=>{let i=`proxy.routes[${o}]`;if(r.name==="")t.push(`${i}.name is required`);else if(n[r.name])t.push(`${i}: duplicate route name ${r.name}`);if(n[r.name]=!0,r.upstream.url==="")t.push(`${i}.upstream.url is required`);if(r.auth.type.toLowerCase()==="iap"){if(r.auth.audience.trim()==="")t.push(`${i}.auth.audience is required when auth.type is iap`);if(r.auth.identity.type.trim()==="")t.push(`${i}.auth.identity.type is required when auth.type is iap`)}let s=r.auth.identity.type.toLowerCase();if(s==="service"||s==="service_account"||yn({type:s,mode:"",service_account:""})){if((r.auth.identity.service_account||r.auth.service_account)==="")t.push(`${i}.auth.identity.service_account is required`)}}),t.push(...vk(e)),t}function vk(e){let t=[],n=e.proxy.token_endpoint;if(!n.enabled)return t;let r=n.host||Tl;if(r==="0.0.0.0")t.push("proxy.token_endpoint.host must be a loopback address");else if(r!==""&&r!==Tl&&r!=="localhost"&&!r.startsWith("127."))t.push("proxy.token_endpoint.host must be a loopback address");if(n.port!==0&&(n.port<Ys||n.port>zs))t.push("proxy.token_endpoint.port is invalid");return t}function Js(e,t,n){if(t.shell||n)return[];for(let r of t.args)if(Mg.includes(r)||r.includes("|")||r.includes(";")||r.includes("&&"))return[`${e}.command contains shell metacharacters; set shell: true to run via a shell`];return[]}function yk(e,t){let n=[];for(let r of t)if(!Og.includes(r))n.push(`${e}.capabilities: unknown capability "${r}"`);return n}function xk(e){if(e==="localhost")return!0;return bk(e)}function bk(e){if(/^(\d{1,3}\.){3}\d{1,3}$/.test(e))return e.split(".").every((n)=>{let r=Number(n);return r>=0&&r<=255});return e.includes(":")}var zs=65535,Ys=1,Wg,ck=0,jg=1,Bg=2,pk;var Xs=D(()=>{Jl();Yl();Wg=["http","tcp","process","command"];pk=["","user","service","service_account"]});import{readdirSync as wk,readFileSync as Sk}from"fs";import{basename as Kg,dirname as kk,extname as Ck,join as mr,resolve as _k}from"path";import{parse as Rk}from"yaml";function ao(e,t,n){try{return xi(e,t,{candidateText:n}),[]}catch(r){if(r instanceof Oe){let o=`${r.kind}: `;return[r.message.startsWith(o)?r.message.slice(o.length):r.message]}return[r instanceof Error?r.message:String(r)]}}function ut(e,t){let{repoRoot:n,configPath:r}=jn(e,t);return xi(n,r)}function yi(e,t){try{return ut(e,t)}catch(n){if(!Ms(n,ar))throw n;let r=e===""?process.cwd():_k(e),o=pr();return o.repoRoot=r,o.configPath=mr(r,St,Kt),o}}function xi(e,t,n){let r=pr(),o=Vs();qg(t,r,o,n?.candidateText,"main"),r.repoRoot=e,r.configPath=t;let i=kk(t);if(Kg(i)===St)Ek(i,r,o);r=Pk(r,e,t,o),r=Bl(r);try{rg(r,o)}catch(a){throw fe(ee,"template merge failed",a)}ng(r,o.provenance),r.provenance=o.provenance;let s=Xt(r);if(s.length>0)throw L(ee,s.join(`
5
- `));return r}function Pk(e,t,n,r){let o=mr(Ft(),"config.local.yaml"),i=mr(t,St,"config.local.yaml");for(let s of[o,i]){if(s===n||!hi(s))continue;qg(s,e,r,void 0,s===o?"home_local":"repo_local")}return e}function qg(e,t,n,r,o="main"){let i=r!==void 0?Vg(r,e):Zl(e),s=Wn(i,"");if(s.length>0)throw L(ee,so(s));eg(t,i,n,{source:e,layer:o})}function Ek(e,t,n){Ug(mr(e,"services"),(o,i,s)=>{let a=Wn(i,`services.${o}`);if(a.length>0)throw L(ee,so(a));let c=t.services[o];t.services[o]=c?qs(c,i):mi(i),Ks(n.services,o,i),Bn(n.provenance,i,s,"modular_service",`services.${o}`)}),Ug(mr(e,"profiles"),(o,i,s)=>{let a=Wn(i,`profiles.${o}`);if(a.length>0)throw L(ee,so(a));t.profiles[o]=Ws(i),Bn(n.provenance,i,s,"modular_profile",`profiles.${o}`)});let r=mr(e,"proxy","routes.yaml");if(hi(r))Tk(r,t,n)}function Tk(e,t,n){let r=Zl(e);if(I(r.proxy))Bn(n.provenance,r.proxy,e,"modular_proxy","proxy");if(Array.isArray(r.routes))Bn(n.provenance,r.routes,e,"modular_proxy","proxy.routes");if(I(r.proxy)){let o=Wn({proxy:r.proxy},"");if(o.length>0)throw L(ee,so(o))}if(Array.isArray(r.routes))r.routes.forEach((o,i)=>{let s=Wn(o,`proxy.routes.${i}`);if(s.length>0)throw L(ee,so(s))});if(I(r.proxy)){if(Array.isArray(r.proxy.routes))t.proxy.routes.push(...r.proxy.routes.map((o)=>gr(o)));if(I(r.proxy.listen)){if(typeof r.proxy.listen.host==="string"&&r.proxy.listen.host!=="")t.proxy.listen.host=r.proxy.listen.host;if(typeof r.proxy.listen.port==="number"&&r.proxy.listen.port!==0)t.proxy.listen.port=r.proxy.listen.port}}if(Array.isArray(r.routes))t.proxy.routes.push(...r.routes.map((o)=>gr(o)))}function Ug(e,t){let n;try{n=wk(e)}catch(r){if(Ak(r))return;throw r}for(let r of n.sort()){let o=Ck(r);if(o!==".yaml"&&o!==".yml")continue;let i=mr(e,r),s=Zl(i);t(Kg(r,o),s,i)}}function Zl(e){let t;try{t=Sk(e,"utf8")}catch(n){throw fe(ee,"unable to read config",n)}return Vg(t,e)}function Vg(e,t){let n;try{n=Rk(e)}catch(r){throw fe(ee,`invalid YAML in ${t}`,r)}if(n===null||n===void 0)return{};if(!I(n))throw L(ee,`unable to decode config: ${t} is not a mapping`);return n}function Ak(e){return typeof e==="object"&&e!==null&&"code"in e&&e.code==="ENOENT"}var Jg=D(()=>{Pe();ze();jl();vi();ig();Wl();Ig();Xs();vi();Xs()});function hr(e){return Object.entries(e.provenance).sort(([t],[n])=>t.localeCompare(n)).flatMap(([t,n])=>{let r=n[n.length-1];if(!r)return[];return[{path:t,value:Lk(e,t),...r,shadowed:n.slice(0,-1)}]})}function Lk(e,t){let n=e;for(let r of t.split(".")){if(typeof n!=="object"||n===null)return;n=n[r]}return n}var st=D(()=>{Jg();vi();Xs();Wl();Yl()});import{readdirSync as Dk}from"fs";import{join as zg,resolve as ed,sep as td}from"path";function Ok(e){let t=zg(Ft(),"state"),n;try{n=Dk(t)}catch{return}let r=Yg(ed(e)),o;for(let i of n){let s=Rl(zg(t,i,"state.json"));if(!s)continue;let a=Yg(ed(s.repo_root));if(!Mk(a,r))continue;if(!o||a.length>o.length)o=a}return o}function Qs(e,t,n=""){if(t!=="")return{repoRoot:ed(t),source:"explicit"};let r=e===""?process.cwd():e;try{let{repoRoot:i}=jn(r,n);return{repoRoot:i,source:"config"}}catch{if(n!=="")return}let o=Ok(r);return o?{repoRoot:o,source:"state-scan"}:void 0}function Yg(e){let t=e.length>1&&e.endsWith(td)?e.slice(0,-1):e;return process.platform==="win32"?t.toLowerCase():t}function Mk(e,t){return t===e||t.startsWith(e.endsWith(td)?e:`${e}${td}`)}var Xg=D(()=>{vi();ze()});import{existsSync as Zg,readFileSync as em}from"fs";import{isAbsolute as $k,join as nd}from"path";import{parse as Ik}from"dotenv";function Hk(){return{name:"dotenv",load:(e)=>{let t=Qg(e.repoRoot,e.profile);if(e.workDir!=="")Object.assign(t,Qg(e.workDir,e.profile));return t}}}function Zs(e){return async(t)=>{let n=t.includes("/versions/")?t:`${t}/versions/latest`,r=await e(),o=await fetch(`https://secretmanager.googleapis.com/v1/${n}:access`,{headers:{Authorization:`Bearer ${r}`}});if(!o.ok)throw L(ee,`secret manager request failed for ${t}: HTTP ${o.status}`);let i=await o.json();if(!i.payload?.data)throw L(ee,`secret manager response for ${t} had no payload`);return Buffer.from(i.payload.data,"base64").toString("utf8")}}function jk(e){let t=e?.environment.sources??[];if(t.length===0)return[...wi];let n=new Set([...Nk,...t]),r=new Set(wi),o=t.filter((s)=>!r.has(s)),i=[];for(let s of wi){if(s==="defaults")i.push(...o);if(n.has(s))i.push(s)}return i}async function ea(e,t){let n={},r=t.serviceCfg.working_dir;if(r!==""&&!$k(r))r=nd(e,r);let o={repoRoot:e,profile:t.profile,service:t.service,serviceCfg:t.serviceCfg,workDir:r,cfg:t.cfg},i=Bk(t),s={process:t.includeProcess===!1?{}:t.clientEnv??co(),profile:bi(t.profileEnv,t.cfg,i),dotenv:bi(await Hk().load(o),t.cfg,i),generated:{},keychain:t.sourceValues?.keychain??Wk(o),secret_manager:t.sourceValues?.secret_manager??await Gk(o,t.fetchSecret),defaults:bi(t.serviceCfg.environment.defaults,t.cfg,i),vars:bi(t.serviceCfg.environment.vars,t.cfg,i),runtime:t.runtime};for(let a of jk(t.cfg)){if(s[a]!==void 0){Object.assign(n,s[a]);continue}let c=t.pluginSources?.find((l)=>l.name===a);if(c)Object.assign(n,bi(await c.load(o),t.cfg,i))}for(let a of t.serviceCfg.environment.required)if((n[a]??"").trim()==="")throw L(ee,`service ${t.service} missing required environment variable ${a}`);return n}function Bk(e){let t={};if(e.cfg)for(let[n,r]of Object.entries(e.cfg.services)){let o={};for(let i of r.ports)if(!i.auto)o[i.name]=i.value;t[n]=o}if(e.assignedPorts)t[e.service]=e.assignedPorts;return t}function bi(e,t,n){if(!t||Object.keys(e).length===0)return e;return zl(e,t,n)}function Wk(e){let t=e.cfg?.environment.sources??[];if(t.length>0&&!t.includes("keychain"))return{};let n={},r=new Set([...Object.keys(e.serviceCfg.environment.defaults),...Object.keys(e.serviceCfg.environment.vars),...e.serviceCfg.environment.required]);for(let o of r){let i=nd(Wt(),"env",o);if(!Zg(i))continue;try{n[o]=em(i,"utf8").replace(/\n$/,"")}catch(s){throw fe(ee,`unable to read keychain env ${o}`,s)}}return n}async function Gk(e,t){if(!(e.cfg?.environment.sources??[]).includes("secret_manager"))return{};let r=e.cfg?.environment.secrets??{},o={};for(let[i,s]of Object.entries(r)){if(!Fk.test(s))throw L(ee,`environment.secrets.${i} is not a Secret Manager resource`);if(!t)throw L(ee,"environment source secret_manager is not configured \u2014 set credentials or remove it from environment.sources");o[i]=await t(s)}return o}function Qg(e,t){let n={},r=[".env",".env.development",".env.local"];if(t!=="")r.push(`.env.${t}`);for(let o of r){let i=nd(e,o);if(!Zg(i))continue;try{let s=Ik(em(i));Object.assign(n,s)}catch(s){throw fe(ee,`unable to read ${i}`,s)}}return n}function co(){let e={};for(let[t,n]of Object.entries(process.env))if(n!==void 0)e[t]=n;return e}function ta(e,t,n,r,o){let i={DEVCTL_SERVICE_NAME:e,DEVCTL_ENVIRONMENT:o,SERVICE_HOST:t};if(r!=="")i.DEVCTL_PROXY_URL=r;if(n.http!==void 0)i.SERVICE_PORT=String(n.http);else{let s=Object.values(n)[0];if(s!==void 0)i.SERVICE_PORT=String(s)}for(let[s,a]of Object.entries(n))i[`${s.toUpperCase()}_PORT`]=String(a);return i}function Si(e){return{...e}}var wi,Fk,Nk;var rd=D(()=>{st();Pe();ze();wi=["process","profile","dotenv","generated","keychain","secret_manager","defaults","vars","runtime"],Fk=/^projects\/[^/]+\/secrets\/[^/]+(?:\/versions\/[^/]+)?$/,Nk=["process","defaults","vars","runtime"]});function At(){return"devctl 0.2.1"}var pt="0.2.1",ki=1;import{createConnection as Uk}from"net";var{spawn:Kk}=globalThis.Bun;function Yk(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 ${ki}`}function Xk(e){if(e.compatible&&e.daemonVersion!==void 0&&e.daemonVersion!==pt)return`attached daemon is devctl ${e.daemonVersion}; this client is ${pt} (run \`devctl down\` then start again to update it)`;return}function od(e,t){if(!e.compat.compatible&&!zk.has(t))throw ke(Je,Yk(e.compat),"run `devctl down` to stop it, then start again")}class rm{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 n=this.buf.split(`
5
+ `));return r}function Pk(e,t,n,r){let o=mr(Ft(),"config.local.yaml"),i=mr(t,St,"config.local.yaml");for(let s of[o,i]){if(s===n||!hi(s))continue;qg(s,e,r,void 0,s===o?"home_local":"repo_local")}return e}function qg(e,t,n,r,o="main"){let i=r!==void 0?Vg(r,e):Zl(e),s=Wn(i,"");if(s.length>0)throw L(ee,so(s));eg(t,i,n,{source:e,layer:o})}function Ek(e,t,n){Ug(mr(e,"services"),(o,i,s)=>{let a=Wn(i,`services.${o}`);if(a.length>0)throw L(ee,so(a));let c=t.services[o];t.services[o]=c?qs(c,i):mi(i),Ks(n.services,o,i),Bn(n.provenance,i,s,"modular_service",`services.${o}`)}),Ug(mr(e,"profiles"),(o,i,s)=>{let a=Wn(i,`profiles.${o}`);if(a.length>0)throw L(ee,so(a));t.profiles[o]=Ws(i),Bn(n.provenance,i,s,"modular_profile",`profiles.${o}`)});let r=mr(e,"proxy","routes.yaml");if(hi(r))Tk(r,t,n)}function Tk(e,t,n){let r=Zl(e);if(I(r.proxy))Bn(n.provenance,r.proxy,e,"modular_proxy","proxy");if(Array.isArray(r.routes))Bn(n.provenance,r.routes,e,"modular_proxy","proxy.routes");if(I(r.proxy)){let o=Wn({proxy:r.proxy},"");if(o.length>0)throw L(ee,so(o))}if(Array.isArray(r.routes))r.routes.forEach((o,i)=>{let s=Wn(o,`proxy.routes.${i}`);if(s.length>0)throw L(ee,so(s))});if(I(r.proxy)){if(Array.isArray(r.proxy.routes))t.proxy.routes.push(...r.proxy.routes.map((o)=>gr(o)));if(I(r.proxy.listen)){if(typeof r.proxy.listen.host==="string"&&r.proxy.listen.host!=="")t.proxy.listen.host=r.proxy.listen.host;if(typeof r.proxy.listen.port==="number"&&r.proxy.listen.port!==0)t.proxy.listen.port=r.proxy.listen.port}}if(Array.isArray(r.routes))t.proxy.routes.push(...r.routes.map((o)=>gr(o)))}function Ug(e,t){let n;try{n=wk(e)}catch(r){if(Ak(r))return;throw r}for(let r of n.sort()){let o=Ck(r);if(o!==".yaml"&&o!==".yml")continue;let i=mr(e,r),s=Zl(i);t(Kg(r,o),s,i)}}function Zl(e){let t;try{t=Sk(e,"utf8")}catch(n){throw fe(ee,"unable to read config",n)}return Vg(t,e)}function Vg(e,t){let n;try{n=Rk(e)}catch(r){throw fe(ee,`invalid YAML in ${t}`,r)}if(n===null||n===void 0)return{};if(!I(n))throw L(ee,`unable to decode config: ${t} is not a mapping`);return n}function Ak(e){return typeof e==="object"&&e!==null&&"code"in e&&e.code==="ENOENT"}var Jg=D(()=>{Pe();ze();jl();vi();ig();Wl();Ig();Xs();vi();Xs()});function hr(e){return Object.entries(e.provenance).sort(([t],[n])=>t.localeCompare(n)).flatMap(([t,n])=>{let r=n[n.length-1];if(!r)return[];return[{path:t,value:Lk(e,t),...r,shadowed:n.slice(0,-1)}]})}function Lk(e,t){let n=e;for(let r of t.split(".")){if(typeof n!=="object"||n===null)return;n=n[r]}return n}var st=D(()=>{Jg();vi();Xs();Wl();Yl()});import{readdirSync as Dk}from"fs";import{join as zg,resolve as ed,sep as td}from"path";function Ok(e){let t=zg(Ft(),"state"),n;try{n=Dk(t)}catch{return}let r=Yg(ed(e)),o;for(let i of n){let s=Rl(zg(t,i,"state.json"));if(!s)continue;let a=Yg(ed(s.repo_root));if(!Mk(a,r))continue;if(!o||a.length>o.length)o=a}return o}function Qs(e,t,n=""){if(t!=="")return{repoRoot:ed(t),source:"explicit"};let r=e===""?process.cwd():e;try{let{repoRoot:i}=jn(r,n);return{repoRoot:i,source:"config"}}catch{if(n!=="")return}let o=Ok(r);return o?{repoRoot:o,source:"state-scan"}:void 0}function Yg(e){let t=e.length>1&&e.endsWith(td)?e.slice(0,-1):e;return process.platform==="win32"?t.toLowerCase():t}function Mk(e,t){return t===e||t.startsWith(e.endsWith(td)?e:`${e}${td}`)}var Xg=D(()=>{vi();ze()});import{existsSync as Zg,readFileSync as em}from"fs";import{isAbsolute as $k,join as nd}from"path";import{parse as Ik}from"dotenv";function Hk(){return{name:"dotenv",load:(e)=>{let t=Qg(e.repoRoot,e.profile);if(e.workDir!=="")Object.assign(t,Qg(e.workDir,e.profile));return t}}}function Zs(e){return async(t)=>{let n=t.includes("/versions/")?t:`${t}/versions/latest`,r=await e(),o=await fetch(`https://secretmanager.googleapis.com/v1/${n}:access`,{headers:{Authorization:`Bearer ${r}`}});if(!o.ok)throw L(ee,`secret manager request failed for ${t}: HTTP ${o.status}`);let i=await o.json();if(!i.payload?.data)throw L(ee,`secret manager response for ${t} had no payload`);return Buffer.from(i.payload.data,"base64").toString("utf8")}}function jk(e){let t=e?.environment.sources??[];if(t.length===0)return[...wi];let n=new Set([...Nk,...t]),r=new Set(wi),o=t.filter((s)=>!r.has(s)),i=[];for(let s of wi){if(s==="defaults")i.push(...o);if(n.has(s))i.push(s)}return i}async function ea(e,t){let n={},r=t.serviceCfg.working_dir;if(r!==""&&!$k(r))r=nd(e,r);let o={repoRoot:e,profile:t.profile,service:t.service,serviceCfg:t.serviceCfg,workDir:r,cfg:t.cfg},i=Bk(t),s={process:t.includeProcess===!1?{}:t.clientEnv??co(),profile:bi(t.profileEnv,t.cfg,i),dotenv:bi(await Hk().load(o),t.cfg,i),generated:{},keychain:t.sourceValues?.keychain??Wk(o),secret_manager:t.sourceValues?.secret_manager??await Gk(o,t.fetchSecret),defaults:bi(t.serviceCfg.environment.defaults,t.cfg,i),vars:bi(t.serviceCfg.environment.vars,t.cfg,i),runtime:t.runtime};for(let a of jk(t.cfg)){if(s[a]!==void 0){Object.assign(n,s[a]);continue}let c=t.pluginSources?.find((l)=>l.name===a);if(c)Object.assign(n,bi(await c.load(o),t.cfg,i))}for(let a of t.serviceCfg.environment.required)if((n[a]??"").trim()==="")throw L(ee,`service ${t.service} missing required environment variable ${a}`);return n}function Bk(e){let t={};if(e.cfg)for(let[n,r]of Object.entries(e.cfg.services)){let o={};for(let i of r.ports)if(!i.auto)o[i.name]=i.value;t[n]=o}if(e.assignedPorts)t[e.service]=e.assignedPorts;return t}function bi(e,t,n){if(!t||Object.keys(e).length===0)return e;return zl(e,t,n)}function Wk(e){let t=e.cfg?.environment.sources??[];if(t.length>0&&!t.includes("keychain"))return{};let n={},r=new Set([...Object.keys(e.serviceCfg.environment.defaults),...Object.keys(e.serviceCfg.environment.vars),...e.serviceCfg.environment.required]);for(let o of r){let i=nd(Wt(),"env",o);if(!Zg(i))continue;try{n[o]=em(i,"utf8").replace(/\n$/,"")}catch(s){throw fe(ee,`unable to read keychain env ${o}`,s)}}return n}async function Gk(e,t){if(!(e.cfg?.environment.sources??[]).includes("secret_manager"))return{};let r=e.cfg?.environment.secrets??{},o={};for(let[i,s]of Object.entries(r)){if(!Fk.test(s))throw L(ee,`environment.secrets.${i} is not a Secret Manager resource`);if(!t)throw L(ee,"environment source secret_manager is not configured \u2014 set credentials or remove it from environment.sources");o[i]=await t(s)}return o}function Qg(e,t){let n={},r=[".env",".env.development",".env.local"];if(t!=="")r.push(`.env.${t}`);for(let o of r){let i=nd(e,o);if(!Zg(i))continue;try{let s=Ik(em(i));Object.assign(n,s)}catch(s){throw fe(ee,`unable to read ${i}`,s)}}return n}function co(){let e={};for(let[t,n]of Object.entries(process.env))if(n!==void 0)e[t]=n;return e}function ta(e,t,n,r,o){let i={DEVCTL_SERVICE_NAME:e,DEVCTL_ENVIRONMENT:o,SERVICE_HOST:t};if(r!=="")i.DEVCTL_PROXY_URL=r;if(n.http!==void 0)i.SERVICE_PORT=String(n.http);else{let s=Object.values(n)[0];if(s!==void 0)i.SERVICE_PORT=String(s)}for(let[s,a]of Object.entries(n))i[`${s.toUpperCase()}_PORT`]=String(a);return i}function Si(e){return{...e}}var wi,Fk,Nk;var rd=D(()=>{st();Pe();ze();wi=["process","profile","dotenv","generated","keychain","secret_manager","defaults","vars","runtime"],Fk=/^projects\/[^/]+\/secrets\/[^/]+(?:\/versions\/[^/]+)?$/,Nk=["process","defaults","vars","runtime"]});function At(){return"devctl 0.2.2"}var pt="0.2.2",ki=1;import{createConnection as Uk}from"net";var{spawn:Kk}=globalThis.Bun;function Yk(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 ${ki}`}function Xk(e){if(e.compatible&&e.daemonVersion!==void 0&&e.daemonVersion!==pt)return`attached daemon is devctl ${e.daemonVersion}; this client is ${pt} (run \`devctl down\` then start again to update it)`;return}function od(e,t){if(!e.compat.compatible&&!zk.has(t))throw ke(Je,Yk(e.compat),"run `devctl down` to stop it, then start again")}class rm{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 n=this.buf.split(`
6
6
  `);this.buf=n.pop()??"";for(let r of n){if(r.trim()==="")continue;this.onLine(r)}}),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 r of this.listeners)r(t.event);return}let n=t.id?this.pending.get(t.id):void 0;if(!n)return;if(this.pending.delete(t.id??""),clearTimeout(n.timer),t.error){n.reject(Mf({error:t.error,kind:t.kind,hint:t.hint,service:t.service}));return}n.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,n=Jk){this.nextID+=1;let r=String(this.nextID);return new Promise((o,i)=>{let s=setTimeout(()=>{this.pending.delete(r),i(Error(`${e} timed out after ${n}ms`))},n);this.pending.set(r,{resolve:o,reject:i,timer:s}),this.socket.write(JSON.stringify({id:r,method:e,params:t})+`
7
7
  `)})}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 om(e,t){let n=Is(e),r=Date.now()+t;return new Promise((o,i)=>{let s=()=>{let a=Uk(n);a.once("connect",()=>{let c=new rm(a);Qk(c).finally(()=>o(c))}),a.once("error",(c)=>{if(a.destroy(),Date.now()>=r){i(ke(Je,"supervisor is not running","run `devctl start` or `devctl attach` after starting services"));return}setTimeout(s,qk)})};s()})}async function Qk(e){let t;try{t=await e.call("ping",null,nm)}catch{return}let n=Zk(t)?t:{};if(typeof n.session==="string")e.session=n.session;let r=typeof n.protocol==="number"?n.protocol:void 0,o=typeof n.version==="string"?n.version:void 0;if(r===void 0){e.compat={compatible:!1,legacy:!0,daemonVersion:o};return}e.compat={compatible:r===ki,legacy:!1,daemonVersion:o,daemonProtocol:r}}function Zk(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}async function vr(e){try{return await om(e,Vk)}catch{return}}function eC(e,t,n,r){return n?[e,...r]:[e,t,...r]}async function im(e,t){let n=await vr(e);if(n)return n;Ff(e);let r=dr(e),o=eC(process.execPath,process.argv[1]??"",Bun.isStandaloneExecutable===!0,["--config",t,"_supervisor","--repo",e]);Kk({cmd:o,env:process.env,stdout:"ignore",stderr:Bun.file(r),stdin:"ignore",detached:!0}).unref();try{return await om(e,nm)}catch{throw ke(Je,"supervisor failed to start",`see ${r} for details`)}}class na{cfg;client;previousPersisted;constructor(e){this.cfg=e}async start(e){return await this.call("start",{...e,client_env:co()})}async stop(e){await this.call("stop",{services:e})}async restart(e,t){await this.call("restart",{services:e,cascade:t===!0,client_env:co()})}async runTask(e){return await this.call("run_task",{name:e,client_env:co()},tm)}async execService(e,t,n=!1){return await this.call("exec",{service:e,command:t,print_env:n,client_env:co()},tm)}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 close(e){if(!this.client)return;try{if(e?.shutdownSupervisor===!0&&e.detach!==!0){let t=Math.max(5000,this.cfg.shutdown.grace_seconds*1000+2000);await this.client.call("shutdown",{stop_services:!0},t)}}finally{this.client.close()}}async call(e,t,n){if(!this.client)throw fe(Je,"supervisor is not running",Error("no client"));return od(this.client,e),this.client.call(e,t,n)}}function Ci(e){let t=e&&Xk(e.compat);if(t)process.stderr.write(`warning: ${t}
8
8
  `)}async function id(e,t,n=""){let r=Qs(e,t,n);if(!r)throw ke(Je,"no devctl configuration found","run `devctl setup`, create a .devctl/config.yaml in the repository root, or pass --repo");let o=await vr(r.repoRoot);return Ci(o),{repoRoot:r.repoRoot,client:o}}async function kt(e,t,n,r){let o=r?.allowMissingConfig===!0?yi(e,t):ut(e,t),i=new na(o);if(!n)return i.client=await vr(o.repoRoot),Ci(i.client),i;return i.client=await im(o.repoRoot,o.configPath),Ci(i.client),i}async function sm(e){let t=new na(pr());return t.client=e,Ci(t.client),t.cfg=await t.configSnapshot(),t}async function am(e,t){let n=Qs(e,"",t),r=n?await vr(n.repoRoot):void 0;if(!r)throw ke(Je,"supervisor is not running","run `devctl start` before `devctl attach`");return sm(r)}async function cm(e,t){let n=Qs(e,"",t),r=n?await vr(n.repoRoot):void 0;if(r)return sm(r);let o=ut(e,t),i=new na(o),s=ur(o.repoRoot);if(i.client=await im(o.repoRoot,o.configPath),Ci(i.client),i.cfg=await i.configSnapshot(),s&&s.processes.length>0)i.previousPersisted=s;return i}var qk=50,nm=8000,Vk=200,Jk=30000,tm=86400000,zk;var sd=D(()=>{st();Xg();rd();Pe();ze();zk=new Set(["logs","logs_page","logs_stats"])});function oa(e){if(yn(e))return Nt({kind:yr,serviceAccount:e.service_account});if(Uf(e)&&(e.type!==""||e.mode!==""))return Nt({kind:ra});let t=(e.type||e.mode).toLowerCase();if(t!=="")return Nt({kind:t,providerConfig:e.config??{}});return Nt({kind:lo})}function xr(e){if(e.type.toLowerCase()==="none")return Nt({kind:lo});let t=e.identity.type.toLowerCase(),n=e.identity.service_account||e.service_account;if(t==="service"||t==="service_account"||e.type==="service_account")return Nt({kind:yr,serviceAccount:n});if(e.type.toLowerCase()==="iap"&&t==="")return Nt({kind:lo});if(e.type===""&&t==="")return Nt({kind:lo});return Nt({kind:ra})}function lm(e){return e.kind===ra||e.kind===yr}function uo(e){if(e.tokenKey!=="")return e.tokenKey;if(e.kind===yr)return`sa:${e.serviceAccount}`;if(e.email!=="")return`user:${e.email}`;return"user"}function Nt(e={}){return{kind:lo,email:"",serviceAccount:"",project:"",projectSource:"",adcAvailable:!1,providerConfig:{},tokenKey:"",...e}}function ad(e){let t=new Map,n=(r,o)=>{if(r!=="")t.set(r,(t.get(r)??!1)||o)};for(let r of Object.values(e.services))if(yn(r.identity)&&r.identity.service_account!=="")n(r.identity.service_account,!0);for(let r of e.proxy.routes){let o=r.auth.type.toLowerCase(),i=r.auth.identity.type.toLowerCase(),s=r.auth.identity.service_account||r.auth.service_account;if(i==="service"||i==="service_account"||o==="service_account")n(s,o!=="none")}return[...t.entries()].map(([r,o])=>({email:r,active:o})).sort((r,o)=>r.email.localeCompare(o.email))}function Gn(e){return ad(e).filter((t)=>t.active).map((t)=>t.email)}function dm(e,t,n){let r=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=oa(i.identity);if(yn(i.identity)&&i.identity.service_account===""){r.push({name:o,message:"service account identity is not configured"});continue}if((lm(s)||i.capabilities.some((c)=>c==="google_api"||c==="iap"||c==="service_identity"))&&!n)r.push({name:o,message:"ADC unavailable"})}return r}function _i(e){if(Gn(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(lm(oa(t.identity)))return!0;return t.capabilities.some((n)=>n==="google_api"||n==="iap"||n==="service_identity")})}function cd(){return{name:"user",accepts:(e)=>!yn(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return Nt({kind:lo});return{...await t(),kind:ra}}}}function ld(){return{name:"service_account",accepts:(e)=>yn(e),resolve:async(e,t)=>{if(e.service_account==="")throw L(Os,"service account identity is not configured");return{...await t(),kind:yr,serviceAccount:e.service_account}}}}async function um(e,t,n){if(n)for(let r=n.length-1;r>=0;r-=1){let o=n[r];if(o?.accepts(e))return o.resolve(e,t)}if(yn(e))return ld().resolve(e,t);return cd().resolve(e,t)}var ra="user",yr="service_account",lo="none";var br=D(()=>{st();Pe()});import{existsSync as ia,readFileSync as tC}from"fs";import{homedir as fm}from"os";import{join as dd}from"path";var{spawn:ud}=globalThis.Bun;function fo(){let e=globalThis;if(e.window&&typeof e.window.fetch!=="function")e.window.fetch=fetch}function pd(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS||dd(fm(),".config","gcloud","application_default_credentials.json");if(!ia(e))return"";try{let t=JSON.parse(tC(e,"utf8"));return typeof t.quota_project_id==="string"?t.quota_project_id:""}catch{return""}}async function Lt(e){let t={gcloudInstalled:await fd("gcloud"),adcAvailable:!1,userEmail:"",projectID:"",projectSource:""};if(e!=="")t.projectID=e,t.projectSource="configuration";else{let n=cC("GOOGLE_CLOUD_PROJECT","GCLOUD_PROJECT","GOOGLE_PROJECT");if(n!=="")t.projectID=n,t.projectSource="environment variable";else if(t.gcloudInstalled){let r=await pm("core/project");if(r!=="")t.projectID=r,t.projectSource="gcloud configuration"}}if(Ri())try{await vm(oC(t),rC)}catch(n){t.adcAvailable=!1,t.error=mo(n)}if(t.userEmail===""&&t.gcloudInstalled)t.userEmail=await pm("core/account");return t}async function oC(e){fo();let{GoogleAuth:t}=await import("google-auth-library"),n=new t({scopes:[nC]});await n.getClient(),e.adcAvailable=!0;let r=await n.getProjectId().catch(()=>"");if(e.projectID===""&&r)e.projectID=r,e.projectSource="application default credentials";let o=await iC(n);if(o!=="")e.userEmail=o}async function iC(e){try{let t=await e.getCredentials();if(t.client_email)return t.client_email}catch{}try{let t=await e.getClient(),n=typeof t.credentials?.id_token==="string"?t.credentials.id_token:"";if(n!==""){let r=sC(n);if(r!=="")return r}}catch{return""}return""}function sC(e){let t=e.split(".");if(t.length<2||!t[1])return"";try{let n=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));return typeof n.email==="string"?n.email:""}catch{return""}}async function mm(e){let t=await Lt(e);return Nt({kind:"user",email:t.userEmail,project:t.projectID,projectSource:t.projectSource,adcAvailable:t.adcAvailable})}async function go(){if(await ud({cmd:["gcloud","auth","application-default","login"],stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited!==0)throw ke(to,"application-default login failed","run `gcloud auth application-default login`")}async function sa(){if(await ud({cmd:["gcloud","auth","application-default","revoke","--quiet"],stdout:"ignore",stderr:"pipe"}).exited!==0)throw ke(to,"failed to revoke application-default credentials","run `gcloud auth application-default revoke`")}function mo(e){let t=aC(e).toLowerCase();if(po(t,["unauth","invalid_grant","token has been expired","expired"]))return ke(to,"credential expired or invalid","run `devctl auth login` or `gcloud auth application-default login`");if(po(t,["api has not been used","has not been enabled","access not configured","service_disabled"]))return ke(cr,"required Google API is not enabled","enable the API in the target service account project; devctl will not enable APIs automatically");if(po(t,["permission","forbidden","iam"])){if(po(t,["serviceaccounttoken","iamcredentials","token creator","getaccesstoken"]))return ke(Os,"cannot impersonate service account","ask an administrator to grant roles/iam.serviceAccountTokenCreator on the target service account");return ke(cr,"authorization failure","verify IAM permissions for the current user on this project")}if(t.includes("audience"))return ke(yl,"IAP audience is incorrect","set auth.audience on the proxy route to the IAP OAuth client ID");if(t.includes("iap"))return fe(yl,"IAP authentication failure",e);if(t.includes("project"))return ke(ee,"wrong or missing Google project","set google.project_id in .devctl/config.yaml");if(po(t,["timeout","network","connection refused","no such host"]))return fe(Je,"network problem reaching Google Cloud",e);if(po(t,["adc","default credentials","could not find"]))return ke(to,"application default credentials unavailable","run `gcloud auth application-default login`");return fe(to,"Google authentication failed",e)}function aC(e){let t=[],n=new Set,r=(o,i)=>{if(o===null||o===void 0||i>8||n.has(o))return;if(typeof o==="string"){t.push(o);return}if(typeof o!=="object")return;n.add(o);let s=o;for(let a of["message","reason","status","code","error_description","permission","service"])r(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)=>r(l,i+1));else r(c,i+1)}};return r(e,0),t.length>0?t.join(" "):String(e)}function po(e,t){return t.some((n)=>e.includes(n))}function cC(...e){for(let t of e){let n=process.env[t];if(n)return n}return""}async function fd(e){try{return(await hm(process.platform==="win32"?["where",e]:["which",e],gm)).code===0}catch{return!1}}function Ri(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS;if(e&&ia(e))return!0;if(ia(dd(fm(),".config","gcloud","application_default_credentials.json")))return!0;let t=process.env.APPDATA;return Boolean(t&&ia(dd(t,"gcloud","application_default_credentials.json")))}async function pm(e){try{let t=await hm(["gcloud","config","get-value",e],gm);if(t.code!==0)return"";let n=t.stdout.trim();if(n===""||n==="(unset)")return"";return n}catch{return""}}async function hm(e,t){let n=ud({cmd:e,stdout:"pipe",stderr:"ignore",env:{...process.env,CLOUDSDK_CORE_DISABLE_PROMPTS:"1",CLOUDSDK_CORE_DISABLE_USAGE_REPORTING:"true"}}),r=n.stdout?new Response(n.stdout).text():Promise.resolve("");try{let[o,i]=await vm(Promise.all([r,n.exited]),t);return{code:i??1,stdout:o}}catch{return n.kill(),{code:1,stdout:""}}}function vm(e,t){return new Promise((n,r)=>{let o=setTimeout(()=>r(Error("timeout")),t);e.then((i)=>{clearTimeout(o),n(i)},(i)=>{clearTimeout(o),r(i)})})}var nC="https://www.googleapis.com/auth/cloud-platform",gm=1500,rC=2000;var wr=D(()=>{eo();Pe();br()});var{spawn:gd}=globalThis.Bun;import{createServer as ym}from"net";async function xm(e,t,n={}){let r={};for(let[i,s]of Object.entries(n))for(let a of Object.values(s))r[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=n[i]?.[c.name],d=l&&l>=md?l:c.value;if(l===void 0&&c.auto)d=await dC();if(d<md||d>lC)throw new Oe(ee,`invalid port ${d} on service ${i}`,{service:i});if(r[d]&&r[d]!==i)throw new Oe(ee,`duplicate port ${d} used by ${r[d]} and ${i}`,{service:i});if(l!==d&&!c.auto&&!await ho(d))throw await uC(i,c.name,d);r[d]=i,a[c.name]=d}o[i]=a}return o}function dC(){return new Promise((e,t)=>{let n=ym();n.listen(0,"127.0.0.1",()=>{let r=n.address();n.close(()=>{if(r&&typeof r==="object")e(r.port);else t(fe(ee,"unable to allocate dynamic port",Error("no address")))})}),n.on("error",(r)=>t(fe(ee,"unable to allocate dynamic port",r)))})}async function aa(e){let t=e.ports.filter((r)=>!r.auto&&r.value>=md);if(t.length===0)return;let n={};for(let r of t){if(await ho(r.value))return;n[r.name]=r.value}return n}function ho(e){return new Promise((t)=>{let n=ym();n.listen(e,"127.0.0.1",()=>{n.close(()=>t(!0))}),n.on("error",()=>t(!1))})}async function uC(e,t,n){let r=await vo(n);return pC(e,t,n,r)}function pC(e,t,n,r){let o=t===""?`${e} port`:`${e} ports.${t}`;if(r&&r.pid===process.pid)return ke(Ke,`${e} blocked: this TUI already holds port ${n}`,`Stop the proxy from the Proxy screen, or change ${o} in .devctl.`,e);if(r)return ke(Ke,`${e} blocked: ${r.command} (pid ${r.pid}) is using port ${n}`,`Open Doctor and press enter to free port ${n}, or change ${o} in .devctl.`,e);return ke(Ke,`${e} blocked: port ${n} is already in use`,`Something is listening on ${n}. Open Doctor to free it, or change ${o} in .devctl.`,e)}function fC(e,t){let n=e.split(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amr-m-abdelgawad/devctl",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Local development orchestrator",
5
5
  "license": "MIT",
6
6
  "type": "module",