infra-kit 0.3.11 → 0.3.13

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.
@@ -1,2 +1,2 @@
1
- import{f as C,i as f,j as g}from"./chunk-PZ2AWWGK.js";import{A as b,B as y,D as k,N as x,V as v,z as u}from"./chunk-M46KGMQ6.js";import{e as w}from"./chunk-CHETBZ6M.js";import"./chunk-X46DP23W.js";import O from"@inquirer/checkbox";import _ from"@inquirer/confirm";import j,{Separator as I}from"@inquirer/select";import{loadDev as K}from"@slip-stream-kit/config/internal";import U from"node:path";import m from"node:process";var W=e=>e.split("/")[0],A=e=>new Map(e.apps.map(r=>[r.name,r])),D=e=>e.localCapable&&e.cloudCapable,N=(e,r,t)=>D(r)?t[`${e} ${r.path}`]??(r.cloudCapable?"cloud":"local"):r.localCapable?"local":"cloud",$=(e,r,t)=>{let a=new Set,o=new Map;for(let n of e){let s=r.get(W(n));for(let c of s?.backends??[]){o.set(c.packageName,c.ownerApp);for(let l of c.routes)N(n,l,t)==="local"&&a.add(c.packageName)}}return{localPkgs:a,ownerByPkg:o}},B=(e,r)=>{let t={},a=!1;for(let o of e?.backends??[]){let n=r.has(o.packageName);for(let s of o.routes){let c=s.localCapable&&n?"local":"cloud";t[s.path]=c,c==="cloud"&&(a=!0)}}return{overrides:t,anyCloud:a}},z=(e,r)=>{let t=A(r),a=e.targets.filter(p=>p.endsWith("/ui")),o=e.targets.filter(p=>p.endsWith("/api")),{localPkgs:n,ownerByPkg:s}=$(a,t,e.sources),c={},l=!1;for(let p of a){let{overrides:d,anyCloud:i}=B(t.get(W(p)),n);i&&(l=!0),c[p]=Object.keys(d).length>0?{proxy:d}:{}}for(let p of o)c[p]??={};for(let p of n){let d=s.get(p);d!=null&&(c[`${d}/api`]??={})}return{presetDef:{apps:c,cmux:e.cmux},anyCloudRoute:l,targetKeys:Object.keys(c).sort()}},M=(e,r)=>{let t=[`--target=${e.targetKeys.join(",")}`];return r.watch&&t.push("--watch"),r.cmux&&t.push("--cmux"),{flags:t.join(" ")}};var h={output:m.stderr,clearPromptOnDone:!0},q={select:e=>g(r=>j({message:e.message,choices:e.choices,default:e.default},r),h),checkbox:e=>g(r=>O({message:e.message,choices:e.choices},r),h),confirm:e=>g(r=>_({message:e.message,default:e.default},r),h)},E=(e,r)=>{let t=new Map;for(let[a,o]of Object.entries(e)){let n={path:a,localCapable:o.from.includes("local"),cloudCapable:o.from.includes("cloud"),default:o.default},s=t.get(o.packageName);s?(s.routes.push(n),s.localCapable=s.localCapable||n.localCapable,s.cloudCapable=s.cloudCapable||n.cloudCapable):t.set(o.packageName,{packageName:o.packageName,routes:[n],localCapable:n.localCapable,cloudCapable:n.cloudCapable,ownerApp:r.get(o.packageName)})}return[...t.values()]},T=async(e,r,t)=>{try{let a=(await K(e))?.proxy?.routes;return a?E(a,t):[]}catch(a){return u.warn(`\u26A0\uFE0F Skipping ${r}: its dev config failed to load \u2014 ${a instanceof Error?a.message:String(a)}`),[]}},V=async e=>{let r=y(e),t=k(e),a=await v(),o=new Map(r.map(i=>[i.packageName,i.name])),n=new Map(r.map(i=>[i.name,i.packageName])),s=new Set(t.map(i=>i.name)),c=new Set(r.map(i=>i.name)),l=[...new Set([...c,...s])].sort(),p=await Promise.all(l.map(async i=>{let P=s.has(i);return{name:i,hasApi:c.has(i),hasUi:P,apiPackage:n.get(i),backends:P?await T(U.join(e,"apps",i,"ui"),i,o):[]}})),d=await C();return{apps:p,presets:Object.keys(a.devServersPresets??{}),environments:Object.keys(d?.envs??{}).sort()}},F=e=>{let r=[];for(let t of e.apps){if(t.hasUi){let a=t.backends.flatMap(n=>n.routes.map(s=>s.path)),o=a.length>0?`frontend \u2014 proxies ${[...a].sort().join(", ")}`:"frontend";r.push({name:`${t.name}/ui`,value:`${t.name}/ui`,description:o})}t.hasApi&&!t.hasUi&&r.push({name:`${t.name}/api`,value:`${t.name}/api`,description:"backend"})}return r},L=e=>{let r={api:e.apps.filter(o=>o.hasApi).map(o=>o.name),ui:e.apps.filter(o=>o.hasUi).map(o=>o.name)},t={},a=new Map;for(let o of e.apps){o.apiPackage!=null&&(t[o.name]=o.apiPackage);for(let n of o.backends)for(let s of n.routes)a.set(`${o.name} ${s.path}`,n.packageName)}return{discovered:r,apiPkgByApp:t,routePkg:(o,n)=>a.get(`${o} ${n}`)}},X=(e,r)=>x({__wizard__:e},L(r)).map(t=>t.message),Z="INFRA_KIT_DEV_WIZARD_CMUX",H=()=>m.env[Z]==="1",G=async(e,r,t)=>{let a=new Map(r.apps.map(n=>[n.name,n])),o={};for(let n of t){let s=a.get(n.split("/")[0]);for(let c of s?.backends??[])for(let l of c.routes){if(!l.localCapable||!l.cloudCapable)continue;let p=await e.select({message:`\u{1F500} ${n} ${l.path} \u2192 local or cloud?`,choices:[{name:"local (this machine)",value:"local"},{name:"cloud",value:"cloud"}],default:l.default??"local"});o[`${n} ${l.path}`]=p}}return o},J=(e,r,t)=>e.apps.find(a=>a.name===r)?.backends.flatMap(a=>a.routes).find(a=>a.path===t),Q=(e,r)=>{let t=[];for(let[a,o]of Object.entries(e.presetDef.apps??{})){if(!a.endsWith("/ui")||!o.proxy)continue;let n=a.split("/")[0];for(let[s,c]of Object.entries(o.proxy)){let l=J(r,n,s),p=l&&l.localCapable&&l.cloudCapable?"":" (only option)";t.push(` ${a} ${s} \u2192 ${c}${p}`)}}if(t.length!==0){u.info("Resolved routes:");for(let a of[...t].sort())u.info(a)}},R=async(e,r)=>{if(r.apps.length===0)return u.warn("No apps discovered to run."),null;let t=await e.checkbox({message:"\u{1F4E6} Which packages?",choices:F(r)});if(t.length===0)return u.warn("No packages selected."),null;let a=t.filter(i=>i.endsWith("/ui")),o=await G(e,r,a),n=await e.confirm({message:"\u{1F440} Rebuild & restart on save (watch)?",default:!1}),s=H()?await e.confirm({message:"\u{1F9E9} Run each app in its own cmux pane?",default:!1}):!1,c={targets:t,sources:o,watch:n,cmux:s},l=z(c,r);if(l.anyCloudRoute){if(r.environments.length===0)return u.warn("\u2601\uFE0F This selection routes to a cloud environment, but no environment token is loaded."),u.warn(" Run `infra-kit env-load` to add one, then re-run the wizard."),null;c.env=await e.select({message:"\u2601\uFE0F Point cloud routes at which environment?",choices:r.environments.map(i=>({name:i,value:i}))}),m.env[w]=c.env}let p=X(l.presetDef,r);if(p.length>0){u.warn("\u26A0\uFE0F Proxy audit found issues with this selection:");for(let i of p)u.warn(` \u2022 ${i}`);return null}Q(l,r),Y(l,c);let d=l.targetKeys.filter(i=>i.endsWith("/api")).map(i=>i.split("/")[0]);return s&&d.length>0?{include:d,presetDef:l.presetDef,watch:n,cmux:!0}:(s&&u.info("\u2139\uFE0F cmux needs at least one local backend (panes are backend-only) \u2014 running in-process instead."),{presetDef:l.presetDef,watch:n,cmux:!1})},Y=(e,r)=>{f.setInteractive();let t=M(e,r);f.addOption(t.flags,!0),f.print(),u.info("\u2139\uFE0F Prefer a short name? Save this selection as a devPreset and run `infra-kit dev <name>`.")},ee=async(e,r)=>{let t=await e.confirm({message:"\u{1F440} Rebuild & restart on save (watch)?",default:!1});return f.setInteractive(),f.addOption(r,!0),t&&f.addOption("--watch",!0),f.print(),{preset:r,watch:t,cmux:!1}},S=" manual",re=async(e,r)=>{if(r.presets.length===0)return R(e,r);let t=await e.select({message:"\u{1F680} Start from a preset, or configure manually?",choices:[...r.presets.map(a=>({name:a,value:a})),new I(" "),{name:"Manual (custom)\u2026",value:S}]});return t===S?R(e,r):ee(e,t)},be=async(e=q,r=b(m.cwd()))=>re(e,await V(r));export{S as MANUAL_CHOICE,Z as WIZARD_CMUX_VAR,X as auditManualPlan,q as defaultPrompts,V as gatherWizardModel,T as loadBackends,be as runDevWizard,re as runWizardFlow};
2
- //# sourceMappingURL=dev-wizard-run-S5HEFLYE.js.map
1
+ import{f as C,i as f,j as g}from"./chunk-QLAQ7F4M.js";import{B as u,C as b,D as y,F as k,P as x,X as v}from"./chunk-SSBYKOHY.js";import{e as w}from"./chunk-CHETBZ6M.js";import"./chunk-X46DP23W.js";import O from"@inquirer/checkbox";import _ from"@inquirer/confirm";import j,{Separator as I}from"@inquirer/select";import{loadDev as K}from"@slip-stream-kit/config/internal";import U from"node:path";import m from"node:process";var W=e=>e.split("/")[0],A=e=>new Map(e.apps.map(r=>[r.name,r])),D=e=>e.localCapable&&e.cloudCapable,N=(e,r,t)=>D(r)?t[`${e} ${r.path}`]??(r.cloudCapable?"cloud":"local"):r.localCapable?"local":"cloud",$=(e,r,t)=>{let a=new Set,o=new Map;for(let n of e){let s=r.get(W(n));for(let c of s?.backends??[]){o.set(c.packageName,c.ownerApp);for(let l of c.routes)N(n,l,t)==="local"&&a.add(c.packageName)}}return{localPkgs:a,ownerByPkg:o}},B=(e,r)=>{let t={},a=!1;for(let o of e?.backends??[]){let n=r.has(o.packageName);for(let s of o.routes){let c=s.localCapable&&n?"local":"cloud";t[s.path]=c,c==="cloud"&&(a=!0)}}return{overrides:t,anyCloud:a}},z=(e,r)=>{let t=A(r),a=e.targets.filter(p=>p.endsWith("/ui")),o=e.targets.filter(p=>p.endsWith("/api")),{localPkgs:n,ownerByPkg:s}=$(a,t,e.sources),c={},l=!1;for(let p of a){let{overrides:d,anyCloud:i}=B(t.get(W(p)),n);i&&(l=!0),c[p]=Object.keys(d).length>0?{proxy:d}:{}}for(let p of o)c[p]??={};for(let p of n){let d=s.get(p);d!=null&&(c[`${d}/api`]??={})}return{presetDef:{apps:c,cmux:e.cmux},anyCloudRoute:l,targetKeys:Object.keys(c).sort()}},M=(e,r)=>{let t=[`--target=${e.targetKeys.join(",")}`];return r.watch&&t.push("--watch"),r.cmux&&t.push("--cmux"),{flags:t.join(" ")}};var h={output:m.stderr,clearPromptOnDone:!0},q={select:e=>g(r=>j({message:e.message,choices:e.choices,default:e.default},r),h),checkbox:e=>g(r=>O({message:e.message,choices:e.choices},r),h),confirm:e=>g(r=>_({message:e.message,default:e.default},r),h)},E=(e,r)=>{let t=new Map;for(let[a,o]of Object.entries(e)){let n={path:a,localCapable:o.from.includes("local"),cloudCapable:o.from.includes("cloud"),default:o.default},s=t.get(o.packageName);s?(s.routes.push(n),s.localCapable=s.localCapable||n.localCapable,s.cloudCapable=s.cloudCapable||n.cloudCapable):t.set(o.packageName,{packageName:o.packageName,routes:[n],localCapable:n.localCapable,cloudCapable:n.cloudCapable,ownerApp:r.get(o.packageName)})}return[...t.values()]},T=async(e,r,t)=>{try{let a=(await K(e))?.proxy?.routes;return a?E(a,t):[]}catch(a){return u.warn(`\u26A0\uFE0F Skipping ${r}: its dev config failed to load \u2014 ${a instanceof Error?a.message:String(a)}`),[]}},V=async e=>{let r=y(e),t=k(e),a=await v(),o=new Map(r.map(i=>[i.packageName,i.name])),n=new Map(r.map(i=>[i.name,i.packageName])),s=new Set(t.map(i=>i.name)),c=new Set(r.map(i=>i.name)),l=[...new Set([...c,...s])].sort(),p=await Promise.all(l.map(async i=>{let P=s.has(i);return{name:i,hasApi:c.has(i),hasUi:P,apiPackage:n.get(i),backends:P?await T(U.join(e,"apps",i,"ui"),i,o):[]}})),d=await C();return{apps:p,presets:Object.keys(a.devServersPresets??{}),environments:Object.keys(d?.envs??{}).sort()}},F=e=>{let r=[];for(let t of e.apps){if(t.hasUi){let a=t.backends.flatMap(n=>n.routes.map(s=>s.path)),o=a.length>0?`frontend \u2014 proxies ${[...a].sort().join(", ")}`:"frontend";r.push({name:`${t.name}/ui`,value:`${t.name}/ui`,description:o})}t.hasApi&&!t.hasUi&&r.push({name:`${t.name}/api`,value:`${t.name}/api`,description:"backend"})}return r},L=e=>{let r={api:e.apps.filter(o=>o.hasApi).map(o=>o.name),ui:e.apps.filter(o=>o.hasUi).map(o=>o.name)},t={},a=new Map;for(let o of e.apps){o.apiPackage!=null&&(t[o.name]=o.apiPackage);for(let n of o.backends)for(let s of n.routes)a.set(`${o.name} ${s.path}`,n.packageName)}return{discovered:r,apiPkgByApp:t,routePkg:(o,n)=>a.get(`${o} ${n}`)}},X=(e,r)=>x({__wizard__:e},L(r)).map(t=>t.message),Z="INFRA_KIT_DEV_WIZARD_CMUX",H=()=>m.env[Z]==="1",G=async(e,r,t)=>{let a=new Map(r.apps.map(n=>[n.name,n])),o={};for(let n of t){let s=a.get(n.split("/")[0]);for(let c of s?.backends??[])for(let l of c.routes){if(!l.localCapable||!l.cloudCapable)continue;let p=await e.select({message:`\u{1F500} ${n} ${l.path} \u2192 local or cloud?`,choices:[{name:"local (this machine)",value:"local"},{name:"cloud",value:"cloud"}],default:l.default??"local"});o[`${n} ${l.path}`]=p}}return o},J=(e,r,t)=>e.apps.find(a=>a.name===r)?.backends.flatMap(a=>a.routes).find(a=>a.path===t),Q=(e,r)=>{let t=[];for(let[a,o]of Object.entries(e.presetDef.apps??{})){if(!a.endsWith("/ui")||!o.proxy)continue;let n=a.split("/")[0];for(let[s,c]of Object.entries(o.proxy)){let l=J(r,n,s),p=l&&l.localCapable&&l.cloudCapable?"":" (only option)";t.push(` ${a} ${s} \u2192 ${c}${p}`)}}if(t.length!==0){u.info("Resolved routes:");for(let a of[...t].sort())u.info(a)}},R=async(e,r)=>{if(r.apps.length===0)return u.warn("No apps discovered to run."),null;let t=await e.checkbox({message:"\u{1F4E6} Which packages?",choices:F(r)});if(t.length===0)return u.warn("No packages selected."),null;let a=t.filter(i=>i.endsWith("/ui")),o=await G(e,r,a),n=await e.confirm({message:"\u{1F440} Rebuild & restart on save (watch)?",default:!1}),s=H()?await e.confirm({message:"\u{1F9E9} Run each app in its own cmux pane?",default:!1}):!1,c={targets:t,sources:o,watch:n,cmux:s},l=z(c,r);if(l.anyCloudRoute){if(r.environments.length===0)return u.warn("\u2601\uFE0F This selection routes to a cloud environment, but no environment token is loaded."),u.warn(" Run `infra-kit env-load` to add one, then re-run the wizard."),null;c.env=await e.select({message:"\u2601\uFE0F Point cloud routes at which environment?",choices:r.environments.map(i=>({name:i,value:i}))}),m.env[w]=c.env}let p=X(l.presetDef,r);if(p.length>0){u.warn("\u26A0\uFE0F Proxy audit found issues with this selection:");for(let i of p)u.warn(` \u2022 ${i}`);return null}Q(l,r),Y(l,c);let d=l.targetKeys.filter(i=>i.endsWith("/api")).map(i=>i.split("/")[0]);return s&&d.length>0?{include:d,presetDef:l.presetDef,watch:n,cmux:!0}:(s&&u.info("\u2139\uFE0F cmux needs at least one local backend (panes are backend-only) \u2014 running in-process instead."),{presetDef:l.presetDef,watch:n,cmux:!1})},Y=(e,r)=>{f.setInteractive();let t=M(e,r);f.addOption(t.flags,!0),f.print(),u.info("\u2139\uFE0F Prefer a short name? Save this selection as a devPreset and run `infra-kit dev <name>`.")},ee=async(e,r)=>{let t=await e.confirm({message:"\u{1F440} Rebuild & restart on save (watch)?",default:!1});return f.setInteractive(),f.addOption(r,!0),t&&f.addOption("--watch",!0),f.print(),{preset:r,watch:t,cmux:!1}},S=" manual",re=async(e,r)=>{if(r.presets.length===0)return R(e,r);let t=await e.select({message:"\u{1F680} Start from a preset, or configure manually?",choices:[...r.presets.map(a=>({name:a,value:a})),new I(" "),{name:"Manual (custom)\u2026",value:S}]});return t===S?R(e,r):ee(e,t)},be=async(e=q,r=b(m.cwd()))=>re(e,await V(r));export{S as MANUAL_CHOICE,Z as WIZARD_CMUX_VAR,X as auditManualPlan,q as defaultPrompts,V as gatherWizardModel,T as loadBackends,be as runDevWizard,re as runWizardFlow};
2
+ //# sourceMappingURL=dev-wizard-run-XM5L23UP.js.map
package/dist/mcp.js CHANGED
@@ -1,2 +1,2 @@
1
- import{Z as k,a as h,aa as R,q as T}from"./chunk-VNHAMXNA.js";import"./chunk-JXLXSX7Q.js";import{a as w}from"./chunk-KPQTJQ42.js";import"./chunk-LKKKDB6V.js";import{i as C}from"./chunk-PZ2AWWGK.js";import{V as S,c as v,x as l,y,z as m}from"./chunk-M46KGMQ6.js";import"./chunk-CHETBZ6M.js";import"./chunk-X46DP23W.js";import{StdioServerTransport as q}from"@modelcontextprotocol/sdk/server/stdio.js";import O from"node:process";import a from"node:process";var E=e=>{a.on("SIGINT",()=>{e.info({msg:"Received SIGINT. Shutting down..."}),a.exit(0)}),a.on("SIGTERM",()=>{e.info({msg:"Received SIGTERM. Shutting down..."}),a.exit(0)}),a.on("uncaughtException",r=>{e.fatal({err:r,msg:"Uncaught Exception"}),e.error(`Uncaught Exception! Check ${l}. Shutting down...`),e.flush(),a.exit(1)}),a.on("unhandledRejection",(r,t)=>{e.fatal({reason:r,promise:t,msg:"Unhandled Rejection"}),e.error(`Unhandled Rejection! Check ${l}. Shutting down...`),e.flush(),a.exit(1)})};import{McpServer as L}from"@modelcontextprotocol/sdk/server/mcp.js";var j=async e=>{};import u from"node:fs";import p from"node:path";import _ from"node:process";var H=p.join(".infra-kit","dev-context"),F=".infra-kit",D=(e,r)=>{let t=p.resolve(e);for(;;){let n=p.join(t,r);if(u.existsSync(n))return n;let o=p.dirname(t);if(o===t)return;t=o}},P=e=>{let r=D(e,H);if(r)return r;let t=D(e,F);return t?p.join(t,"dev-context"):void 0},d=e=>typeof e=="number"&&Number.isFinite(e)?e:0,f=e=>typeof e=="string"?e:"",U=(e,r,t)=>{let n=p.join(e,r);try{let o=u.statSync(n),i=JSON.parse(u.readFileSync(n,"utf-8"));if(typeof i!="object"||i===null)return null;let s=i;return{app:r.replace(/\.json$/,""),package:f(s.package),port:d(s.port),alias:f(s.alias),origin:f(s.origin),release:f(s.release),pid:d(s.pid),writtenAt:d(s.writtenAt),mtimeMs:Math.round(o.mtimeMs),ageMs:Math.max(0,Math.round(t-o.mtimeMs))}}catch{return null}},M=(e=_.cwd())=>{let r=Date.now(),t=P(e);if(!t||!u.existsSync(t))return{session:"none",fragmentDir:t,readAt:r,apps:[]};let n;try{n=u.readdirSync(t).filter(i=>i.endsWith(".json"))}catch{return{session:"none",fragmentDir:t,readAt:r,apps:[]}}let o=n.map(i=>U(t,i,r)).filter(i=>i!==null).sort((i,s)=>i.app.localeCompare(s.app));return{session:o.length>0?"active":"none",fragmentDir:t,readAt:r,apps:o}};var $="infra-kit://config",z="infra-kit://dev-context",G={loadConfig:S,readDevContext:()=>M()},g=(e,r)=>({contents:[{uri:e,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}),b=async(e,r=G)=>{e.registerResource("infra-kit-config",$,{title:"infra-kit config",description:"The merged infra-kit.json configuration (all override layers applied). Read-only.",mimeType:"application/json"},async t=>{try{return g(t.toString(),await r.loadConfig())}catch(n){return g(t.toString(),{error:n instanceof Error?n.message:String(n)})}}),e.registerResource("infra-kit-dev-context",z,{title:"infra-kit dev context",description:"What `infra-kit dev` last wrote: backends currently up, their ports/origins, and fragment freshness. Read-only and advisory (no liveness probe). No active session resolves to an empty payload.",mimeType:"application/json"},t=>g(t.toString(),r.readDevContext()))};var N=e=>typeof e=="object"&&e!==null&&e.confirm===!0,J=(e,r)=>{let t=typeof r=="object"&&r!==null?Object.fromEntries(Object.entries(r).filter(([o])=>o!=="confirm")):{},n={status:"confirmation_required",tool:e,resolvedArgs:t,message:`${e} mutates external state and is gated. It was NOT executed. Re-call ${e} with the same arguments plus "confirm": true to execute.`};return{content:h(JSON.stringify(n,null,2)),structuredContent:n,isError:!0}},x=({toolName:e,handler:r,requiresHumanConfirm:t})=>async n=>{m.info({msg:`Tool execution started: ${e}`,params:n});try{if(await T(),C.reset(),t===!0&&!N(n))return m.info({msg:`Tool execution gated (awaiting confirm): ${e}`}),J(e,n);let o=await r({...n,confirmedCommand:!0});return m.info({msg:`Tool execution successful: ${e}`}),o}catch(o){throw m.error({err:o,params:n,msg:`Tool execution failed: ${e}`}),o}};var A=async e=>{for(let r of k())e.registerTool(r.name,{description:r.description,inputSchema:r.inputSchema,outputSchema:r.outputSchema},x({toolName:r.name,handler:r.handler,requiresHumanConfirm:r.requiresHumanConfirm}))};async function I(){v.enabled=!0;let e=new L({name:"infra-kit",version:w.version},{capabilities:{resources:{listChanged:!0},tools:{},prompts:{}}});return await j(e),await b(e),await A(e),e}R();var c=y(),K=async()=>{let e;try{e=await I(),c.info("MCP Server instance created")}catch(r){c.error({err:r,msg:"Failed to create MCP server"}),c.error("Fatal error during server creation."),O.exit(1)}try{let r=new q;await e.connect(r),c.info({msg:"Server connected to transport. Ready."})}catch(r){c.error({err:r,msg:"Failed to initialize server"}),c.error("Fatal error during server transport init."),O.exit(1)}};E(c);K();
1
+ import{a as d,aa as h,d as p,da as S,r as y}from"./chunk-LMAO6FEM.js";import"./chunk-SHFPI4QA.js";import{a as v}from"./chunk-DPCWCXJD.js";import"./chunk-LKKKDB6V.js";import{i as x}from"./chunk-QLAQ7F4M.js";import{A as l,B as c,X as g,c as u,z as a}from"./chunk-SSBYKOHY.js";import"./chunk-CHETBZ6M.js";import"./chunk-X46DP23W.js";import{StdioServerTransport as O}from"@modelcontextprotocol/sdk/server/stdio.js";import E from"node:process";import n from"node:process";var C=e=>{n.on("SIGINT",()=>{e.info({msg:"Received SIGINT. Shutting down..."}),n.exit(0)}),n.on("SIGTERM",()=>{e.info({msg:"Received SIGTERM. Shutting down..."}),n.exit(0)}),n.on("uncaughtException",r=>{e.fatal({err:r,msg:"Uncaught Exception"}),e.error(`Uncaught Exception! Check ${a}. Shutting down...`),e.flush(),n.exit(1)}),n.on("unhandledRejection",(r,o)=>{e.fatal({reason:r,promise:o,msg:"Unhandled Rejection"}),e.error(`Unhandled Rejection! Check ${a}. Shutting down...`),e.flush(),n.exit(1)})};import{McpServer as H}from"@modelcontextprotocol/sdk/server/mcp.js";var T=async e=>{};var j="infra-kit://config",M="infra-kit://dev-context",b={loadConfig:g,readDevContext:()=>p()},m=(e,r)=>({contents:[{uri:e,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}),R=async(e,r=b)=>{e.registerResource("infra-kit-config",j,{title:"infra-kit config",description:"The merged infra-kit.json configuration (all override layers applied). Read-only.",mimeType:"application/json"},async o=>{try{return m(o.toString(),await r.loadConfig())}catch(t){return m(o.toString(),{error:t instanceof Error?t.message:String(t)})}}),e.registerResource("infra-kit-dev-context",M,{title:"infra-kit dev context",description:"What `infra-kit dev` last wrote: backends currently up, their ports/origins, and fragment freshness. Read-only and advisory (no liveness probe). No active session resolves to an empty payload.",mimeType:"application/json"},o=>m(o.toString(),r.readDevContext()))};var D=e=>typeof e=="object"&&e!==null&&e.confirm===!0,I=(e,r)=>{let o=typeof r=="object"&&r!==null?Object.fromEntries(Object.entries(r).filter(([s])=>s!=="confirm")):{},t={status:"confirmation_required",tool:e,resolvedArgs:o,message:`${e} mutates external state and is gated. It was NOT executed. Re-call ${e} with the same arguments plus "confirm": true to execute.`};return{content:d(JSON.stringify(t,null,2)),structuredContent:t,isError:!0}},f=({toolName:e,handler:r,requiresHumanConfirm:o})=>async t=>{c.info({msg:`Tool execution started: ${e}`,params:t});try{if(await y(),x.reset(),o===!0&&!D(t))return c.info({msg:`Tool execution gated (awaiting confirm): ${e}`}),I(e,t);let s=await r({...t,confirmedCommand:!0});return c.info({msg:`Tool execution successful: ${e}`}),s}catch(s){throw c.error({err:s,params:t,msg:`Tool execution failed: ${e}`}),s}};var w=async e=>{for(let r of h())e.registerTool(r.name,{description:r.description,inputSchema:r.inputSchema,outputSchema:r.outputSchema},f({toolName:r.name,handler:r.handler,requiresHumanConfirm:r.requiresHumanConfirm}))};async function k(){u.enabled=!0;let e=new H({name:"infra-kit",version:v.version},{capabilities:{resources:{listChanged:!0},tools:{},prompts:{}}});return await T(e),await R(e),await w(e),e}S();var i=l(),P=async()=>{let e;try{e=await k(),i.info("MCP Server instance created")}catch(r){i.error({err:r,msg:"Failed to create MCP server"}),i.error("Fatal error during server creation."),E.exit(1)}try{let r=new O;await e.connect(r),i.info({msg:"Server connected to transport. Ready."})}catch(r){i.error({err:r,msg:"Failed to initialize server"}),i.error("Fatal error during server transport init."),E.exit(1)}};C(i);P();
2
2
  //# sourceMappingURL=mcp.js.map
package/dist/mcp.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/entry/mcp.ts", "../src/lib/error-handlers/index.ts", "../src/mcp/server.ts", "../src/mcp/prompts/index.ts", "../src/mcp/resources/dev-context.ts", "../src/mcp/resources/index.ts", "../src/lib/tool-handler/tool-handler.ts", "../src/mcp/tools/index.ts"],
4
- "sourcesContent": ["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport process from 'node:process'\n\nimport { setupErrorHandlers } from 'src/lib/error-handlers'\nimport { initLoggerMcp } from 'src/lib/logger'\nimport { suppressTypelessPackageJsonWarning } from 'src/lib/node-warnings'\n\nimport { createMcpServer } from '../mcp/server'\n\n// The MCP tools run the same commands as the CLI, so they hit the same consumer `.ts` configs.\n// This keeps the client's log pane clean; it cannot affect protocol framing either way, since\n// the stdio transport frames JSON-RPC on stdout and `emitWarning` only ever writes to stderr.\nsuppressTypelessPackageJsonWarning()\n\nconst logger = initLoggerMcp()\n\nconst startServer = async () => {\n let server\n\n try {\n server = await createMcpServer()\n\n logger.info('MCP Server instance created')\n } catch (error) {\n logger.error({ err: error, msg: 'Failed to create MCP server' })\n logger.error(`Fatal error during server creation.`)\n\n process.exit(1)\n }\n\n try {\n const transport = new StdioServerTransport()\n\n await server.connect(transport)\n\n logger.info({ msg: 'Server connected to transport. Ready.' })\n } catch (error) {\n logger.error({ err: error, msg: 'Failed to initialize server' })\n logger.error(`Fatal error during server transport init.`)\n\n process.exit(1)\n }\n}\n\n// Setup error handlers\nsetupErrorHandlers(logger)\n\n// Start the server\nstartServer()\n", "import process from 'node:process'\nimport type { Logger } from 'pino'\n\nimport { LOG_FILE_PATH } from '../logger/index'\n\n/**\n * Setup error handlers for the application\n * @param logger - The logger instance\n *\n * ONLY FOR SERVER!\n */\nexport const setupErrorHandlers = (logger: Logger) => {\n process.on('SIGINT', () => {\n logger.info({ msg: 'Received SIGINT. Shutting down...' })\n process.exit(0)\n })\n\n process.on('SIGTERM', () => {\n logger.info({ msg: 'Received SIGTERM. Shutting down...' })\n process.exit(0)\n })\n\n process.on('uncaughtException', (error) => {\n logger.fatal({ err: error, msg: 'Uncaught Exception' })\n logger.error(`Uncaught Exception! Check ${LOG_FILE_PATH}. Shutting down...`)\n logger.flush()\n process.exit(1)\n })\n\n process.on('unhandledRejection', (reason, promise) => {\n logger.fatal({ reason, promise, msg: 'Unhandled Rejection' })\n logger.error(`Unhandled Rejection! Check ${LOG_FILE_PATH}. Shutting down...`)\n logger.flush()\n process.exit(1)\n })\n}\n", "import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { mcpMode } from 'src/lib/mcp-mode'\n\nimport packageJson from '../../package.json' with { type: 'json' }\nimport { initializePrompts } from './prompts'\nimport { initializeResources } from './resources'\nimport { initializeTools } from './tools'\n\nexport async function createMcpServer() {\n // Marks `process.stdin` as the JSON-RPC transport for every prompt guard in the\n // process. Set HERE rather than in `entry/mcp.ts` (the sole caller) because that\n // module starts a server at import time and so can never be unit-tested \u2014 a test\n // there could only stub the flag, which proves nothing about it ever being set.\n // This runs before `server.connect`, so no tool handler can outrun it.\n mcpMode.enabled = true\n\n const server = new McpServer(\n {\n name: 'infra-kit',\n version: packageJson.version,\n },\n {\n capabilities: {\n // `initializeResources` registers read-only resources below; the SDK's\n // `registerResource` also declares `resources.listChanged` on top of this.\n resources: { listChanged: true },\n tools: {},\n prompts: {},\n },\n },\n )\n\n await initializePrompts(server)\n await initializeResources(server)\n await initializeTools(server)\n\n return server\n}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nexport const initializePrompts = async (_server: McpServer) => {}\n", "import fs from 'node:fs'\nimport path from 'node:path'\nimport process from 'node:process'\n\n/** Repo-relative dev-context fragment directory the `infra-kit dev` runner writes into. */\nconst DEV_CONTEXT_DIR = path.join('.infra-kit', 'dev-context')\n\n/** The state directory that holds {@link DEV_CONTEXT_DIR}, used as an up-tree fallback anchor. */\nconst STATE_DIR = '.infra-kit'\n\n/**\n * Search upward from `start` for `relative`, returning the first hit or `undefined`. Mirrors the\n * `infra-kit/vite` helper's own `findUp`, because the MCP server is long-lived and may be rooted at\n * any nested package dir \u2014 the fragments live at the repo root's `.infra-kit/dev-context`, not the cwd.\n */\nconst findUp = (start: string, relative: string): string | undefined => {\n let dir = path.resolve(start)\n\n for (;;) {\n const candidate = path.join(dir, relative)\n\n if (fs.existsSync(candidate)) return candidate\n\n const parent = path.dirname(dir)\n\n if (parent === dir) return undefined\n\n dir = parent\n }\n}\n\n/**\n * Locate the dev-context fragment directory for `cwd`: prefer an existing `.infra-kit/dev-context` found\n * up-tree, else derive it from an up-tree `.infra-kit` (which may exist before the runner has written any\n * fragment), else `undefined` when neither anchor is present.\n */\nconst resolveFragmentDir = (cwd: string): string | undefined => {\n const existing = findUp(cwd, DEV_CONTEXT_DIR)\n\n if (existing) return existing\n\n const state = findUp(cwd, STATE_DIR)\n\n return state ? path.join(state, 'dev-context') : undefined\n}\n\n/** One backend the `infra-kit dev` runner has recorded as up, plus advisory staleness metadata. */\nexport interface DevContextApp {\n /** App folder name the fragment is keyed by (`<app>.json`). */\n app: string\n /** The app's package name (what the vite helper's `readLocalSet` matches on). */\n package: string\n /** The ACTUAL bound port \u2014 the runner is the binder, so this is authoritative. */\n port: number\n /** The registered portless hostname the runner published for this backend. */\n alias: string\n /** The authoritative local origin the vite helper proxies to. */\n origin: string\n /** Release slug the runner recorded (empty when it derived none). */\n release: string\n /** Writer PID \u2014 advisory only; NOT probed for liveness here. */\n pid: number\n /** Epoch ms the runner stamped into the fragment. */\n writtenAt: number\n /** The fragment file's mtime in epoch ms \u2014 the freshness signal a consumer should judge staleness by. */\n mtimeMs: number\n /** How old the fragment is (now \u2212 mtime), in ms. Advisory: a live backend refreshes this on each write. */\n ageMs: number\n}\n\n/**\n * The dev-context resource payload. Read-only and side-effect free: this reflects what `infra-kit dev`\n * last wrote to disk, never a live probe. `session: 'none'` (with an empty `apps`) is the normal,\n * non-error state when no dev session has written any fragment \u2014 an absent session is not a failure.\n */\nexport interface DevContextSnapshot {\n /** `'active'` iff at least one readable fragment was found, else `'none'`. */\n session: 'active' | 'none'\n /** The resolved fragment directory, or `undefined` when no `.infra-kit` anchor exists up-tree. */\n fragmentDir: string | undefined\n /** Epoch ms this snapshot was read, so a consumer can compute freshness against {@link DevContextApp.mtimeMs}. */\n readAt: number\n /** Every backend the runner currently records as up, sorted by app name. Empty when the session is `none`. */\n apps: DevContextApp[]\n}\n\n/** Coerce an unknown parsed fragment field to a number, defaulting to `0` when absent/non-numeric. */\nconst num = (value: unknown): number => {\n return typeof value === 'number' && Number.isFinite(value) ? value : 0\n}\n\n/** Coerce an unknown parsed fragment field to a string, defaulting to `''`. */\nconst str = (value: unknown): string => {\n return typeof value === 'string' ? value : ''\n}\n\n/** Parse one `<app>.json` fragment into a {@link DevContextApp}, or `null` when it is unreadable/not JSON. */\nconst readFragment = (fragmentDir: string, file: string, now: number): DevContextApp | null => {\n const full = path.join(fragmentDir, file)\n\n try {\n const stat = fs.statSync(full)\n const parsed: unknown = JSON.parse(fs.readFileSync(full, 'utf-8'))\n\n if (typeof parsed !== 'object' || parsed === null) return null\n\n const fragment = parsed as Record<string, unknown>\n\n return {\n app: file.replace(/\\.json$/, ''),\n package: str(fragment.package),\n port: num(fragment.port),\n alias: str(fragment.alias),\n origin: str(fragment.origin),\n release: str(fragment.release),\n pid: num(fragment.pid),\n writtenAt: num(fragment.writtenAt),\n mtimeMs: Math.round(stat.mtimeMs),\n ageMs: Math.max(0, Math.round(now - stat.mtimeMs)),\n }\n } catch {\n // A half-written, deleted-mid-read, or non-JSON fragment is skipped rather than failing the whole\n // read \u2014 the resource stays advisory and never throws on a transient runner write.\n return null\n }\n}\n\n/**\n * Read the dev-context fragments `infra-kit dev` last wrote for the repo containing `cwd`.\n *\n * Strictly read-only and never throws: a missing anchor, a missing fragment dir, or an unreadable\n * fragment all resolve to a clean snapshot (`session: 'none'` when nothing readable was found). Freshness\n * is reported via each fragment's `mtimeMs`/`ageMs`; no liveness is asserted that was not observed on disk.\n */\nexport const readDevContext = (cwd: string = process.cwd()): DevContextSnapshot => {\n const now = Date.now()\n const fragmentDir = resolveFragmentDir(cwd)\n\n if (!fragmentDir || !fs.existsSync(fragmentDir)) {\n return { session: 'none', fragmentDir, readAt: now, apps: [] }\n }\n\n let files: string[]\n\n try {\n files = fs.readdirSync(fragmentDir).filter((file) => {\n return file.endsWith('.json')\n })\n } catch {\n return { session: 'none', fragmentDir, readAt: now, apps: [] }\n }\n\n const apps = files\n .map((file) => {\n return readFragment(fragmentDir, file, now)\n })\n .filter((app): app is DevContextApp => {\n return app !== null\n })\n .sort((a, b) => {\n return a.app.localeCompare(b.app)\n })\n\n return { session: apps.length > 0 ? 'active' : 'none', fragmentDir, readAt: now, apps }\n}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport type { InfraKitConfig } from 'src/lib/infra-kit-config'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\n\nimport type { DevContextSnapshot } from './dev-context'\nimport { readDevContext } from './dev-context'\n\n/** Stable URI of the merged-config resource. */\nexport const CONFIG_RESOURCE_URI = 'infra-kit://config'\n\n/** Stable URI of the dev-context resource. */\nexport const DEV_CONTEXT_RESOURCE_URI = 'infra-kit://dev-context'\n\n/**\n * The two disk reads the resources need, injected so the registration is unit-testable without touching\n * the filesystem or the config-loader's mtime cache. Production uses the real readers by default.\n */\nexport interface ResourceDeps {\n /** Loads the merged `infra-kit.json` (all override layers applied). May throw when no config exists. */\n loadConfig: () => Promise<InfraKitConfig>\n /** Reads the dev-context fragments `infra-kit dev` last wrote. Never throws; absent session \u21D2 empty. */\n readDevContext: () => DevContextSnapshot\n}\n\nconst defaultDeps: ResourceDeps = {\n loadConfig: getInfraKitConfig,\n readDevContext: () => {\n return readDevContext()\n },\n}\n\n/** Serialize a resource body as pretty JSON text \u2014 the wire form every MCP client can read. */\nconst jsonResource = (uri: string, value: unknown): { contents: { uri: string; mimeType: string; text: string }[] } => {\n return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(value, null, 2) }] }\n}\n\n/**\n * Register infra-kit's READ-ONLY MCP resources so an agent can inspect the repo's config and dev state\n * without calling a tool (or mutating anything):\n *\n * - `infra-kit://config` \u2014 the merged `infra-kit.json` an agent would run against.\n * - `infra-kit://dev-context` \u2014 what `infra-kit dev` last wrote (backends up, ports, freshness); an\n * absent dev session resolves to an empty `session: 'none'` payload, NOT\n * an error.\n *\n * Both handlers are side-effect free and swallow their reader's failure into an `{ error }` payload rather\n * than throwing, so a bad on-disk config can never crash the long-lived MCP server.\n */\nexport const initializeResources = async (server: McpServer, deps: ResourceDeps = defaultDeps) => {\n server.registerResource(\n 'infra-kit-config',\n CONFIG_RESOURCE_URI,\n {\n title: 'infra-kit config',\n description: 'The merged infra-kit.json configuration (all override layers applied). Read-only.',\n mimeType: 'application/json',\n },\n async (uri) => {\n try {\n return jsonResource(uri.toString(), await deps.loadConfig())\n } catch (error) {\n return jsonResource(uri.toString(), { error: error instanceof Error ? error.message : String(error) })\n }\n },\n )\n\n server.registerResource(\n 'infra-kit-dev-context',\n DEV_CONTEXT_RESOURCE_URI,\n {\n title: 'infra-kit dev context',\n description:\n 'What `infra-kit dev` last wrote: backends currently up, their ports/origins, and fragment ' +\n 'freshness. Read-only and advisory (no liveness probe). No active session resolves to an empty payload.',\n mimeType: 'application/json',\n },\n // The SDK's `ReadResourceCallback` accepts a synchronous return; the disk read is sync, so no `async`.\n (uri) => {\n return jsonResource(uri.toString(), deps.readDevContext())\n },\n )\n}\n", "import { commandEcho } from 'src/lib/command-echo'\nimport { ensureUserProjectConfig } from 'src/lib/config-bootstrap'\nimport { logger } from 'src/lib/logger'\nimport { textContent } from 'src/types'\nimport type { ToolsExecutionResult } from 'src/types'\n\ninterface ToolHandlerArgs {\n toolName: string\n handler: (params: any) => Promise<ToolsExecutionResult>\n /**\n * When true, this tool is gated by the destructive-op confirm gate below. Sourced from the\n * catalog tool's {@link CatalogMcpTool.requiresHumanConfirm} at registration (`mcp/tools/index.ts`).\n */\n requiresHumanConfirm?: boolean\n}\n\n/** True when the incoming MCP params carry an explicit `confirm:true` (the call-2 execute signal). */\nconst isConfirmed = (params: unknown): boolean => {\n return typeof params === 'object' && params !== null && (params as { confirm?: unknown }).confirm === true\n}\n\n/**\n * The call-1 gate response for a `requiresHumanConfirm` tool: it names the tool and echoes the\n * resolved args (minus the `confirm` flag itself) and tells the caller to re-invoke with\n * `confirm:true`. Crucially it sets `isError: true` \u2014 the MCP SDK validates a result's\n * `structuredContent` against the tool's `outputSchema` UNLESS `isError` is set (server/mcp.js\n * `validateToolOutput`), and every gated tool declares an outputSchema the gate payload does not\n * match, so without `isError` the real server would throw an output-validation error instead of\n * returning the gate. This is a soft stop, not a failure: the tool did NOT run.\n */\nconst buildConfirmGate = (toolName: string, params: unknown): ToolsExecutionResult => {\n const resolvedArgs =\n typeof params === 'object' && params !== null\n ? Object.fromEntries(\n Object.entries(params as Record<string, unknown>).filter(([key]) => {\n return key !== 'confirm'\n }),\n )\n : {}\n\n const structuredContent = {\n status: 'confirmation_required' as const,\n tool: toolName,\n resolvedArgs,\n message: `${toolName} mutates external state and is gated. It was NOT executed. Re-call ${toolName} with the same arguments plus \"confirm\": true to execute.`,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n isError: true,\n }\n}\n\nexport const createToolHandler = ({\n toolName,\n handler,\n requiresHumanConfirm,\n}: ToolHandlerArgs): ((params: unknown) => Promise<ToolsExecutionResult>) => {\n return async (params: unknown) => {\n logger.info({ msg: `Tool execution started: ${toolName}`, params })\n try {\n // MCP entry-boundary seed. This wrapper is the sole chokepoint for every exposed tool\n // (`src/mcp/tools/index.ts` holds the only `registerTool` call), and the `mcp` command itself is\n // in program.ts's SEED_EXCLUDED \u2014 so an MCP server seeds lazily, on its FIRST tool invocation,\n // and a server that never receives one never writes to $HOME. The once-per-process guard inside\n // ensureUserProjectConfig makes calls 2..N free on this long-lived process.\n //\n // Deliberately INSIDE the try, not before it: defense-in-depth. ensureUserProjectConfig is\n // contractually never-throw, but if that contract ever broke, an unhandled rejection here would\n // take down the long-lived server. Inside the try it degrades to an ordinary tool error.\n await ensureUserProjectConfig()\n\n // The command handlers record their resolved flags into the `commandEcho` singleton. On the CLI,\n // Commander's `preAction` clears it before every command; this long-lived server never runs that\n // hook, so without a reset here one tool call's flags would leak into the next one's snapshot and\n // the options array would grow for the life of the process.\n commandEcho.reset()\n\n // Orthogonal destructive-op confirm gate. It sits BEFORE the handler and is INDEPENDENT of the\n // `confirmedCommand:true` injected below \u2014 that flag is a prompt-skip / behavior discriminator\n // (e.g. worktrees-remove keys `allowEditorRelaunch` off it) and MUST keep being injected on the\n // real call, or the non-TTY server would hang on an inquirer prompt and the Zed relaunch would\n // re-enable. When a tool is flagged and the caller has not opted in with `confirm:true`, return\n // the resolved-args gate and DO NOT run the handler; a second call carrying `confirm:true` falls\n // through and executes exactly as an unflagged tool would.\n if (requiresHumanConfirm === true && !isConfirmed(params)) {\n logger.info({ msg: `Tool execution gated (awaiting confirm): ${toolName}` })\n\n return buildConfirmGate(toolName, params)\n }\n\n const payload = await handler({ ...(params as object), confirmedCommand: true })\n\n logger.info({ msg: `Tool execution successful: ${toolName}` })\n\n return payload\n } catch (error) {\n logger.error({\n err: error,\n params,\n msg: `Tool execution failed: ${toolName}`,\n })\n\n throw error\n }\n }\n}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { getExposedMcpTools } from 'src/lib/command-catalog'\nimport { createToolHandler } from 'src/lib/tool-handler'\n\nexport const initializeTools = async (server: McpServer) => {\n // The registered tool set is derived from the single command catalog, filtered\n // by its explicit `mcpExposed` allowlist. doctor is intentionally excluded there\n // (host-inspecting) and must never be registered here.\n for (const tool of getExposedMcpTools()) {\n server.registerTool(\n tool.name,\n {\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n },\n createToolHandler({\n toolName: tool.name,\n handler: tool.handler,\n requiresHumanConfirm: tool.requiresHumanConfirm,\n }),\n )\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["StdioServerTransport", "process", "process", "setupErrorHandlers", "logger", "process", "error", "LOG_FILE_PATH", "reason", "promise", "McpServer", "initializePrompts", "_server", "fs", "path", "process", "DEV_CONTEXT_DIR", "STATE_DIR", "findUp", "start", "relative", "dir", "candidate", "parent", "resolveFragmentDir", "cwd", "existing", "state", "num", "value", "str", "readFragment", "fragmentDir", "file", "now", "full", "stat", "parsed", "fragment", "readDevContext", "files", "apps", "app", "a", "b", "CONFIG_RESOURCE_URI", "DEV_CONTEXT_RESOURCE_URI", "defaultDeps", "getInfraKitConfig", "readDevContext", "jsonResource", "uri", "value", "initializeResources", "server", "deps", "error", "isConfirmed", "params", "buildConfirmGate", "toolName", "resolvedArgs", "key", "structuredContent", "textContent", "createToolHandler", "handler", "requiresHumanConfirm", "logger", "ensureUserProjectConfig", "commandEcho", "payload", "error", "initializeTools", "server", "tool", "getExposedMcpTools", "createToolHandler", "createMcpServer", "mcpMode", "server", "McpServer", "package_default", "initializePrompts", "initializeResources", "initializeTools", "suppressTypelessPackageJsonWarning", "logger", "initLoggerMcp", "startServer", "server", "createMcpServer", "error", "process", "transport", "StdioServerTransport", "setupErrorHandlers"]
3
+ "sources": ["../src/entry/mcp.ts", "../src/lib/error-handlers/index.ts", "../src/mcp/server.ts", "../src/mcp/prompts/index.ts", "../src/mcp/resources/index.ts", "../src/lib/tool-handler/tool-handler.ts", "../src/mcp/tools/index.ts"],
4
+ "sourcesContent": ["import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport process from 'node:process'\n\nimport { setupErrorHandlers } from 'src/lib/error-handlers'\nimport { initLoggerMcp } from 'src/lib/logger'\nimport { suppressTypelessPackageJsonWarning } from 'src/lib/node-warnings'\n\nimport { createMcpServer } from '../mcp/server'\n\n// The MCP tools run the same commands as the CLI, so they hit the same consumer `.ts` configs.\n// This keeps the client's log pane clean; it cannot affect protocol framing either way, since\n// the stdio transport frames JSON-RPC on stdout and `emitWarning` only ever writes to stderr.\nsuppressTypelessPackageJsonWarning()\n\nconst logger = initLoggerMcp()\n\nconst startServer = async () => {\n let server\n\n try {\n server = await createMcpServer()\n\n logger.info('MCP Server instance created')\n } catch (error) {\n logger.error({ err: error, msg: 'Failed to create MCP server' })\n logger.error(`Fatal error during server creation.`)\n\n process.exit(1)\n }\n\n try {\n const transport = new StdioServerTransport()\n\n await server.connect(transport)\n\n logger.info({ msg: 'Server connected to transport. Ready.' })\n } catch (error) {\n logger.error({ err: error, msg: 'Failed to initialize server' })\n logger.error(`Fatal error during server transport init.`)\n\n process.exit(1)\n }\n}\n\n// Setup error handlers\nsetupErrorHandlers(logger)\n\n// Start the server\nstartServer()\n", "import process from 'node:process'\nimport type { Logger } from 'pino'\n\nimport { LOG_FILE_PATH } from '../logger/index'\n\n/**\n * Setup error handlers for the application\n *\n * @param logger - The logger instance\n *\n * ONLY FOR SERVER!\n */\nexport const setupErrorHandlers = (logger: Logger) => {\n process.on('SIGINT', () => {\n logger.info({ msg: 'Received SIGINT. Shutting down...' })\n process.exit(0)\n })\n\n process.on('SIGTERM', () => {\n logger.info({ msg: 'Received SIGTERM. Shutting down...' })\n process.exit(0)\n })\n\n process.on('uncaughtException', (error) => {\n logger.fatal({ err: error, msg: 'Uncaught Exception' })\n logger.error(`Uncaught Exception! Check ${LOG_FILE_PATH}. Shutting down...`)\n logger.flush()\n process.exit(1)\n })\n\n process.on('unhandledRejection', (reason, promise) => {\n logger.fatal({ reason, promise, msg: 'Unhandled Rejection' })\n logger.error(`Unhandled Rejection! Check ${LOG_FILE_PATH}. Shutting down...`)\n logger.flush()\n process.exit(1)\n })\n}\n", "import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { mcpMode } from 'src/lib/mcp-mode'\n\nimport packageJson from '../../package.json' with { type: 'json' }\nimport { initializePrompts } from './prompts'\nimport { initializeResources } from './resources'\nimport { initializeTools } from './tools'\n\nexport async function createMcpServer() {\n // Marks `process.stdin` as the JSON-RPC transport for every prompt guard in the\n // process. Set HERE rather than in `entry/mcp.ts` (the sole caller) because that\n // module starts a server at import time and so can never be unit-tested \u2014 a test\n // there could only stub the flag, which proves nothing about it ever being set.\n // This runs before `server.connect`, so no tool handler can outrun it.\n mcpMode.enabled = true\n\n const server = new McpServer(\n {\n name: 'infra-kit',\n version: packageJson.version,\n },\n {\n capabilities: {\n // `initializeResources` registers read-only resources below; the SDK's\n // `registerResource` also declares `resources.listChanged` on top of this.\n resources: { listChanged: true },\n tools: {},\n prompts: {},\n },\n },\n )\n\n await initializePrompts(server)\n await initializeResources(server)\n await initializeTools(server)\n\n return server\n}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nexport const initializePrompts = async (_server: McpServer) => {}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport type { InfraKitConfig } from 'src/lib/infra-kit-config'\nimport { getInfraKitConfig } from 'src/lib/infra-kit-config'\n\nimport type { DevContextSnapshot } from './dev-context'\nimport { readDevContext } from './dev-context'\n\n/** Stable URI of the merged-config resource. */\nexport const CONFIG_RESOURCE_URI = 'infra-kit://config'\n\n/** Stable URI of the dev-context resource. */\nexport const DEV_CONTEXT_RESOURCE_URI = 'infra-kit://dev-context'\n\n/**\n * The two disk reads the resources need, injected so the registration is unit-testable without touching\n * the filesystem or the config-loader's mtime cache. Production uses the real readers by default.\n */\nexport interface ResourceDeps {\n /** Loads the merged `infra-kit.json` (all override layers applied). May throw when no config exists. */\n loadConfig: () => Promise<InfraKitConfig>\n /** Reads the dev-context fragments `infra-kit dev` last wrote. Never throws; absent session \u21D2 empty. */\n readDevContext: () => DevContextSnapshot\n}\n\nconst defaultDeps: ResourceDeps = {\n loadConfig: getInfraKitConfig,\n readDevContext: () => {\n return readDevContext()\n },\n}\n\n/** Serialize a resource body as pretty JSON text \u2014 the wire form every MCP client can read. */\nconst jsonResource = (uri: string, value: unknown): { contents: { uri: string; mimeType: string; text: string }[] } => {\n return { contents: [{ uri, mimeType: 'application/json', text: JSON.stringify(value, null, 2) }] }\n}\n\n/**\n * Register infra-kit's READ-ONLY MCP resources so an agent can inspect the repo's config and dev state\n * without calling a tool (or mutating anything):\n *\n * - `infra-kit://config` \u2014 the merged `infra-kit.json` an agent would run against.\n * - `infra-kit://dev-context` \u2014 what `infra-kit dev` last wrote (backends up, ports, freshness); an\n * absent dev session resolves to an empty `session: 'none'` payload, NOT\n * an error.\n *\n * Both handlers are side-effect free and swallow their reader's failure into an `{ error }` payload rather\n * than throwing, so a bad on-disk config can never crash the long-lived MCP server.\n */\nexport const initializeResources = async (server: McpServer, deps: ResourceDeps = defaultDeps) => {\n server.registerResource(\n 'infra-kit-config',\n CONFIG_RESOURCE_URI,\n {\n title: 'infra-kit config',\n description: 'The merged infra-kit.json configuration (all override layers applied). Read-only.',\n mimeType: 'application/json',\n },\n async (uri) => {\n try {\n return jsonResource(uri.toString(), await deps.loadConfig())\n } catch (error) {\n return jsonResource(uri.toString(), { error: error instanceof Error ? error.message : String(error) })\n }\n },\n )\n\n server.registerResource(\n 'infra-kit-dev-context',\n DEV_CONTEXT_RESOURCE_URI,\n {\n title: 'infra-kit dev context',\n description:\n 'What `infra-kit dev` last wrote: backends currently up, their ports/origins, and fragment ' +\n 'freshness. Read-only and advisory (no liveness probe). No active session resolves to an empty payload.',\n mimeType: 'application/json',\n },\n // The SDK's `ReadResourceCallback` accepts a synchronous return; the disk read is sync, so no `async`.\n (uri) => {\n return jsonResource(uri.toString(), deps.readDevContext())\n },\n )\n}\n", "import { commandEcho } from 'src/lib/command-echo'\nimport { ensureUserProjectConfig } from 'src/lib/config-bootstrap'\nimport { logger } from 'src/lib/logger'\nimport { textContent } from 'src/types'\nimport type { ToolsExecutionResult } from 'src/types'\n\ninterface ToolHandlerArgs {\n toolName: string\n handler: (params: any) => Promise<ToolsExecutionResult>\n /**\n * When true, this tool is gated by the destructive-op confirm gate below. Sourced from the\n * catalog tool's {@link CatalogMcpTool.requiresHumanConfirm} at registration (`mcp/tools/index.ts`).\n */\n requiresHumanConfirm?: boolean\n}\n\n/** True when the incoming MCP params carry an explicit `confirm:true` (the call-2 execute signal). */\nconst isConfirmed = (params: unknown): boolean => {\n return typeof params === 'object' && params !== null && (params as { confirm?: unknown }).confirm === true\n}\n\n/**\n * The call-1 gate response for a `requiresHumanConfirm` tool: it names the tool and echoes the\n * resolved args (minus the `confirm` flag itself) and tells the caller to re-invoke with\n * `confirm:true`. Crucially it sets `isError: true` \u2014 the MCP SDK validates a result's\n * `structuredContent` against the tool's `outputSchema` UNLESS `isError` is set (server/mcp.js\n * `validateToolOutput`), and every gated tool declares an outputSchema the gate payload does not\n * match, so without `isError` the real server would throw an output-validation error instead of\n * returning the gate. This is a soft stop, not a failure: the tool did NOT run.\n */\nconst buildConfirmGate = (toolName: string, params: unknown): ToolsExecutionResult => {\n const resolvedArgs =\n typeof params === 'object' && params !== null\n ? Object.fromEntries(\n Object.entries(params as Record<string, unknown>).filter(([key]) => {\n return key !== 'confirm'\n }),\n )\n : {}\n\n const structuredContent = {\n status: 'confirmation_required' as const,\n tool: toolName,\n resolvedArgs,\n message: `${toolName} mutates external state and is gated. It was NOT executed. Re-call ${toolName} with the same arguments plus \"confirm\": true to execute.`,\n }\n\n return {\n content: textContent(JSON.stringify(structuredContent, null, 2)),\n structuredContent,\n isError: true,\n }\n}\n\nexport const createToolHandler = ({\n toolName,\n handler,\n requiresHumanConfirm,\n}: ToolHandlerArgs): ((params: unknown) => Promise<ToolsExecutionResult>) => {\n return async (params: unknown) => {\n logger.info({ msg: `Tool execution started: ${toolName}`, params })\n try {\n // MCP entry-boundary seed. This wrapper is the sole chokepoint for every exposed tool\n // (`src/mcp/tools/index.ts` holds the only `registerTool` call), and the `mcp` command itself is\n // in program.ts's SEED_EXCLUDED \u2014 so an MCP server seeds lazily, on its FIRST tool invocation,\n // and a server that never receives one never writes to $HOME. The once-per-process guard inside\n // ensureUserProjectConfig makes calls 2..N free on this long-lived process.\n //\n // Deliberately INSIDE the try, not before it: defense-in-depth. ensureUserProjectConfig is\n // contractually never-throw, but if that contract ever broke, an unhandled rejection here would\n // take down the long-lived server. Inside the try it degrades to an ordinary tool error.\n await ensureUserProjectConfig()\n\n // The command handlers record their resolved flags into the `commandEcho` singleton. On the CLI,\n // Commander's `preAction` clears it before every command; this long-lived server never runs that\n // hook, so without a reset here one tool call's flags would leak into the next one's snapshot and\n // the options array would grow for the life of the process.\n commandEcho.reset()\n\n // Orthogonal destructive-op confirm gate. It sits BEFORE the handler and is INDEPENDENT of the\n // `confirmedCommand:true` injected below \u2014 that flag is a prompt-skip / behavior discriminator\n // (e.g. worktrees-remove keys `allowEditorRelaunch` off it) and MUST keep being injected on the\n // real call, or the non-TTY server would hang on an inquirer prompt and the Zed relaunch would\n // re-enable. When a tool is flagged and the caller has not opted in with `confirm:true`, return\n // the resolved-args gate and DO NOT run the handler; a second call carrying `confirm:true` falls\n // through and executes exactly as an unflagged tool would.\n if (requiresHumanConfirm === true && !isConfirmed(params)) {\n logger.info({ msg: `Tool execution gated (awaiting confirm): ${toolName}` })\n\n return buildConfirmGate(toolName, params)\n }\n\n const payload = await handler({ ...(params as object), confirmedCommand: true })\n\n logger.info({ msg: `Tool execution successful: ${toolName}` })\n\n return payload\n } catch (error) {\n logger.error({\n err: error,\n params,\n msg: `Tool execution failed: ${toolName}`,\n })\n\n throw error\n }\n }\n}\n", "import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\n\nimport { getExposedMcpTools } from 'src/lib/command-catalog'\nimport { createToolHandler } from 'src/lib/tool-handler'\n\nexport const initializeTools = async (server: McpServer) => {\n // The registered tool set is derived from the single command catalog, filtered\n // by its explicit `mcpExposed` allowlist. doctor is intentionally excluded there\n // (host-inspecting) and must never be registered here.\n for (const tool of getExposedMcpTools()) {\n server.registerTool(\n tool.name,\n {\n description: tool.description,\n inputSchema: tool.inputSchema,\n outputSchema: tool.outputSchema,\n },\n createToolHandler({\n toolName: tool.name,\n handler: tool.handler,\n requiresHumanConfirm: tool.requiresHumanConfirm,\n }),\n )\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["StdioServerTransport", "process", "process", "setupErrorHandlers", "logger", "process", "error", "LOG_FILE_PATH", "reason", "promise", "McpServer", "initializePrompts", "_server", "CONFIG_RESOURCE_URI", "DEV_CONTEXT_RESOURCE_URI", "defaultDeps", "getInfraKitConfig", "readDevContext", "jsonResource", "uri", "value", "initializeResources", "server", "deps", "error", "isConfirmed", "params", "buildConfirmGate", "toolName", "resolvedArgs", "key", "structuredContent", "textContent", "createToolHandler", "handler", "requiresHumanConfirm", "logger", "ensureUserProjectConfig", "commandEcho", "payload", "error", "initializeTools", "server", "tool", "getExposedMcpTools", "createToolHandler", "createMcpServer", "mcpMode", "server", "McpServer", "package_default", "initializePrompts", "initializeResources", "initializeTools", "suppressTypelessPackageJsonWarning", "logger", "initLoggerMcp", "startServer", "server", "createMcpServer", "error", "process", "transport", "StdioServerTransport", "setupErrorHandlers"]
7
7
  }
@@ -1,2 +1,2 @@
1
- import{h as i}from"./chunk-2QJAIOPI.js";import{a as t}from"./chunk-KPQTJQ42.js";import"./chunk-LKKKDB6V.js";import"./chunk-CHETBZ6M.js";import a from"node:fs";import o from"node:process";import{fileURLToPath as s}from"node:url";var m=r=>{let n=r.indexOf("--parent-pid");if(n===-1)return;let e=Number(r[n+1]);return Number.isInteger(e)&&e>0?e:void 0},p=()=>a.realpathSync(s(new URL("./cli.js",import.meta.url))),d=async()=>{await i(t.version,{selfRealPath:p(),parentPid:m(o.argv)})};d().catch(()=>{o.exitCode=0});
1
+ import{h as i}from"./chunk-2QJAIOPI.js";import{a as t}from"./chunk-DPCWCXJD.js";import"./chunk-LKKKDB6V.js";import"./chunk-CHETBZ6M.js";import a from"node:fs";import o from"node:process";import{fileURLToPath as s}from"node:url";var m=r=>{let n=r.indexOf("--parent-pid");if(n===-1)return;let e=Number(r[n+1]);return Number.isInteger(e)&&e>0?e:void 0},p=()=>a.realpathSync(s(new URL("./cli.js",import.meta.url))),d=async()=>{await i(t.version,{selfRealPath:p(),parentPid:m(o.argv)})};d().catch(()=>{o.exitCode=0});
2
2
  //# sourceMappingURL=update-check.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "infra-kit",
3
3
  "type": "module",
4
- "version": "0.3.11",
4
+ "version": "0.3.13",
5
5
  "files": [
6
6
  "dist"
7
7
  ],
@@ -41,17 +41,17 @@
41
41
  "@inquirer/input": "^5.1.2",
42
42
  "@inquirer/password": "^5.0.2",
43
43
  "@inquirer/select": "^5.2.1",
44
- "@modelcontextprotocol/sdk": "^1.29.0",
45
- "@slip-stream-kit/config": "^0.3.3",
46
- "chalk": "^5.6.2",
44
+ "@modelcontextprotocol/sdk": "^1.30.0",
45
+ "@slip-stream-kit/config": "^0.3.13",
46
+ "chalk": "^6.0.0",
47
47
  "chokidar": "^5.0.0",
48
48
  "commander": "^15.0.0",
49
- "fastify": "5.10.0",
50
- "ink": "^7.1.0",
49
+ "fastify": "catalog:",
50
+ "ink": "^7.1.1",
51
51
  "pino": "^10.3.1",
52
52
  "pino-pretty": "^13.1.3",
53
- "portless": "^0.15.2",
54
- "react": "19.2.7",
53
+ "portless": "^0.15.5",
54
+ "react": "19.2.8",
55
55
  "yaml": "^2.9.0",
56
56
  "zod": "^4.4.3",
57
57
  "zx": "^8.8.5"
@@ -61,7 +61,7 @@
61
61
  "@types/react": "19.2.17",
62
62
  "@wl/eslint-config": "workspace:*",
63
63
  "@wl/vitest-config": "workspace:*",
64
- "esbuild": "^0.28.1",
64
+ "esbuild": "^0.28.2",
65
65
  "ink-testing-library": "^4.0.0",
66
66
  "typescript": "^6.0.3"
67
67
  }
package/readme.md CHANGED
@@ -45,7 +45,7 @@ Strict schema file that declares audit rules (`requiredScripts`, `requiredFiles`
45
45
  | Group | Command | Purpose |
46
46
  |-------|---------|---------|
47
47
  | **Develop** | `dev` | Start local dev server with portless proxy (interactive wizard, long-running) |
48
- | | `dev-status` | Report what `infra-kit dev` currently has running (read-only) |
48
+ | | `dev-status` | Report what `infra-kit dev` currently has running (read-only; finds the fragment dir by searching upward, so it works from any subdirectory) |
49
49
  | **Release** | `release merge-dev` | Merge `origin/dev` into every open release branch and push |
50
50
  | | `release list` | List available releases |
51
51
  | | `release create` | Create a new release |
@@ -1,2 +0,0 @@
1
- import{a as _}from"./chunk-LKKKDB6V.js";import{L,T as A,V as O,g as S,m as R,z as c}from"./chunk-M46KGMQ6.js";import B from"node:process";import{$ as w}from"zx";var M=async()=>{try{return await w`cmux --version`.quiet(),!0}catch{return!1}},j=async r=>{let{cwd:e,title:t,layout:o}=r,s=JSON.stringify(o),n=(await w({env:{...B.env,CMUX_QUIET:"1"}})`cmux new-workspace --name ${t} --cwd ${e} --focus false --layout ${s}`).stdout;return U(n)},F=async r=>{try{await w`cmux close-workspace --workspace ${r}`.quiet()}catch(e){c.debug({error:e,ref:r},"cmux: skipped closing dev workspace")}},U=r=>{let e=r.match(/workspace:\d+/);if(!e)throw new Error("cmux: could not locate workspace ref in new-workspace output");return e[0]};import{$ as J}from"zx";import{realpath as q}from"node:fs/promises";import{$ as I}from"zx";var y=async r=>{try{return await q(r)}catch{return r}},P=async()=>{try{let[r,e]=await Promise.all([I`cmux workspace list --json`.quiet(),I`cmux workspace-group list --json`.quiet()]),t=JSON.parse(r.stdout).workspaces??[],o=JSON.parse(e.stdout).groups??[],s=new Set(o.map(n=>n.anchor_workspace_ref).filter(n=>typeof n=="string")),a=new Map;for(let n of t)!n.current_directory||s.has(n.ref)||a.set(await y(n.current_directory),n.ref);return a}catch(r){return c.debug({error:r},"cmux: skipped listing workspaces by cwd"),new Map}};var G=async r=>{try{let t=(await P()).get(await y(r));if(!t)return;await J`cmux workspace close ${t}`.quiet()}catch(e){c.debug({error:e,cwd:r},"cmux: skipped closing workspace by cwd")}};import{$ as k}from"zx";var N=async r=>{try{let e=(await k`cmux workspace-group list --json`.quiet()).stdout;return(JSON.parse(e).groups??[]).find(s=>s.name===r)?.ref??null}catch(e){return c.debug({error:e,name:r},"cmux: skipped group lookup"),null}},H=async(r,e)=>{try{let o=(await k`cmux workspace-group create --name ${r} --from ${e.join(",")}`).stdout.match(/workspace_group:\d+/)?.[0]??await N(r);return o&&await k`cmux workspace-group pin ${o}`.quiet(),o??null}catch(t){return c.warn({error:t,name:r},"\u26A0\uFE0F cmux: failed to create workspace group"),null}};import{$ as m}from"zx";var z=async r=>{let{cwd:e,title:t,group:o}=r,s=A(await O()),a=o?(await m`cmux workspace create --cwd ${e} --group ${o} --group-placement end`).stdout:(await m`cmux workspace create --cwd ${e}`).stdout,n=Q(a),p=(await m`cmux list-pane-surfaces --workspace ${n}`).stdout,u=K(p);return await m`cmux new-split right --workspace ${n} --surface ${u}`,s==="three-pane"&&await m`cmux new-split down --workspace ${n} --surface ${u}`,t&&await m`cmux workspace rename --workspace ${n} --title ${t}`,n},K=r=>{let e=r.match(/surface:\d+/);if(!e)throw new Error("cmux: could not locate initial surface in list-pane-surfaces output");return e[0]},Q=r=>{let e=r.match(/workspace:\d+/);if(!e)throw new Error("cmux: could not locate workspace ref in workspace create output");return e[0]};var V=r=>{let{branch:e}=r,t=S(e);return t?R(t):e};var X=new Set(["ExitPromptError","AbortPromptError","PromptCancelledError"]),T=r=>r instanceof Error&&X.has(r.name),ne=r=>{if(T(r))return!0;let e=r?.cause;return T(e)};import{loadDev as Y}from"@slip-stream-kit/config/internal";import Z from"node:path";var rr=(r,e)=>{let t=new Set;for(let o of Object.values(r))for(let s of Object.keys(L(o,e).proxy))t.add(s);return[...t]},er=async(r,e)=>{let t={},o=[];return await Promise.all(e.map(async s=>{try{let n=(await Y(Z.join(r,"apps",s,"ui")))?.proxy?.routes;if(!n)return;t[s]=Object.fromEntries(Object.entries(n).map(([p,u])=>[p,u.packageName]))}catch(a){o.push({app:s,message:a instanceof Error?a.message:String(a)})}})),{routePkg:(s,a)=>t[s]?.[a],loadErrors:o}},pe=async(r,e,t,o)=>{let{routePkg:s,loadErrors:a}=await er(r,rr(e,t));return{ctx:{discovered:t,apiPkgByApp:Object.fromEntries(o.map(n=>[n.name,n.packageName])),routePkg:s},loadErrors:a}};import{execFile as tr}from"node:child_process";import{createHash as or}from"node:crypto";import{existsSync as W,readFileSync as d}from"node:fs";import sr from"node:http";import nr from"node:https";import ar from"node:net";import{homedir as ir}from"node:os";import{dirname as h,join as l}from"node:path";import f from"node:process";import cr from"node:tls";import{fileURLToPath as ur}from"node:url";import{promisify as pr}from"node:util";var mr=pr(tr),lr=r=>{let e=JSON.parse(d(r,"utf-8")),t=typeof e.bin=="string"?e.bin:e.bin?.portless;return t==null||t===""?null:t},fr=()=>{try{let r=h(ur(import.meta.url));for(;;){let e=l(r,"node_modules","portless","package.json");if(W(e)){let o=lr(e);return o==null?null:l(h(e),o)}let t=h(r);if(t===r)return null;r=t}}catch{return null}},b,D=()=>(b===void 0&&(b=fr()),b),gr=/^[\w@%+=:,./-]+$/,dr="'\\''",xr=r=>gr.test(r)?r:`'${r.replaceAll("'",dr)}'`,ve=(r,e)=>{let t=[e.execPath??f.execPath,e.bin,...r];return(e.sudo===!0?"sudo ":"")+t.map(xr).join(" ")},yr=1500,C=1500,wr="x-portless",E="127.0.0.1",Pr="localhost",kr="/Library/LaunchDaemons/sh.portless.proxy.plist",hr="/etc/systemd/system/portless.service",br=(r=W)=>{try{return f.platform==="darwin"?r(kr)?"yes":"no":f.platform==="linux"?r(hr)?"yes":"no":"unknown"}catch{return"unknown"}},Cr=r=>new Promise(e=>{let t=ar.connect({host:E,port:r}),o=s=>{t.destroy(),e(s)};t.setTimeout(C),t.once("connect",()=>{o(!0)}),t.once("timeout",()=>{o(!1)}),t.once("error",()=>{o(!1)})}),Er=(r,e)=>new Promise(t=>{let s=(e?nr.request:sr.request)({host:E,port:r,method:"HEAD",path:"/",timeout:C,...e?{rejectUnauthorized:!1,servername:Pr}:{}},a=>{a.resume(),t(a.headers[wr]==="1")});s.on("error",()=>{t(!1)}),s.on("timeout",()=>{s.destroy(),t(!1)}),s.end()}),vr=async(r,{timeoutMs:e})=>{let t=D();if(t==null)throw new Error("portless is not installed (not resolvable from node_modules)");await mr(f.execPath,[t,...r],{signal:AbortSignal.timeout(e),encoding:"utf-8",env:_(f.env)})},v=()=>f.env.PORTLESS_STATE_DIR??l(ir(),".portless"),Sr="ca.pem",Rr="ca.trusted",$=()=>l(v(),Sr),Se=()=>{try{let r=d(l(v(),Rr),"utf-8").trim();return r===""?!1:or("sha256").update(d($())).digest("hex")===r.toLowerCase()}catch{return!1}},Re=(r,e)=>new Promise(t=>{let o;try{o=d($())}catch{t({ok:!1,code:"ENOENT"});return}let s=cr.connect({host:E,port:r,servername:e,ca:[o],rejectUnauthorized:!0},()=>{let a=s.authorizationError,n=s.authorized;s.destroy(),t(n?{ok:!0}:{ok:!1,code:a?.code??a?.message??"UNKNOWN"})});s.setTimeout(C),s.once("timeout",()=>{s.destroy(),t({ok:!1,code:"ETIMEDOUT"})}),s.once("error",a=>{s.destroy(),t({ok:!1,code:a.code??"UNKNOWN"})})}),Le=()=>{try{let r=JSON.parse(d(l(v(),"routes.json"),"utf-8"));return Array.isArray(r)?r.flatMap(e=>{let{hostname:t,port:o}=e??{};return typeof t!="string"||t===""||typeof o!="number"?[]:[{name:t,port:o}]}):[]}catch{return[]}},Ae=(r={})=>{let e=r.bin===void 0?D():r.bin,t=r.run??vr,o=r.isListening??Cr,s=r.isProxyServing??Er,a=r.isServiceInstalled??br,n=r.timeoutMs??yr,p=null,u=async i=>{try{return await t(i,{timeoutMs:n}),!0}catch{return!1}},x=async()=>(p??=await u(["--version"]),p);return{binPath:()=>e,isAvailable:x,isProxyServing:async(i,g)=>!await x()||!await o(i)?!1:s(i,g),probeProxy:async(i,g)=>await o(i)?await s(i,g)?"serving":"not-portless":"daemon-down",isServiceInstalled:a,registerAlias:async(i,g)=>await x()?u(["alias",i,String(g)]):!1,removeAlias:async i=>{await x()&&await u(["alias","--remove",i])}}};export{pe as a,fr as b,ve as c,Cr as d,Er as e,$ as f,Se as g,Re as h,Le as i,Ae as j,y as k,P as l,G as m,N as n,H as o,M as p,j as q,F as r,z as s,V as t,ne as u};
2
- //# sourceMappingURL=chunk-JXLXSX7Q.js.map