@flisk-sh/flisk 1.4.6 → 1.4.7

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.
Files changed (66) hide show
  1. package/README.md +2 -2
  2. package/cli/dist/agent-runtimes/custom.js +94 -13
  3. package/cli/dist/commands/agent.js +144 -123
  4. package/cli/dist/commands/doctor.js +12 -0
  5. package/cli/dist/commands/logs.js +71 -4
  6. package/cli/dist/commands/project.js +16 -0
  7. package/cli/dist/commands/resource.js +3 -1
  8. package/cli/dist/commands/status.js +43 -24
  9. package/cli/dist/commands/ui.js +41 -2
  10. package/cli/dist/commands/up.js +54 -11
  11. package/cli/dist/components/DetailPanel.js +23 -3
  12. package/cli/dist/mcp/tools.js +9 -6
  13. package/cli/dist/types/index.js +0 -2
  14. package/cli/dist/utils/agent-format.js +18 -0
  15. package/cli/dist/utils/agent-sessions.js +122 -0
  16. package/cli/dist/utils/agent-skills.js +64 -0
  17. package/cli/dist/utils/cold-preflight.js +3 -0
  18. package/cli/dist/utils/doctor-fix-first.js +93 -0
  19. package/cli/dist/utils/doctor-report.js +3 -0
  20. package/cli/dist/utils/native-runtime.js +3 -0
  21. package/cli/dist/utils/native-service.js +2 -2
  22. package/cli/dist/utils/service-manifest.js +8 -15
  23. package/cli/dist/utils/services.js +7 -10
  24. package/cli/dist/utils/startup-report.js +45 -3
  25. package/cli/dist/utils/status-groups.js +23 -0
  26. package/cli/dist/utils/up-hints.js +42 -0
  27. package/cli/dist/utils/up-progress.js +117 -0
  28. package/cli/dist/utils/up-readiness.js +5 -0
  29. package/cli/dist-bundle/{chunk-U5FCQF42.js → chunk-6OW2GVQV.js} +1 -1
  30. package/cli/dist-bundle/chunk-B2JOM3HN.js +35 -0
  31. package/cli/dist-bundle/{chunk-R7DWXISM.js → chunk-F2DLIM75.js} +1 -1
  32. package/cli/dist-bundle/{chunk-ETXZ5LUG.js → chunk-L6O7TT3K.js} +10 -10
  33. package/cli/dist-bundle/chunk-MFY7RZ76.js +130 -0
  34. package/cli/dist-bundle/{chunk-MUDE4DZD.js → chunk-MYAYJF4Q.js} +1 -1
  35. package/cli/dist-bundle/{chunk-DPQK5TIS.js → chunk-P7GZ6NGM.js} +1 -1
  36. package/cli/dist-bundle/{chunk-IDNES5PE.js → chunk-T44DEEL6.js} +1 -1
  37. package/cli/dist-bundle/{chunk-C4UFBXRD.js → chunk-TIPSERWO.js} +1 -1
  38. package/cli/dist-bundle/{chunk-BIVAAWBX.js → chunk-VYPB7MY5.js} +1 -1
  39. package/cli/dist-bundle/{chunk-WCGBVETR.js → chunk-XHENT4KO.js} +1 -1
  40. package/cli/dist-bundle/{chunk-CAEMWNPF.js → chunk-YTBTCAWY.js} +1 -1
  41. package/cli/dist-bundle/cli.js +462 -377
  42. package/cli/dist-bundle/{cold-preflight-KUWOL2Q2.js → cold-preflight-DQVJUGBB.js} +1 -1
  43. package/cli/dist-bundle/{demo-stage-QCC4IWXR.js → demo-stage-F4YA7UL2.js} +1 -1
  44. package/cli/dist-bundle/{open-AZNX4AC5.js → open-ZUSSVPD3.js} +1 -1
  45. package/cli/dist-bundle/project-JFCTPRP5.js +2 -0
  46. package/cli/dist-bundle/{services-EY5WKPHQ.js → services-DOHDHOCT.js} +1 -1
  47. package/cli/dist-bundle/{tilt-port-owner-UBU6JWOA.js → tilt-port-owner-XODQHXHX.js} +1 -1
  48. package/cli/package.json +1 -1
  49. package/cli/templates/agent-skills/flisk-agent-ops/SKILL.md +39 -0
  50. package/cli/templates/agent-skills/flisk-build/SKILL.md +85 -0
  51. package/cli/templates/agent-skills/flisk-doctor/SKILL.md +28 -0
  52. package/cli/templates/agent-skills/flisk-recipe-ai-agent/SKILL.md +45 -0
  53. package/cli/templates/agent-skills/flisk-recipe-blog/SKILL.md +36 -0
  54. package/cli/templates/agent-skills/flisk-recipe-saas/SKILL.md +39 -0
  55. package/cli/templates/agent-skills/flisk-recipe-shop/SKILL.md +41 -0
  56. package/cli/templates/agent-skills/flisk-recipe-starters/SKILL.md +46 -0
  57. package/cli/templates/agent-skills/flisk-resource/SKILL.md +27 -0
  58. package/cli/templates/agent-skills/flisk-status/SKILL.md +25 -0
  59. package/cli/templates/agent-skills/flisk-troubleshoot/SKILL.md +227 -0
  60. package/cli/templates/agent-skills/flisk-up/SKILL.md +37 -0
  61. package/package.json +1 -1
  62. package/runtime/engine/schemas/service-schema.json +3 -15
  63. package/runtime/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +7 -1
  64. package/cli/dist-bundle/chunk-CDKEHFW6.js +0 -35
  65. package/cli/dist-bundle/chunk-GT76VMUL.js +0 -130
  66. package/cli/dist-bundle/project-5SWIGCNP.js +0 -2
@@ -1,130 +0,0 @@
1
- import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
2
- import{p as At,w as Ft}from"./chunk-MUDE4DZD.js";import{f as j,g as It,h as Lt,i as Mt,j as X,k as Ie,l as Ht,m as Wt,n as Vt,o as Ut,p as ce,q as Bt,s as Gt,t as Kt}from"./chunk-IDNES5PE.js";import{c as jt}from"./chunk-GPUJ55MI.js";import{b as B,c as qt,d as Y}from"./chunk-3J6XI3NX.js";import{a as Ot,f as _t,g as ae,j as Jt,k as zt,l as Xt,m as Yt,o as Qt,p as Le,q as Me,r as Zt,s as He,t as We,u as es,v as Ve,w as ts}from"./chunk-BIVAAWBX.js";import{C as Pt,F as bt,G as je,H as re,I as Dt,J as oe,O as Ae,P as $t,Q as ie,R as Ct,S as Nt,U as Tt,V as Et,W as Fe,a as b,ka as Rt,ma as v,t as z,w as ne,x as vt,y as xt}from"./chunk-ETXZ5LUG.js";import{C as w,D as St,L as wt,b as f,d as $,h as kt,j as F,m as U,n as J,o as Oe,q as _e,x as h,y as yt}from"./chunk-C4UFBXRD.js";import{execFileSync as xn}from"node:child_process";import{readFileSync as qo}from"node:fs";import{join as Jo}from"node:path";import{execFileSync as so,execSync as sn}from"node:child_process";import{existsSync as C,readdirSync as dn,readFileSync as G}from"node:fs";import{join as x,normalize as De,relative as $e}from"node:path";import{Command as no}from"commander";import{existsSync as Nn}from"node:fs";var le="docs/agent-hosts.md",Ue=`WebContainers cannot run Flisk: there is no Docker daemon or Tilt. Run \`flisk up\` on a machine with Docker. See ${le}.`,ss=`Docker is not reachable from this Dev Container. Mount the host Docker socket (-v /var/run/docker.sock:/var/run/docker.sock) or add the docker-outside-of-docker feature to devcontainer.json, then retry. Docker-in-docker is not supported. See ${le}.`;function Tn(){return{env:process.env,versions:process.versions,platform:process.platform,fileExists:Nn}}function ns(e=Tn()){let{env:t,versions:s,platform:n,fileExists:r}=e;return s.webcontainer?{kind:"webcontainer",canUp:!1}:t.CODESPACES==="true"?{kind:"codespaces",canUp:!0}:t.REMOTE_CONTAINERS||r("/.dockerenv")&&t.DEVCONTAINER?{kind:"devcontainer",canUp:!0}:n==="win32"?{kind:"native-windows",canUp:t.FLISK_ALLOW_NATIVE_WINDOWS==="1"}:n==="linux"&&t.WSL_DISTRO_NAME?{kind:"wsl2",canUp:!0}:{kind:"local",canUp:!0}}function rs(e){return e==="devcontainer"||e==="codespaces"}function de(e){return e.some(t=>!z(t)&&(xt(t.config?.appType)||t.config?.appType==="frontend"||t.config?.appType==="worker"))}import{existsSync as En,readFileSync as Rn}from"node:fs";import{join as On,relative as os,resolve as is,sep as _n}from"node:path";var jn=2,ue=["services/*/*"];function as(e){return e.split(_n).filter(Boolean)}function An(e,t){let s=0,n=0,r=-1,o=0;for(;n<t.length;)if(s<e.length&&e[s]!=="*"&&(e[s]==="?"||e[s]===t[n]))s++,n++;else if(s<e.length&&e[s]==="*")r=s++,o=n;else if(r!==-1)s=r+1,n=++o;else return!1;for(;s<e.length&&e[s]==="*";)s++;return s===e.length}function Fn(e,t){return e.length===t.length&&e.every((s,n)=>An(s,t[n]))}function Be(e){let t=On(e,".flisk",U);if(!En(t))return ue;let s="discovery.paths must be an array of strings";try{let n=JSON.parse(Rn(t,"utf-8"))?.discovery?.paths;if(n===void 0)return ue;if(Array.isArray(n)&&n.every(r=>typeof r=="string"))return n}catch(n){s=String(n)}return console.warn(`Ignoring discovery configuration in ${t}: ${s}; using default paths: ${ue.join(", ")}`),ue}function pe(e,t,s){let n=as(os(e,is(e,t)));return s.some(r=>{let o=as(os(e,is(e,r)));for(let i=0;i<=jn;i++){let a=n.length-i;if(a>0&&Fn(o,n.slice(0,a)))return!0}return!1})}function fi(e,t,s,n){let r=Be(e);if(pe(e,t,r))return{path:t};let o=`services/${s}/${n}`;return pe(e,o,r)?{path:o,adjustedFrom:t}:{path:t}}import{execFile as In}from"node:child_process";import{promisify as Ln}from"node:util";var Mn=Ln(In),Hn=1e4,Wn=5,cs=10*6e4,Vn=3,Un=200,Bn=async e=>{let{stdout:t,stderr:s}=await Mn("docker",e,{encoding:"utf-8",timeout:Hn,windowsHide:!0});return`${t}${s}`};function Gn(e,t){let s=Jt(t).toLowerCase(),n=e.replace(/-/g,"_").toLowerCase();return n===s||n.startsWith(`${s}_`)}function Kn(e,t=Date.now()){if(e.status==="restarting")return!0;if(e.restartCount<Wn)return!1;if(e.status==="running"){let n=Date.parse(e.startedAt);return Number.isNaN(n)||t-n<cs}let s=Date.parse(e.finishedAt??"");return e.exitCode!==0&&!Number.isNaN(s)&&t-s<cs}function qn(e,t){let s=/database "([^"]+)" does not exist/i.exec(e);return s?`database "${s[1]}" does not exist: re-run the project's database provisioning (flisk up) or create it`:/Cannot find module|MODULE_NOT_FOUND|module not found/i.test(e)?"a module is missing from the image: rebuild it (flisk up --force) after checking the resource's dependencies":/EADDRINUSE|address already in use/i.test(e)?"the port inside the container is already in use: check for two processes started by the same command":/ECONNREFUSED|connection refused|could not connect/i.test(e)?"a dependency is not reachable: check that the service it connects to is running and healthy":/\b(?:environment variable|env var)\b.*\b(?:missing|not set|required|undefined)\b/i.test(e)||/\b[A-Z][A-Z0-9_]{2,}\b (?:is )?(?:not set|required|missing)\b/.test(e)?"a required environment variable is missing: check the project's .env":t.exitCode===137?"killed (exit 137), usually out of memory: raise the container's memory limit":null}function Jn(e){let t=[];for(let s of e.split(/\r?\n/)){let[n,r,o,i,a,c]=s.split(" ");!n||r===void 0||t.push({name:n.replace(/^\//,""),restartCount:Number(r)||0,status:o??"",startedAt:i??"",exitCode:Number(a)||0,finishedAt:c??""})}return t}async function ls(e,t=[],s=Bn,n=Date.now()){let r="Crash-looping containers",o;try{o=(await s(["ps","-a","--format",'{{.ID}} {{.Label "com.docker.compose.project"}}'])).split(/\r?\n/).map(m=>m.split(" ")).filter(([m,u])=>m&&u&&Gn(u,e)).map(([m])=>m)}catch{return{name:r,didPass:!0,isSkipped:!0,message:"Docker not available - skipped crash-loop check"}}if(o.length===0)return{name:r,didPass:!0,isSkipped:!0,message:"No project containers to check"};let i;try{i=Jn(await s(["inspect","--format","{{.Name}} {{.RestartCount}} {{.State.Status}} {{.State.StartedAt}} {{.State.ExitCode}} {{.State.FinishedAt}}",...o]))}catch{return{name:r,didPass:!0,isSkipped:!0,message:"Containers changed while checking - skipped crash-loop check"}}let a=i.filter(d=>Kn(d,n));if(a.length===0)return{name:r,didPass:!0,message:"No project container is crash-looping"};let c=[],l=new Set;for(let d of a){let m="";try{m=await s(["logs","--tail",String(Vn),d.name])}catch{}let u=re(m,t).split(/\r?\n/).map(k=>k.trim().slice(0,Un)).filter(Boolean),g=qn(m,d);g&&l.add(g),c.push([`${d.name}: ${d.status}, restarted ${d.restartCount} times`,...u.map(k=>` | ${k}`)].join(`
3
- `))}return{name:r,didPass:!1,message:`${a.length} container${a.length===1?" is":"s are"} crash-looping:
4
- ${c.join(`
5
- `)}`,fix:[...l,"Read the full log: docker logs <container>"].join("; ")}}import{execFile as ar,execFileSync as cr}from"node:child_process";import{existsSync as lr,realpathSync as hs,statfsSync as dr}from"node:fs";import{homedir as ur}from"node:os";import{join as pr}from"node:path";import{promisify as fr}from"node:util";import{statfsSync as zn}from"node:fs";import{homedir as Xn}from"node:os";var fe="Disk Space",Yn=1024**3,Qn=2,Ge=10,ds=["Free disk space, then retry: flisk doctor"," docker system prune -af # unused images and build cache (needs Docker running)"," npm cache clean --force"," bun pm cache rm"," brew cleanup -s"].join(`
6
- `);function us(e=zn,t=Xn()){let s;try{let r=e(t);s=Number(r.bavail)*Number(r.bsize)/Yn}catch{return{name:fe,didPass:!0,isSkipped:!0,message:"Could not read free disk space"}}let n=`${s.toFixed(1)} GB free`;return s<Qn?{name:fe,didPass:!1,message:`${n}: Docker cannot build or start containers`,fix:ds}:s<Ge?{name:fe,didPass:!1,isWarning:!0,message:`${n}: image builds may fail`,fix:ds}:{name:fe,didPass:!0,message:n}}var Zn="Quit Docker Desktop completely (whale icon > Quit Docker Desktop), reopen it, and wait until it shows Running. If it keeps getting stuck, check free disk space. Then retry: flisk doctor";function ps(e,t=process.platform){return t!=="darwin"||!e?null:{name:"Container Runtime",didPass:!1,message:"Docker Desktop is open, but its engine is not responding",fix:Zn}}import{execFileSync as er}from"node:child_process";var Ke=(e,t)=>er(e,t,{encoding:"utf-8",stdio:"pipe",windowsHide:!0}),fs=["traefik-public","traefik-private","backend","database","infisical-network","verdaccio-network","proxy"];function qe(e){let t=e.replace(/-/g,"_");return fs.map(s=>`${t}_${s}`)}function ms(e){let t=new Set;for(let s of e)for(let n of["traefik-public","traefik-private"])s.endsWith(`_${n}`)&&t.add(s.slice(0,-(n.length+1)));return t}function Je(e,t){for(let s of fs)if(e.endsWith(`_${s}`)){let n=e.slice(0,-(s.length+1));if(t.has(n))return n}return null}function bi(e,t=Ke){let s=new Set(t("docker",["network","ls","--format","{{.Name}}"]).split(/\r?\n/).map(r=>r.trim()).filter(Boolean)),n={removed:[],kept:[]};for(let r of qe(e))if(s.has(r))try{t("docker",["network","rm",r]),n.removed.push(r)}catch(o){let i=String(o.stderr??o).trim();n.kept.push({name:r,reason:i.split(/\r?\n/)[0]??""})}return n}function me(e,t=process.platform,s=Ke){if(t==="win32")return[];let n;try{n=s("ps",["-axo","pid=,command="])}catch{return[]}let r=[];for(let o of n.split(/\r?\n/)){let i=/^\s*(\d+)\s+(.*)$/.exec(o);if(!i)continue;let a=i[2]??"",c=(a.split(/\s+/)[0]??"").split("/").at(-1),l=` -f ${e}`,d=a.indexOf(l),m=a[d+l.length];c==="tilt"&&/^\S+\s+up\s/.test(a)&&d>0&&(m===void 0||m===" ")&&r.push(Number(i[1]))}return r}function Di(e,t=process.platform,s=Ke){let n=me(e,t,s);for(let r of n)try{s("kill",["-TERM",String(r)])}catch{}return n}async function $i(e,t=8e3,s=me){let n=Date.now()+t;for(;s(e).length>0;){if(Date.now()>=n)return!1;await new Promise(r=>setTimeout(r,250))}return!0}import{execFile as tr}from"node:child_process";function ze(e){return new Promise(t=>{tr(w("tilt")??"tilt",["get","uiresources","-o","json","--port",String(e)],{timeout:15e3,maxBuffer:16*1024*1024},(s,n)=>t(s?null:n))})}function sr(e){let s=(JSON.parse(e).items??[]).filter(n=>n.status?.disableStatus?.state!=="Disabled").map(n=>n.status?.updateStatus==="not_applicable"?{...n,status:{...n.status,updateStatus:"ok"}}:n);return JSON.stringify({items:s})}function nr(e){return(JSON.parse(e).items??[]).filter(t=>t.status?.disableStatus?.state!=="Disabled").flatMap(t=>t.metadata?.name?[t.metadata.name]:[])}function rr(e,t){let s=JSON.parse(e).items??[],n=new Map(s.map(r=>[r.metadata?.name,r]));return{missing:t.filter(r=>!n.has(r)),disabled:t.filter(r=>n.get(r)?.status?.disableStatus?.state==="Disabled")}}function or(e){return e.endsWith("-typecheck")}function ir(e,t,s){let n=Yt(sr(e),t),r=n.failures.filter(i=>!or(i.name)),o={ready:r.length===0&&n.pendingCount===0&&n.okCount>0,failures:r.map(i=>({name:i.name,message:i.error||`${i.updateStatus}/${i.runtimeStatus}`})),pending:n.pendingCount,timedOut:!1,enabled:nr(e)};if(s&&s.length>0){let{missing:i,disabled:a}=rr(e,s);if(i.length>0&&(o={...o,ready:!1,pending:o.pending+i.length}),a.length>0)return{settled:!0,result:{...o,ready:!1,failures:[...o.failures,...a.map(c=>({name:c,message:"disabled in Tilt"}))]}}}return{result:o,settled:o.ready||o.failures.length>0&&o.pending===0}}async function Ri(e,t={}){let s=t.timeoutMs??Number(process.env.FLISK_UP_READY_TIMEOUT_MS??9e5),n=t.intervalMs??2e3,r=t.fetchJson??ze,o=t.deferred??Xt(),i=Date.now()+s,a={ready:!1,failures:[],pending:0,timedOut:!1,enabled:[]};for(;;){let c=await r(e);if(c)try{let l=ir(c,o,t.expected);if(a=l.result,l.settled||a.failures.length>0&&t.isStalled?.(c))return a}catch{}if(Date.now()>=i)return{...a,ready:!1,timedOut:!0};await new Promise(l=>setTimeout(l,n))}}var M=fr(ar),mr=15e3,hr=/^flisk_doctor_probe_(\d+)$/,gr=async e=>{let{stdout:t}=await M("docker",e,{encoding:"utf-8",timeout:mr,windowsHide:!0});return t};function kr(){try{return cr("docker",["info","--format","{{.ServerVersion}}"],{encoding:"utf-8",timeout:15e3,stdio:["ignore","pipe","ignore"]}),!0}catch{return!1}}function he(e){return String(e.stderr??e).trim().split(/\r?\n/)[0]??""}var yr=120;function Sr(e){let t=/^\s*(?:(?:(\d+)-)?(\d+):)?(\d+):(\d+)\s*$/.exec(e);if(!t)return null;let[,s="0",n="0",r="0",o="0"]=t;return((Number(s)*24+Number(n))*60+Number(r))*60+Number(o)}async function wr(e,t=ze,s=2,n=r=>new Promise(o=>setTimeout(o,r))){for(let r=0;r<s;r++){r>0&&await n(1e3);for(let o of e)if(await t(o)!==null)return!0}return!1}async function vr(e){if(process.platform==="win32")return"unknown";try{let{stdout:t}=await M("ps",["-p",String(e),"-o","etime="],{encoding:"utf-8",timeout:5e3}),s=Sr(t);if(s===null||s<yr)return"unknown";let{stdout:n}=await M("lsof",["-nP","-a","-p",String(e),"-iTCP","-sTCP:LISTEN","-Fn"],{encoding:"utf-8",timeout:5e3}),r=[...n.matchAll(/^n.*:(\d+)$/gm)].map(o=>Number(o[1]));return await wr(r)?"healthy":"stale"}catch{return"unknown"}}function xr(e=gr){return{colima:Pr,colimaStatus:()=>B(),dockerAnswers:kr,async listNetworks(){let t=(await e(["network","ls","--format","{{.Name}}"])).split(/\r?\n/).map(n=>n.trim()).filter(Boolean);return(await Promise.all(t.map(async n=>{try{let r=await e(["network","inspect","--format","{{len .Containers}}",n]);return{name:n,containers:Number(r.trim())||0}}catch(r){if(/no such network|not found/i.test(String(r)))return null;throw r}}))).filter(n=>n!==null)},async removeNetwork(t){try{await e(["network","rm",t])}catch(s){throw new Error(he(s))}},freeDiskGib(){try{let t=dr(ur());return Number(t.bavail)*Number(t.bsize)/1024**3}catch{return null}},async reclaimable(){let t=null;try{let n=await e(["system","df","--format","{{.Type}} {{.Reclaimable}}"]);for(let r of n.split(/\r?\n/)){let[o,i]=r.split(" ");o==="Build Cache"&&i&&(t=i.replace(/\s*\(.*\)/,"").trim())}}catch{}let s=0;try{s=(await e(["images","-q","-f","dangling=true"])).split(/\r?\n/).filter(Boolean).length}catch{}return{buildCache:t,danglingImages:s}},async prune(t){try{await e(t==="builder"?["builder","prune","-f"]:["image","prune","-f"])}catch(s){throw new Error(he(s))}},async tiltStatus(t){return vr(t)},stopProcess(t){process.kill(t,"SIGTERM")},isProcessAlive(t){try{return process.kill(t,0),!0}catch(s){return s.code==="EPERM"}}}}var gs=xr();async function Pr(e){if(e==="native-lima"){let n=Y();if(!n){let o=lr("/opt/homebrew/bin/brew")?"/opt/homebrew/bin/brew":"brew";if(await M(o,["reinstall","lima"],{encoding:"utf-8",timeout:3e5}),n=Y(),!n)throw new Error("brew reinstall lima did not provide a native limactl")}let r=n?{...process.env,PATH:`${n}:${process.env.PATH??""}`}:process.env;await M("colima",["start"],{encoding:"utf-8",timeout:3e5,env:r});return}let t=Y(),s=t?{...process.env,PATH:`${t}:${process.env.PATH??""}`}:process.env;await M("colima",[e],{encoding:"utf-8",timeout:3e5,env:s})}async function ks(e,t=gs,s={}){let n=[],r=[];for(let u of s.tiltPids??[])await t.tiltStatus(u)==="stale"?n.push({id:`stop-tilt:${u}`,check:"Tilt processes",scope:"project",command:`kill -TERM ${u}`,status:"planned",reason:"this project's Tilt is running but its UI does not answer"}):r.push(u);let o=r.length>0?`Tilt is running for this project (pid ${r.join(", ")}); run flisk down first`:void 0,i=t.colimaStatus(),a=qt(i,t.dockerAnswers());if(a==="start")n.push({id:"colima:start",check:"Colima",scope:"host",command:"colima start",status:"planned",reason:i.problem??"the Colima VM is not running"});else if(a==="restart")n.push({id:"colima:restart",check:"Colima",scope:"host",command:"colima restart",status:"planned",reason:"the Colima VM is running but its Docker daemon does not answer"});else if(a==="native-lima"){let u=Y();n.push({id:"colima:native-lima",check:"Colima",scope:"host",command:u?`PATH="${u}:$PATH" colima start`:"brew reinstall lima",status:"planned",reason:u?`the colima CLI on this PATH cannot see the VM; a native limactl is installed in ${u}`:"the colima CLI on this PATH cannot see the VM; lima is installed as an x86_64 build (brew reinstall lima)"})}let c=await t.listNetworks(),l=t.freeDiskGib();if(l!==null&&l<Ge){let{buildCache:u,danglingImages:g}=await t.reclaimable(),k=`${l.toFixed(1)} GB free`;u&&!/^0(\.0+)?\s*B$/i.test(u)&&n.push({id:"prune:builder",check:"Disk Space",scope:"host",command:"docker builder prune -f",status:"planned",reason:`${k}; ${u} of build cache reclaimable`}),g>0&&n.push({id:"prune:images",check:"Disk Space",scope:"host",command:"docker image prune -f",status:"planned",reason:`${k}; ${g} untagged image${g===1?"":"s"}`})}let d=new Set(e?qe(e):[]),m=ms(c.map(u=>u.name));for(let u of c){let g=hr.exec(u.name),k,S,D="unused network";if(g)k="probe",S="Orphaned probe networks";else if(d.has(u.name))k="project",S="Docker networks";else if(s.machine&&Je(u.name,m))k="machine",S="Docker networks",D=`unused network of project "${Je(u.name,m)}"`;else continue;let N={id:`remove-network:${u.name}`,check:S,scope:k,command:`docker network rm ${u.name}`,status:"planned",reason:D};u.containers>0?(N.status="skipped",N.reason=`${u.containers} container(s) still attached`):k==="project"&&o?(N.status="skipped",N.reason=o):g&&t.isProcessAlive(Number(g[1]))&&(N.status="skipped",N.reason=`probe of a running process (pid ${g[1]})`),n.push(N)}return n}async function ys(e,t,s=gs){let n=[];for(let r of e){if(r.status!=="planned"||t.dryRun){n.push(r);continue}if(!t.confirm){n.push({...r,status:"skipped",reason:"confirmation required; run in a terminal or pass --yes"});continue}let o=!1;try{o=await t.confirm(r)}catch{}if(!o){n.push({...r,status:"skipped",reason:"not confirmed"});continue}try{if(r.id.startsWith("prune:")){await s.prune(r.id==="prune:builder"?"builder":"images"),n.push({...r,status:"applied",reason:"pruned"});continue}if(r.id.startsWith("colima:")){let i=r.id.slice(7);if(i==="start"||i==="restart"){await s.colima(i),n.push({...r,status:"applied",reason:`colima ${i} ran`});continue}try{await s.colima("native-lima"),n.push({...r,status:"applied",reason:"native lima repaired and colima started"})}catch(a){n.push({...r,status:"failed",reason:he(a)})}continue}if(r.id.startsWith("stop-tilt:")){s.stopProcess(Number(r.id.slice(10))),n.push({...r,status:"applied",reason:"stopped"});continue}await s.removeNetwork(r.id.slice(15)),n.push({...r,status:"applied",reason:"removed"})}catch(i){n.push({...r,status:"failed",reason:he(i)})}}return n}function Ss(e){return e.some(t=>t.status==="failed")}async function br(e){try{let{stdout:t}=await M(e,["--version"],{encoding:"utf-8",timeout:5e3,windowsHide:!0});return t.trim().split(/\r?\n/)[0]||null}catch{return null}}async function Dr(e=St("flisk"),t=br){let s=new Set,n=[];for(let r of e){let o=r;try{o=hs(r)}catch{}s.has(o)||(s.add(o),n.push({path:r,version:await t(r)}))}return n}async function Xe(e){let t=e??await Dr();if(t.length<=1)return{name:"Flisk installs",didPass:!0,message:"One flisk install on PATH"};let s=t.map(r=>`${r.path} (${r.version??"version unknown"})`).join(", "),n=new Set(t.map(r=>r.version));return{name:"Flisk installs",didPass:!1,isWarning:!0,message:`${t.length} flisk installs on PATH${n.size>1?" with different versions":""}: ${s}`,fix:"Keep one: remove the others, or run flisk upgrade. The first on PATH is the one your shell runs; tools that launch /usr/local/bin/flisk directly may use another"}}function ws(e,t=me){let s=new Set([e]);try{s.add(hs(e))}catch{}let n=new Set;for(let r of s)for(let o of t(pr(r,".flisk",".flisk-out","Tiltfile")))n.add(o);return[...n]}import{spawn as $r}from"node:child_process";import{appendFileSync as Cr,existsSync as Cs,mkdirSync as Nr,readFileSync as Ns,rmSync as Ts,writeFileSync as vs}from"node:fs";import{connect as Tr}from"node:net";import{join as H}from"node:path";var Ze=H(".flisk","native"),Er="run.json",Es=6e4,Rr=1e3,Rs=5e3,xs=5,Or=1e4;function ge(e,t=""){return`http://localhost:${e}${t}`}function _r(e,t,s=()=>{}){return e.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g,(n,r,o)=>{let i=t[r];return i!==void 0&&i!==""?i:o!==void 0?o:(s(r),"")})}function Ye(e){let t=new Set(e.map(o=>o.name)),s=new Map(e.map(o=>[o.name,o.dependsOn.filter(i=>t.has(i))])),n=new Set,r=[];for(;s.size>0;){let o=[...s.entries()].filter(([,i])=>i.every(a=>n.has(a))).map(([i])=>i).sort();if(o.length===0)throw new Error(`circular dependsOn between ${[...s.keys()].sort().join(", ")}`);for(let i of o)s.delete(i),n.add(i);r.push(o)}return r}function et(e,t=process.env){let s=[],n=[],r=new Set(e.map(a=>a.name)),o=new Map,i=e.map(a=>{let c=a.config??{},l=Array.isArray(c.dependsOn)?c.dependsOn:[];for(let g of l)r.has(g)||s.push(`service "${a.name}" dependsOn "${g}", which is not a service with a command in this project. Shared infrastructure such as a database or the gateway still needs Docker: add a "${g}" service with a command, or remove it from dependsOn.`);let d=typeof c.port=="number"?c.port:void 0;if(d!==void 0){let g=o.get(d);g?s.push(`services "${g}" and "${a.name}" both use port ${d}`):o.set(d,a.name)}let m={};for(let[g,k]of Object.entries(c.env??{}))m[g]=_r(String(k),{...t,...m},S=>n.push(`${a.name}: ${S}`));d!==void 0&&(m.PORT=String(d));let u=typeof c.healthCheckPath=="string"?c.healthCheckPath:"";return{name:a.name,command:String(c.command),cwd:a.path,port:d,env:m,dependsOn:l.filter(g=>r.has(g)),healthUrl:d!==void 0&&u?ge(d,u):void 0,url:d!==void 0?ge(d):void 0,restart:c.restart!=="no"}});try{Ye(i)}catch(a){s.push(a instanceof Error?a.message:String(a))}return{services:i,errors:s,missingVars:n}}function Os(e,t=w,s=Cs){let n=[];for(let r of e){let o=vt(r.command);if(!o||["cd",".","source","true",":"].includes(o))continue;(o.includes("/")?s(H(r.cwd,o)):t(o))||n.push({name:r.name,program:o})}return n}function tt(e,t=1e3){return new Promise(s=>{let n=Tr({host:"localhost",port:e}),r=o=>{n.destroy(),s(o)};n.setTimeout(t,()=>r(!1)),n.once("connect",()=>r(!0)),n.once("error",()=>r(!1))})}async function jr(e){try{let t=await fetch(e,{signal:AbortSignal.timeout(2e3),redirect:"manual"});return await t.body?.cancel(),t.status<400}catch{return!1}}async function Ps(e,t){let s=Date.now()+(t.timeoutMs??Es),n=t.intervalMs??250;if(!e.healthUrl&&e.port===void 0)return await Qe(Rr),t.isAlive();for(;Date.now()<s;){if(!t.isAlive())return!1;if(e.healthUrl?await jr(e.healthUrl):await tt(e.port))return!0;await Qe(n)}return!1}function Qe(e){return new Promise(t=>setTimeout(t,e))}function ke(e){return H(e,Ze,Er)}function _s(e,t){return H(e,Ze,"logs",`${t}.log`)}function ye(e){try{let t=JSON.parse(Ns(ke(e),"utf-8"));return t&&typeof t.pid=="number"&&t.services?t:null}catch{return null}}function Ar(e,t){let s=H(e,Ze);Nr(H(s,"logs"),{recursive:!0});let n=H(s,".gitignore");Cs(n)||vs(n,`*
7
- `),vs(ke(e),`${JSON.stringify(t,null,2)}
8
- `)}function I(e){try{return process.kill(e,0),!0}catch(t){return t.code==="EPERM"}}function bs(e,t){try{process.kill(-e,t)}catch{try{process.kill(e,t)}catch{}}}async function js(e,t){let s=Date.now()+t;for(;Date.now()<s;){if(!I(e))return!0;await Qe(100)}return!I(e)}async function As(e,t=Rs){bs(e,"SIGTERM"),await js(e,t)||bs(e,"SIGKILL")}async function Yi(e,t={}){let s=ye(e);if(!s)return{wasRunning:!1,services:[]};let n=t.timeoutMs??Rs*2;if(s.pid!==process.pid&&I(s.pid)){try{process.kill(s.pid,"SIGTERM")}catch{}await js(s.pid,n)}for(let r of Object.values(s.services))r.pid&&I(r.pid)&&await As(r.pid);return Ts(ke(e),{force:!0}),{wasRunning:!0,services:Object.keys(s.services),supervisorPid:s.pid}}function Qi(e,t){let s=ye(e),n=s!==null&&I(s.pid);return Object.fromEntries(t.map(r=>{let o=s?.services[r];return!n||!o?[r,{status:"unknown",reason:"not running"}]:!o.pid||!I(o.pid)?[r,{status:"pending",reason:"restarting"}]:o.ready?[r,{status:"ready"}]:[r,{status:"pending",reason:"starting"}]}))}function Zi(e,t,s){let n=s.sinceMs===void 0?null:(s.now??Date.now())-s.sinceMs,r=[];for(let o of t){let i;try{i=Ns(_s(e,o),"utf-8")}catch{continue}for(let a of i.split(`
9
- `)){if(!a)continue;let c=/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z) (.*)$/.exec(a),l=c?.[1]??null;n!==null&&(!l||Date.parse(l)<n)||r.push({time:l,resource:o,level:null,source:null,text:c?.[2]??a})}}return r.sort((o,i)=>(o.time??"").localeCompare(i.time??"")),r.slice(-s.tail)}var Ds=[f.cyan,f.magenta,f.green,f.yellow,f.blue,f.red],$s=class{projectRoot;events;options;services=new Map;startedAt=new Date().toISOString();stopping=!1;constructor(t,s,n={},r={}){this.projectRoot=t,this.events=n,this.options=r;for(let o of s)this.services.set(o.name,{plan:o,startedAt:0,quickCrashes:0,gaveUp:!1,isReady:!1})}get prefixWidth(){return Math.max(...[...this.services.keys()].map(t=>t.length))}async start(){this.saveState();let t=[...this.services.values()].map(r=>r.plan),s=new Map,n=[];for(let r of Ye(t).flat()){let o=this.services.get(r);s.set(r,(async()=>{let i=await Promise.all(o.plan.dependsOn.map(c=>s.get(c)));if(!i.every(Boolean)){let c=o.plan.dependsOn.filter((l,d)=>!i[d]);return n.push({name:r,reason:`not started: ${c.join(", ")} did not start`}),!1}if(this.stopping)return!1;this.spawn(o);let a=await Ps(o.plan,{isAlive:()=>!this.stopping&&!o.gaveUp,timeoutMs:this.options.readyTimeoutMs});return a?(o.isReady=!0,this.saveState(),this.events.onNote?.(r,"ready")):this.stopping||n.push({name:r,reason:o.gaveUp?"kept exiting; see its output above":`not ready after ${Math.round((this.options.readyTimeoutMs??Es)/1e3)}s (${o.plan.healthUrl?`no answer from ${o.plan.healthUrl}`:`nothing listening on port ${o.plan.port}`})`}),a})())}return await Promise.all(s.values()),n}async stop(){if(this.stopping)return;this.stopping=!0;let t=Ye([...this.services.values()].map(s=>s.plan)).flat().reverse();for(let s of t){let n=this.services.get(s);n.restartTimer&&clearTimeout(n.restartTimer);let r=n.child?.pid;r&&n.child?.exitCode===null&&n.child.signalCode===null&&await As(r)}Ts(ke(this.projectRoot),{force:!0})}pidOf(t){return this.services.get(t)?.child?.pid}spawn(t){let{plan:s}=t;t.startedAt=Date.now();let n=$r(s.command,{cwd:s.cwd,env:{...this.options.baseEnv??process.env,...s.env},shell:!0,detached:!0,stdio:["ignore","pipe","pipe"]});t.child=n,this.events.onNote?.(s.name,`started: ${s.command}${n.pid?` (pid ${n.pid})`:""}`),this.pipeLines(s.name,n.stdout,"stdout"),this.pipeLines(s.name,n.stderr,"stderr"),n.once("error",r=>{this.events.onNote?.(s.name,`could not start: ${r.message}`)}),n.once("exit",(r,o)=>this.onExit(t,n,r,o)),this.saveState()}onExit(t,s,n,r){if(this.stopping||t.child!==s)return;t.isReady=!1,this.saveState();let o=r?`was stopped by ${r}`:`exited with code ${n}`;if(!t.plan.restart){t.gaveUp=!0,this.events.onNote?.(t.plan.name,`${o}; not restarting ("restart": "no")`);return}let i=Date.now()-t.startedAt;if(t.quickCrashes=i<Or?t.quickCrashes+1:1,t.quickCrashes>=xs){t.gaveUp=!0,this.events.onNote?.(t.plan.name,`${o}; it exited ${xs} times in a row, so it is not restarted. Fix it, then run flisk up again.`);return}let a=(this.options.restartDelayMs??1e3)*2**(t.quickCrashes-1);this.events.onNote?.(t.plan.name,`${o}; restarting in ${a/1e3}s`),t.restartTimer=setTimeout(()=>{this.stopping||(this.spawn(t),Ps(t.plan,{isAlive:()=>!this.stopping&&t.child?.exitCode===null,timeoutMs:this.options.readyTimeoutMs}).then(c=>{!c||this.stopping||(t.isReady=!0,this.saveState(),this.events.onNote?.(t.plan.name,"ready again"))}))},a)}pipeLines(t,s,n){if(!s)return;let r=_s(this.projectRoot,t),o="",i=a=>{try{Cr(r,`${new Date().toISOString()} ${a}
10
- `)}catch{}this.events.onLine?.(t,a,n)};s.setEncoding?.("utf-8"),s.on("data",a=>{let c=(o+a).split(/\r?\n/);o=c.pop()??"";for(let l of c)i(l)}),s.on("end",()=>{o&&i(o),o=""})}saveState(){let t={};for(let[s,n]of this.services)t[s]={pid:n.child?.pid,port:n.plan.port,url:n.plan.url,ready:n.isReady};Ar(this.projectRoot,{pid:process.pid,startedAt:this.startedAt,services:t})}};function ea(e,t,s){return(Ds[t%Ds.length]??f.cyan)(`${e.padEnd(s)} | `)}function Fs(e){return{name:"Runtime",didPass:!0,message:`Local processes: ${b(e.length,"service")} declare a command, so Docker, Compose and Tilt are not needed`}}function Is(e,t=w){let s=Os(et(e.filter(z)).services,t);return s.length===0?{name:"Service commands",didPass:!0,message:"Every service command starts a program that is installed"}:{name:"Service commands",didPass:!1,message:`${b(s.length,"service")} run a program that is not on PATH:
11
- ${s.map(({name:n,program:r})=>`${n}: ${r}`).join(`
12
- `)}`,fix:'Install the program, or change "command" in the service.json'}}function Ls(e){let t=new Set(e.map(n=>n.name)),s=e.flatMap(n=>{let r=n.config?.dependsOn;return(Array.isArray(r)?r:[]).filter(o=>!t.has(o)).map(o=>`${n.name} dependsOn "${o}", which is not a service of this project`)});return s.length===0?{name:"Service dependencies",didPass:!0,message:"Every dependsOn names a service of this project"}:{name:"Service dependencies",didPass:!1,message:s.join(`
13
- `),fix:"Add the missing service with a command, or remove it from dependsOn. A database or the gateway still needs the Docker path."}}async function Ms(e,t,s){let n=ye(e);if(!n||!I(n.pid))return{name:"Service Health",didPass:!0,isSkipped:!0,message:"Not running - start it with flisk up"};let r=[];for(let o of et(t.filter(z)).services){if(o.port===void 0)continue;(o.healthUrl?await fetch(o.healthUrl,{signal:AbortSignal.timeout(s)}).then(a=>a.status<400).catch(()=>!1):await tt(o.port,s))||r.push(`${o.name}: no answer from ${o.healthUrl??ge(o.port)}`)}return r.length===0?{name:"Service Health",didPass:!0,message:"Every running service answers"}:{name:"Service Health",didPass:!1,message:r.join(`
14
- `),fix:"Read the service output in .flisk/native/logs/<service>.log, or the flisk up terminal"}}var st=1;function Fr(e,t=[]){return{schemaVersion:st,data:e,errors:t}}function Ir(e){return e instanceof wt?{error:{code:"COMMAND_FAILED",message:e.message,...e.suggestions.length>0?{suggestions:e.suggestions}:{}},exitCode:e.exitCode}:{error:{code:"INTERNAL",message:e instanceof Error?e.message:String(e)},exitCode:2}}function ca(e){let t=Ir(e);console.log(JSON.stringify(Fr(null,[t.error]))),console.error(t.error.message),process.exit(t.exitCode)}async function Ws(e,t){let s=[],n=[],r=o=>{o.status==="fulfilled"?s.push(o.value):n.push({code:"INTERNAL",message:o.reason instanceof Error?o.reason.message:String(o.reason)})};for(let o of await Promise.allSettled(e.map(i=>Promise.resolve().then(i))))r(o);for(let o of t)for(let i of await Promise.allSettled([Promise.resolve().then(o)]))r(i);return{checks:s,errors:n}}function Hs(e){let t=e.find(s=>s.name===$);return t?t.didPass:null}function L(e,t,s=[],n,r){return{schemaVersion:st,data:{ready:s.length===0&&e.every(o=>o.didPass||o.isSkipped||o.isWarning),inProject:t,checks:e,...r?{host:{...r,containerRuntimeReachable:Hs(e),canUp:r.canUp&&Hs(e)!==!1}}:{},...n!==void 0?{ports:{http:{requested:n?.requested.ingressHttp??80,chosen:n?.ingressHttp??null,explicit:n?.explicit.ingressHttp??!!process.env.FLISK_HTTP_PORT,reason:n?.reason.ingressHttp??"port planning failed"},https:{requested:n?.requested.ingressHttps??443,chosen:n?.ingressHttps??null,explicit:n?.explicit.ingressHttps??!!process.env.FLISK_HTTPS_PORT,reason:n?.reason.ingressHttps??"port planning failed"},postgres:{requested:n?.requested.postgres??5432,chosen:n?.postgres??null,explicit:n?.explicit.postgres??!!process.env.FLISK_POSTGRES_PORT,reason:n?.reason.postgres??"port planning failed"}}}:{}},errors:s}}function Vs(e){return e.errors.length>0?2:e.data.ready?0:1}import{existsSync as rt,readFileSync as Us}from"node:fs";import{dirname as Lr,join as nt,normalize as Mr}from"node:path";function Hr(e){let t=new Set,s=/^load\s*\(([\s\S]*?)\)/gm;for(let n of e.matchAll(s)){let r=n[1]??"",o=/["']([^"']+)["']/g,i=Array.from(r.matchAll(o),a=>a[1]);for(let a of i.slice(1))a.startsWith("_")&&t.add(a)}return Array.from(t).sort()}function Bs(e,t,s){return t==="ext://flisk-cli"?nt(s,".flisk",".flisk-out","flisk-cli-ext","Tiltfile"):t.startsWith(".")||!t.includes("://")&&!t.startsWith("@")?Mr(nt(Lr(e),t)):null}function Wr(e,t,s){let n=[],r=/^load\s*\(([\s\S]*?)\)/gm;for(let o of t.matchAll(r)){let c=(o[1]??"").match(/["']([^"']+)["']/)?.[1];if(!c)continue;let l=Bs(e,c,s);l&&(rt(l)||n.push(`${e}: ${c} -> ${l}`))}return n}function Vr(e,t,s){let n=[],r=/^load\s*\(([\s\S]*?)\)/gm;for(let o of t.matchAll(r)){let c=(o[1]??"").match(/["']([^"']+)["']/)?.[1];if(c){let l=Bs(e,c,s);l&&n.push(l)}}return n}function Ur(e,t){let s=new Set,n=[e];for(;n.length>0;){let r=n.pop();if(!r||s.has(r)||!rt(r))continue;s.add(r);let o=Us(r,"utf-8");for(let i of Vr(r,o,t))s.has(i)||n.push(i)}return Array.from(s).sort()}function Gs(){let e=h()??process.cwd(),t=nt(e,".flisk",".flisk-out","Tiltfile");if(!rt(t))return{name:"Starlark Load Exports",didPass:!0,isSkipped:!0,message:"No generated Tiltfile to check"};let s=[],n=[],r=Ur(t,e);for(let i of r){let a=Us(i,"utf-8"),c=Hr(a);c.length>0&&s.push(`${i}: ${c.join(", ")}`);for(let l of Wr(i,a,e))n.push(l)}if(s.length===0&&n.length===0)return{name:"Starlark Load Exports",didPass:!0,message:`${b(r.length,"reachable Starlark file")} ${r.length===1?"has":"have"} valid load exports and paths`};let o=[];return s.length>0&&o.push(`Private exported symbols:
15
- ${s.join(`
16
- `)}`),n.length>0&&o.push(`Missing relative load targets:
17
- ${n.join(`
18
- `)}`),{name:"Starlark Load Exports",didPass:!1,message:`Starlark load() issues will stop Tilt before services start:
19
- ${o.join(`
20
-
21
- `)}`,fix:"Regenerate with a current Flisk (`flisk config regenerate`). If the issue remains, rename loaded symbols without leading underscores and update stale load paths."}}import{readFileSync as Br}from"node:fs";import{join as Gr}from"node:path";function Ks(e){let t=e.trim().match(/(\d+)\.(\d+)(?:\.(\d+))?/);return t?[Number(t[1]),Number(t[2]),Number(t[3]??0)]:null}function Kr(e){try{return JSON.parse(Br(Gr(e,".flisk",U),"utf-8"))?.minFliskVersion}catch{return}}function qs(e,t){let s=Kr(e);if(s===void 0)return{status:"none"};let n=typeof s=="string"?Ks(s):null;if(!n||!/^\d+\.\d+\.\d+$/.test(String(s).trim()))return{status:"malformed",message:`.flisk/project.json minFliskVersion ${JSON.stringify(s)} is not a version like "1.3.80"`,fix:'Set minFliskVersion to a MAJOR.MINOR.PATCH string, for example "1.3.80".'};let r=Ks(t),o=r!==null&&(()=>{for(let[a,c]of n.entries()){let l=r[a]??0;if(l!==c)return l>c}return!0})(),i=n.join(".");return o?{status:"ok",required:i}:{status:"too-old",required:i,message:`flisk ${t} is older than the ${i} this project requires (minFliskVersion)`,fix:"Run: flisk upgrade"}}import{existsSync as qr,readdirSync as Jr}from"node:fs";import{join as Js}from"node:path";function zs(e,t=0){if(qr(Js(e,kt)))return!0;if(t>=2)return!1;try{return Jr(e,{withFileTypes:!0}).some(s=>s.isDirectory()&&!s.name.startsWith(".")&&s.name!=="node_modules"&&zs(Js(e,s.name),t+1))}catch{return!1}}function Xs(e=process.cwd()){let t=h(e)??(zs(e)?e:null);if(!t)return null;try{let{resources:s}=Rt(t);return ne(s)?{projectRoot:t,resources:s}:null}catch{return null}}import{relative as zr}from"node:path";function Se(e,t){return zr(e,t.configPath)||t.configPath}function Ys(e){let t=e.config?.dependsOn;return t===void 0?[]:Array.isArray(t)&&t.every(s=>typeof s=="string")?t:null}function Xr(e){let t=new Set(e.map(a=>a.name)),s=new Map(e.map(a=>[a.name,(Ys(a)??[]).filter(c=>t.has(c))])),n=new Map,r=new Map,o=[],i=a=>{r.set(a,"visiting"),o.push(a);for(let c of s.get(a)??[])if(r.get(c)==="visiting"){let l=o.slice(o.indexOf(c)),d=l.indexOf([...l].sort()[0]??c),m=[...l.slice(d),...l.slice(0,d)];n.set(m.join(">"),[...m,m[0]??c])}else r.has(c)||i(c);o.pop(),r.set(a,"done")};for(let a of[...s.keys()].sort())r.has(a)||i(a);return[...n.values()]}function we(e=h()??process.cwd(),t=v(e)){let s=t.flatMap(n=>Ys(n)===null?[`${Se(e,n)} dependsOn ${JSON.stringify(n.config?.dependsOn)}: expected an array of service names`]:[]);return s.length===0?{name:"dependsOn",didPass:!0,message:"Every dependsOn is a list of service names"}:{name:"dependsOn",didPass:!1,message:`${b(s.length,"invalid dependsOn","invalid dependsOn")}:
22
- ${s.join(`
23
- `)}`,fix:'Write "dependsOn" as an array of service names, for example ["postgres", "api"]'}}function ve(e=h()??process.cwd(),t=v(e)){let s=new Map(t.map(o=>[o.name,o])),n=Xr(t);if(n.length===0)return{name:"Circular dependencies",didPass:!0,message:"No service depends on itself through dependsOn"};let r=n.map(o=>{let i=s.get(o[0]??""),a=o[1]??"";return`${i?Se(e,i):o[0]} dependsOn "${a}": circular dependency ${o.join(" -> ")}`});return{name:"Circular dependencies",didPass:!1,message:`${n.length} ${n.length===1?"circular dependency":"circular dependencies"}:
24
- ${r.join(`
25
- `)}`,fix:'Remove one "dependsOn" entry in each cycle so the services no longer wait on each other, or move what they share into a third service'}}function xe(e=h()??process.cwd(),t=v(e)){let s=t.flatMap(n=>{let r=n.config?.port;return r===void 0?[]:typeof r=="number"&&Number.isInteger(r)&&r>=1&&r<=65535?[]:[`${Se(e,n)} port ${JSON.stringify(r)}: not an integer from 1 to 65535`]});return s.length===0?{name:"Service ports",didPass:!0,message:"Every declared port is valid"}:{name:"Service ports",didPass:!1,message:`${b(s.length,"invalid port")}:
26
- ${s.join(`
27
- `)}`,fix:'Set "port" to the port the service listens on, an integer from 1 to 65535'}}function Pe(e=h()??process.cwd(),t=!1,s=v(e)){let n=[],r=[];for(let i of s){let a=i.config?.schemaVersion;if(a===Fe)continue;let c=Se(e,i);a===void 0?n.push(`${c} schemaVersion: missing`):r.push(`${c} schemaVersion ${JSON.stringify(a)}: this flisk supports ${Fe}`)}if(n.length===0&&r.length===0)return{name:"schemaVersion",didPass:!0,message:"Every service.json has a supported schemaVersion"};let o=[...n.length>0?["Run `flisk config migrate` to add the missing schemaVersion"]:[],...r.length>0?["A version this flisk does not support cannot be migrated: upgrade flisk"]:[]];return{name:"schemaVersion",didPass:!1,isWarning:!t,message:`${b(n.length+r.length,"service.json file")} without a supported schemaVersion:
28
- ${[...n,...r].join(`
29
- `)}`,fix:o.join(". ")}}function Fa(e,t){let s=Pe(e,!1,t);s.didPass||(console.warn(f.yellow(`\u26A0 ${s.message}`)),s.fix&&console.warn(f.gray(` ${s.fix}`)))}function Ia(e,t={},s=process.exit){let n=t.resources??v(e),r=[X(e,n),we(e,n),ve(e,n),xe(e,n)].filter(i=>!i.didPass);if(r.length===0)return;let o=r.map(i=>`${i.message}${i.fix?`
30
- Fix: ${i.fix}`:""}`).join(`
31
- `);t.onInvalid?.(o);for(let i of r)console.error(f.red(`\u2717 ${i.message}`)),i.fix&&console.error(f.gray(` Fix: ${i.fix}`));s(1)}import{execFileSync as Q}from"node:child_process";import{lookup as Yr}from"node:dns/promises";var Qs="Windows did not resolve *.flisk.localhost to 127.0.0.1. Browser URLs with hostnames will fail until you fix hosts or use the 127.0.0.1 URLs printed by flisk networks.";function Qr(e){return e.length>0&&e.every(t=>t==="127.0.0.1"||t==="::1")}function Zr(e){return e.split(/\r?\n/).flatMap(t=>{let s=t.match(/^\s*(\d+)\s+(\d+)(?:\s|$)/);return s?[[Number(s[1]),Number(s[2])]]:[]})}function eo(e,t,s){let n=s.replace(/-/g,"_").toLowerCase();return e.filter(r=>{let o=t.find(a=>a.publishedPorts.includes(r));if(!o||!n)return!0;let i=o.name.toLowerCase();return!i.startsWith(`${n}_`)&&!i.startsWith(`${n}-`)})}function to(){return w("docker")}function Zs(){let e=to();if(!e)return{name:"Windows Runtime Tools",didPass:!1,message:"Docker is not running or not installed.",fix:`1. Install Docker Desktop: https://www.docker.com/products/docker-desktop/
32
- 2. Start Docker Desktop and wait for "Running" status
33
- 3. Enable WSL2 backend in Settings \u2192 General
34
- 4. Enable WSL Integration for Ubuntu in Settings \u2192 Resources \u2192 WSL Integration
35
- 5. Verify: docker version && docker compose version
36
- Docs: https://docs.docker.com/desktop/wsl/`};if(!w("tilt"))return{name:"Windows Runtime Tools",didPass:!1,message:"tilt.exe not found on PATH.",fix:`Install Tilt:
37
- curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
38
- Or download from: https://docs.tilt.dev/install.html
39
- Verify: tilt version`};let t=w("bun");return((process.argv[1]??"").toLowerCase().includes("node_modules")?w("node"):!0)?{name:"Windows Runtime Tools",didPass:!0,message:t?`Docker (${e}), Tilt, and Bun found on PATH`:`Docker (${e}) and Tilt found on PATH; \u26A0\uFE0F Bun was not found (generated services need Bun)
40
- Fix: curl -fsSL https://bun.sh/install | bash
41
- Verify: bun --version`}:{name:"Windows Runtime Tools",didPass:!1,message:"node.exe not found on PATH for this npm installation.",fix:`Install Node.js 22.12+: https://nodejs.org/
42
- Or use Bun (includes Node.js): https://bun.sh/install`}}function en(){try{return Q(w("docker")??"docker",["info","--format","{{.OSType}}"],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim().toLowerCase()==="windows"?{name:"Docker container mode",didPass:!1,message:"Docker Desktop is using Windows containers. Flisk requires Linux containers.",fix:`1. Open Docker Desktop \u2192 Settings \u2192 General
43
- 2. Ensure "Use WSL 2 instead of Hyper-V" is checked
44
- 3. Click "Apply & Restart"
45
- 4. Verify: docker info --format '{{.OSType}}' (should print "linux")
46
- Docs: https://docs.docker.com/desktop/wsl/`}:{name:"Docker container mode",didPass:!0,message:"Docker Desktop is using Linux containers"}}catch{return{name:"Docker container mode",didPass:!1,message:"Docker daemon is not responding.",fix:`1. Start Docker Desktop and wait for "Running" status
47
- 2. Verify: docker version
48
- 3. If stuck: restart Docker Desktop or run \`systemctl restart docker\` in WSL2
49
- Docs: https://docs.docker.com/desktop/troubleshoot/`}}}async function tn(){let e=[],t=[];try{let l=Q("netsh",["interface","ipv4","show","excludedportrange","protocol=tcp"],{encoding:"utf-8",timeout:1e4,windowsHide:!0});t=Zr(l)}catch{e.push(`Could not inspect Windows Hyper-V excluded TCP port ranges.
50
- Fix: run as Administrator: netsh interface ipv4 show excludedportrange protocol=tcp
51
- If netsh fails, this check is skipped.`)}let s=[80,443,5432],n=s.filter(l=>t.some(([d,m])=>l>=d&&l<=m));n.length>0&&e.push(`\u26A0\uFE0F Windows reserved port${n.length>1?"s":""} ${n.join(", ")} for Hyper-V/WSL.
52
- Fix 1 (reset reserved ranges): run as Administrator: net stop winnat && net start winnat
53
- Fix 2 (change Flisk ports): set FLISK_HTTP_PORT, FLISK_HTTPS_PORT, FLISK_POSTGRES_PORT in project config
54
- Verify: flisk doctor
55
- Docs: https://github.com/flisk-sh/flisk/blob/main/docs/wsl2.md#troubleshooting`);let r=[];for(let l of s)await Zt(l)==="in-use"&&r.push(l);let o=[];try{let l=Q(w("docker")??"docker",["ps","--format","{{.Names}}\\t{{.Ports}}"],{encoding:"utf-8",timeout:1e4,windowsHide:!0});o=zt(l,s)}catch{}let i=h()?oe():"",a=eo(r,o,i);if(a.length>0)try{let l=Q("powershell.exe",["-NoProfile","-Command",`$ports = '${a.join(",")}'.Split(','); foreach ($port in $ports) { Get-NetTCPConnection -LocalPort ([int]$port) -State Listen -ErrorAction SilentlyContinue | ForEach-Object { $connection = $_; $service = Get-CimInstance Win32_Service | Where-Object { $_.ProcessId -eq $connection.OwningProcess } | Select-Object -First 1; if ([int]$port -eq 80 -and ($service.Name -eq 'W3SVC' -or $connection.OwningProcess -eq 4)) { '80: IIS / World Wide Web Publishing (HTTP.sys)' } elseif ($service) { '{0}: {1}' -f $port, $service.DisplayName } else { '{0}: PID {1}' -f $port, $connection.OwningProcess } } }`],{encoding:"utf-8",timeout:1e4,windowsHide:!0}).trim();e.push(`\u26A0\uFE0F Ports ${a.join(", ")} are already listening${l?`:
56
- ${l}`:"."}
57
- Fix: stop the conflicting service or change Flisk ports (FLISK_HTTP_PORT, etc.)
58
- Verify: flisk doctor
59
- Docs: https://github.com/flisk-sh/flisk/blob/main/docs/wsl2.md#troubleshooting`)}catch{e.push(`\u26A0\uFE0F Ports ${a.join(", ")} are already in use.
60
- Fix: netstat -ano | findstr :${a.join(" :")}
61
- Verify: flisk doctor`)}let c=!0;for(let l of["flisk.localhost","example.flisk.localhost"])try{let d=await Yr(l,{all:!0,verbatim:!0});Qr(d.map(m=>m.address))||(c=!1)}catch{c=!1}c||e.push(`${Qs}
62
- Fix: add these lines to C:\\Windows\\System32\\drivers\\etc\\hosts (run Notepad as Administrator):
63
- 127.0.0.1 flisk.localhost
64
- 127.0.0.1 example.flisk.localhost
65
- Verify: ping flisk.localhost (should resolve to 127.0.0.1)
66
- Docs: https://github.com/flisk-sh/flisk/blob/main/docs/wsl2.md#troubleshooting`);try{Q("git",["config","core.autocrlf"],{encoding:"utf-8",timeout:5e3,windowsHide:!0}).trim().toLowerCase()==="true"&&e.push(`git core.autocrlf is true; generated files may get CRLF while Docker Linux containers expect LF.
67
- Fix: git config core.autocrlf input (run in Flisk repo root)
68
- Verify: git config core.autocrlf (should show "input")`)}catch{}return e.push(`Docker Desktop Settings \u2192 Resources \u2192 File sharing must include this project directory.
69
- Fix: Docker Desktop \u2192 Settings \u2192 Resources \u2192 File sharing \u2192 add project path
70
- Verify: flisk doctor`),{name:"Windows ports and DNS",didPass:c&&a.length===0,message:e.length>0?e.join(`
71
-
72
- `):"Required ports are available and Flisk localhost DNS resolves to loopback",...c?{}:{fix:Qs}}}var O=1e4,ut={dockerMissing:"See https://docs.docker.com/get-docker/",dockerDaemonDown:"Start Docker Desktop, OrbStack, or Colima, then retry: flisk doctor",tiltMissing:"curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash",bunMissing:"curl -fsSL https://bun.sh/install | bash",port80:"Stop the process bound to port 80, or stop local nginx/caddy. Then: flisk doctor",port5432:"Stop local Postgres or change the host port. Then: flisk doctor",notProject:"flisk project --yes"},nn="WSL2 detected. Use Docker Desktop WSL integration. Guide: docs/wsl2.md",rn="Native Windows landscape boot is unsupported. Use WSL2 Ubuntu with Docker Desktop integration. Guide: docs/wsl2.md",ot=[0,25,0],W=[1,2,0];function Ce(e,t){let s=ct(e);return s?lt(s,t):!1}function on(e,t,s=process.platform==="linux"&&!!process.env.WSL_DISTRO_NAME){if(!s)return{name:"WSL project location",didPass:!0,isSkipped:!0,message:"Not running in WSL2 - skipped project filesystem check"};let n=e.replace(/\\/g,"/").replace(/\/+$/,"").toLowerCase();if(n!=="/mnt/c"&&!n.startsWith("/mnt/c/"))return{name:"WSL project location",didPass:!0,message:`${nn} Project is on the WSL filesystem.`};let r=`${nn} Project is under /mnt/c (${e}); hot reload may be broken. Move it into the WSL filesystem, such as ~/projects.`;return{name:"WSL project location",didPass:!t,isWarning:!t,message:r,fix:"Move the repository under your WSL home directory (for example, ~/projects) and retry flisk doctor."}}function ro(e){let t=e.replace(/^\[(.*)\]$/,"$1");return t==="0.0.0.0"||/^[0:]*:0\.0\.0\.0$/.test(t)?!0:t.includes(":")&&/^[0:]+$/.test(t)}function oo(e=process.env,t=!1){let s=(e.FLISK_BIND_ADDRESS??"").trim();return ro(s)?{name:"Dev Port Bind Address",didPass:!t,isWarning:!t,message:`FLISK_BIND_ADDRESS is ${s}: Traefik and Postgres are reachable from other machines on the network.`,fix:"Unset FLISK_BIND_ADDRESS to keep dev ports on 127.0.0.1. Keep it only when you test from another device on purpose."}:{name:"Dev Port Bind Address",didPass:!0,message:s?`Dev ports bind to ${s}`:"Dev ports bind to 127.0.0.1 (this machine only)"}}function un(e){let t=e.filter(n=>!n.didPass&&!n.isSkipped&&!n.isWarning),s=e.filter(n=>n.didPass||n.isSkipped||n.isWarning);return t.sort((n,r)=>{let o=i=>i.name==="Host Ports"&&/\b5432\b/.test(i.message)?0:i.name==="Host Ports"?1:i.name==="Ingress Ports"?2:3;return o(n)-o(r)}),[...t,...s]}function io(e,t){return t?e?"Doctor passed. Next: flisk up":"Doctor passed. Next: flisk project example":"Doctor failed. Fix the items above, then run: flisk doctor"}var at={checkDockerOperatingSystem:"Docker OS",checkDockerCompose:"Docker Compose",checkDockerVersions:"Docker Versions",checkDockerNetworkCapacity:"Docker Network Pool",checkContainerResourceHealth:"Container Resource Health",checkCrashLoopingContainers:"Crash-looping containers",checkTiltResourceHealth:"Tilt Resources",checkServiceHealth:"Service Health",checkServiceSmoke:"Service Smoke",checkAdvertisedEndpoints:"Advertised Endpoints"},an=new Set(["Docker OS","Docker Compose","Docker Versions","Docker Network Pool","Container Resource Health","Crash-looping containers","Tilt Resources","Service Health","Service Smoke","Advertised Endpoints",...Object.keys(at)]);function ao(e){return!!(e.requiresContainerRuntime||e.checkName&&an.has(e.checkName)||e.name&&an.has(e.name))}function R(e,t){let s=()=>t();return s.requiresContainerRuntime=!0,s.checkName=e,s}async function co(e,t={}){let s=[],n=!!t.containerRuntimeFailed;for(let r of e){if(n&&ao(r)){let i=r.checkName??(r.name&&at[r.name]?at[r.name]:r.name&&r.name!=="check"&&r.name!=="fn"?r.name:"Container check");s.push({name:i,didPass:!0,isSkipped:!0,message:`Docker not running - skipped ${i} check`});continue}let o=await r();if(s.push(o),o.name===$&&!o.didPass&&(n=!0),!t.all&&!o.didPass&&!o.isSkipped&&!o.isWarning)break}return s}function lo(e){let t=0,s=0,n=0,r=0;for(let o of e)o.isSkipped?n++:o.isWarning?r++:o.didPass?t++:s++;return r>0?`${t} passed, ${r} warned, ${s} failed, ${n} skipped`:`${t} passed, ${s} failed, ${n} skipped`}function pt(e){return It(e)}function uo(e,t,s,n,r){return async(o=j)=>{try{return await o(t,O),{name:e,didPass:!0,message:s}}catch(i){return pt(i)?{name:e,didPass:!1,message:`${e} did not respond within ${O/1e3}s`,fix:r}:{name:e,didPass:!1,message:n,fix:r}}}}async function it(e){try{return await j(e,O),!0}catch{return!1}}async function po(){let e=B();return e.isColima?e.running&&!e.problem?{name:"Colima",didPass:!0,message:`Colima VM is running${e.arch?` (${e.arch})`:""}`}:{name:"Colima",didPass:!1,message:e.problem??"The Colima VM is not running",fix:e.fix??`Start Colima: colima start
73
- Verify: colima status
74
- Docs: https://github.com/abiosoft/colima`}:{name:"Colima",didPass:!0,message:"Docker does not run on Colima"}}async function ft(){if(process.platform==="win32")try{return await j("docker ps",O),{name:$,didPass:!0,message:"Docker Desktop Linux container engine is running"}}catch{return{name:$,didPass:!1,message:"Docker is not running",fix:`Start Docker Desktop, enable WSL2 integration, then retry: flisk doctor
75
- Verify: docker ps
76
- Docs: https://docs.docker.com/desktop/wsl/`}}let e=!!w("docker");try{return await j("docker ps",O),{name:$,didPass:!0,message:"Docker daemon is running"}}catch(s){if(pt(s))return{name:$,didPass:!1,message:"Docker daemon is not responding (timeout)",fix:`Restart Docker daemon: systemctl restart docker (Linux) or restart Docker Desktop/Colima
77
- Verify: docker ps
78
- Docs: https://docs.docker.com/config/daemon/`}}let t=ps(await it("pgrep -f com.docker.backend"));return t||(await it("colima status")?{name:$,didPass:!0,message:"Colima (Docker runtime) is running"}:w("colima")?{name:$,didPass:!1,message:"Colima is installed but not running",fix:`Start Colima: colima start
79
- Verify: colima status
80
- Docs: https://github.com/abiosoft/colima`}:await it("podman ps")?{name:$,didPass:!0,message:"Podman is running"}:e?{name:$,didPass:!1,message:"Docker daemon is not running",fix:`Start Docker daemon: systemctl start docker (Linux) or start Docker Desktop
81
- Verify: docker ps
82
- Docs: https://docs.docker.com/engine/daemon/`}:{name:$,didPass:!1,message:"Docker is not installed",fix:`Install Docker: https://docs.docker.com/get-docker/
83
- Verify: docker --version
84
- After install, start Docker and retry: flisk doctor`})}var Z=uo("Docker Compose","docker compose version","Docker Compose plugin available","Docker Compose plugin not found","Install Docker Compose: https://docs.docker.com/compose/install/");Z.requiresContainerRuntime=!0;Z.checkName="Docker Compose";var fo=[25,0,0],mo=[2,20,2];function ct(e){let t=e.trim().match(/(\d+)\.(\d+)(?:\.(\d+))?/);return t?[Number(t[1]),Number(t[2]),Number(t[3]??0)]:null}function lt(e,t){for(let[s,n]of t.entries()){let r=e[s]??0;if(r!==n)return r>n}return!0}async function ee(e=j){let t=async a=>String(await e(a,O)).trim(),s,n;try{[s,n]=await Promise.all([t("docker version --format '{{.Server.Version}}'"),t("docker compose version --short")])}catch{return{name:"Docker Versions",didPass:!0,isSkipped:!0,message:"Could not read Docker Engine/Compose versions - skipped version check"}}let r=ct(s),o=ct(n);if(!r||!o)return{name:"Docker Versions",didPass:!0,isSkipped:!0,message:`Unrecognized Docker version output (engine "${s}", compose "${n}") - skipped version check`};let i=[];return lt(r,fo)||i.push(`Docker Engine ${s} (need 25.0+)`),lt(o,mo)||i.push(`Docker Compose ${n} (need 2.20.2+)`),i.length===0?{name:"Docker Versions",didPass:!0,message:`Docker Engine ${s} and Compose ${n} support generated healthchecks`}:{name:"Docker Versions",didPass:!1,message:`Docker is too old for generated healthchecks (they use start_interval):
85
- ${i.join(`
86
- `)}`,fix:`Update Docker: Docker Desktop (auto-updates), or Linux: apt/yum/dnf update docker-ce docker-compose-plugin
87
- Verify: docker version --format '{{.Server.Version}}' && docker compose version --short
88
- Docs: https://docs.docker.com/engine/install/`}}ee.requiresContainerRuntime=!0;ee.checkName="Docker Versions";async function mt(e=j){let t;try{t=String(await e("tilt version",O)).trim()}catch{return{name:"Tilt CLI",didPass:!1,message:"Tilt CLI not found",fix:`Install Tilt: curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
89
- Verify: tilt version
90
- Docs: https://docs.tilt.dev/install.html`}}return Ce(t,ot)?{name:"Tilt CLI",didPass:!0,message:`Tilt ${t} is available`}:{name:"Tilt CLI",didPass:!1,message:`Tilt ${t||"version unknown"} is below the required v${ot.join(".")} floor`,fix:`Upgrade Tilt to v${ot.join(".")} or newer: https://docs.tilt.dev/install.html
91
- Verify: tilt version
92
- If using Homebrew: brew upgrade tilt`}}async function ht(e=j){let t;try{t=String(await e("docker info --format '{{.OSType}}'",O)).trim().toLowerCase()}catch{return{name:"Docker OS",didPass:!0,isSkipped:!0,message:"Docker OS unavailable - daemon failure reported separately"}}return t!=="linux"?{name:"Docker OS",didPass:!1,message:`Docker is using the ${t||"unknown"} engine; Flisk requires Linux containers`,fix:`Switch Docker Desktop to Linux containers: Settings > General > Use the WSL 2 based engine
93
- Or on Windows: docker context use default (if using WSL2 backend)
94
- Verify: docker info --format '{{.OSType}}'
95
- Docs: https://docs.docker.com/desktop/wsl/`}:{name:"Docker OS",didPass:!0,message:"Docker is using Linux containers"}}ht.requiresContainerRuntime=!0;ht.checkName="Docker OS";async function ho(e=j){let t=h();if(!t)return{name:"Bun",didPass:!0,isSkipped:!0,message:"Not in a project - skipping Bun check"};if(!de(v(t)))return{name:"Bun",didPass:!0,isSkipped:!0,message:"No generated JS services - skipping Bun check"};try{let s=String(await e("bun --version",O)).trim();return Ce(s,W)?{name:"Bun",didPass:!0,message:`Bun ${s} is available`}:{name:"Bun",didPass:!1,message:`Bun ${s||"version unknown"} is below the required ${W.join(".")} floor`,fix:`Upgrade Bun to ${W.join(".")} or newer: https://bun.sh/install
96
- Verify: bun --version
97
- If using npm: npm install -g bun@latest`}}catch{return{name:"Bun",didPass:!1,message:"Bun is not installed",fix:`Install Bun: curl -fsSL https://bun.sh/install | bash
98
- Verify: bun --version
99
- Docs: https://bun.sh/docs/installation`}}}function go(){let e=h()??process.cwd(),t=Pt(e);if(t.missing.length>0)return{name:"Environment Variables",didPass:!1,message:`Missing required env variables: ${t.missing.join(", ")}`,fix:C(x(e,".env"))?`Edit .env and set these values:
100
- ${t.missing.map(n=>`${n}=<value>`).join(`
101
- `)}`:"No .env file yet. Run `flisk project` to create one with working defaults."};if(t.invalid.length>0)return{name:"Environment Variables",didPass:!1,message:`Invalid env variables: ${t.invalid.join(", ")}`,fix:"Edit .env and provide values for empty variables"};let s=t.warnings.filter(n=>!n.startsWith(".env file not found"));return{name:"Environment Variables",didPass:!0,message:s.length>0?`All required environment variables are set (warning: ${s.join("; ")})`:"All required environment variables are set"}}function ko(){let e=h()??process.cwd(),t=x(e,".flisk",".flisk-out"),s=Oe.filter(n=>!C(x(t,n)));return s.length===0?{name:"Master Configs",didPass:!0,message:`${Oe.join(", ")} found`}:{name:"Master Configs",didPass:!1,message:`Master configs missing: ${s.join(", ")}`,fix:"Run: flisk project"}}function cn(e=yt()){let t=h();if(!t)return{name:"Flisk version",didPass:!0,isSkipped:!0,message:"Not in a project - skipping Flisk version check"};let s=qs(t,e);return s.status==="none"?{name:"Flisk version",didPass:!0,isSkipped:!0,message:`flisk ${e} (no minFliskVersion in .flisk/project.json)`}:s.status==="malformed"||s.status==="too-old"?{name:"Flisk version",didPass:!1,message:s.message,fix:s.fix}:{name:"Flisk version",didPass:!0,message:`flisk ${e} meets minFliskVersion ${s.required}`}}function yo(){let e=h()??process.cwd(),t=Be(e),s=v(e).filter(r=>!pe(e,r.path,t));if(s.length===0)return{name:"Resource Discovery",didPass:!0,message:`All resources are covered by discovery.paths ${JSON.stringify(t)}`};let n=s.map(r=>De($e(e,r.path))).join(", ");return{name:"Resource Discovery",didPass:!1,message:`${b(s.length,"resource")} outside discovery.paths ${JSON.stringify(t)}: ${n}. \`flisk up\` will not start them`,fix:"Move each under services/<stack>/<name>, or add a matching pattern to discovery.paths in .flisk/project.json and run: flisk config regenerate"}}function So(e){let t=[],s=(n,r)=>{for(let o of dn(n,{withFileTypes:!0})){if(r===""&&o.name===".flisk-contract")continue;let i=r===""?o.name:`${r}/${o.name}`;o.isDirectory()?s(x(n,o.name),i):o.isFile()&&t.push(i)}};return s(e,""),t.sort()}function wo(e=!1){let t=h()??process.cwd(),s=v(t).filter(o=>(o.config?.appType??o.type)!=="library");if(s.length===0)return{name:"Generated Files",didPass:!0,isSkipped:!0,message:"No resources to check"};let n=[],r=[];for(let o of s){let i=De($e(t,o.path)),a=x(o.path,F),c=C(a)?So(a):[];c.length===0?(r.push(i),n.push(`${i}: no ${F}/ files`)):n.push(`${i}: ${c.join(", ")}`)}return r.length===0?{name:"Generated Files",didPass:!0,message:`${b(s.length,"resource")} with generated files:
102
- ${n.join(`
103
- `)}`}:{name:"Generated Files",didPass:!1,isWarning:!e,message:`${r.length} of ${b(s.length,"resource")} not generated yet:
104
- ${n.join(`
105
- `)}`,fix:"Run `flisk up` to generate them (it needs Docker, Compose and Tilt). `flisk config verify` checks the project-level files only."}}var vo=["install-deps.sh","bun-hoisted-symlink-fix.sh","prisma-bun-client-link-fix.sh","prisma-normalize-client.sh","infisical-entrypoint.sh"],xo=/shared-platform-engineering\/docker-templates\/([\w.-]+)/g;function Po(e){let t=new Set;for(let s of v(e)){let n=x(s.path,F);if(C(n)){for(let r of dn(n))if(!(!r.startsWith("Dockerfile")||r.endsWith(".dockerignore")))for(let o of G(x(n,r),"utf-8").matchAll(xo))t.add(o[1])}}return[...t]}function bo(){let e=h()??process.cwd(),t=[];C(x(e,J))||t.push(`${J} (workspace root manifest)`);let s=x(e,"shared-platform-engineering","docker-templates"),n=[];try{n=Po(e)}catch{}for(let r of new Set([...vo,...n]))C(x(s,r))||t.push(`shared-platform-engineering/docker-templates/${r}`);return t.length===0?{name:"Generated Runtime Assets",didPass:!0,message:"Generated Docker runtime assets are present"}:{name:"Generated Runtime Assets",didPass:!1,message:`Generated Dockerfiles reference missing project runtime assets:
106
- ${t.join(`
107
- `)}`,fix:"Run `flisk project --yes` (or `flisk config regenerate`) with an updated Flisk. `flisk up` also refreshes these assets before starting Tilt."}}function Do(){let e=h()??process.cwd(),t=v(e),s=[];for(let n of t){let r=x(n.path,J);if(!C(r))continue;let o=n.config?.appType??"backend",i=_e[o]??_e.backend,a;try{a=JSON.parse(G(r,"utf-8")).scripts??{}}catch{s.push(`${n.name} (package.json is invalid JSON)`);continue}let c=i.filter(l=>!a[l]);c.length>0&&s.push(`${n.name}: missing "${c.join('", "')}"`)}return s.length===0?{name:"Startup Scripts",didPass:!0,message:"All services have required package.json scripts"}:{name:"Startup Scripts",didPass:!1,message:`Services missing required package.json scripts:
108
- ${s.join(`
109
- `)}`,fix:`Add the missing scripts to each service's package.json, e.g.:
110
- "dev": "bun run src/index.ts",
111
- "build": "tsc",
112
- "start": "bun run dist/index.js"`}}function pn(e){if(e.startsWith("@"))return[e.split("/").slice(0,2).join("/")];let t=e.split("/")[0]??e;return[`@types/${t}`,t]}function fn(e,t,s=0){let n=e[t];if(typeof n!="string"||s>3)return!1;if(/(^|[\s;&|(])tsc(\s|$)/.test(n))return!0;for(let r of n.matchAll(/\b(?:bun|npm|pnpm|yarn)\s+run\s+([\w:.-]+)/g))if(fn(e,r[1],s+1))return!0;return!1}function ln(e){let t=pn(e);return e.includes("/")||e.startsWith("@")?t[t.length-1]:t[0]}var $o={"@types/bun":"^1.4.2","@types/node":"^20.0.0"};function Co(e,t){return(t.includes("bun")||e.has("@types/bun"))&&!e.has("@types/node")}function No(){let e=h()??process.cwd(),t=v(e),s=[],n=new Set;for(let o of t){let i=x(o.path,J),a=x(o.path,F,"tsconfig.docker.autogenerated.json");if(!C(i)||!C(a))continue;let d=dt(a)?.compilerOptions?.types;if(!Array.isArray(d))continue;let m=dt(i),u=m?.scripts??{};if(!fn(u,"build"))continue;let g=new Set([...Object.keys(m?.devDependencies??{}),...Object.keys(m?.dependencies??{})]),k=d.filter(S=>typeof S=="string").filter(S=>!pn(S).some(D=>g.has(D)));for(let S of k)n.add(ln(S));if(k.length>0){let S=k.map(D=>`"${D}" (needs ${ln(D)})`).join(", ");s.push(`${o.name}: "types" lists ${S}`)}Co(g,d)&&(n.add("@types/node"),s.push(`${o.name}: uses @types/bun, which references node types internally, but @types/node is not a direct dependency`))}if(s.length===0)return{name:"TypeScript Type Dependencies",didPass:!0,message:`Every service's Docker tsconfig "types" entry has its @types package installed`};let r=[...n].map(o=>{let i=$o[o];return i?`"${o}": "${i}"`:`${o} (bun add -d ${o})`});return{name:"TypeScript Type Dependencies",didPass:!1,message:`\`bun run build\` will fail with TS2688 (Cannot find type definition file) because a @types package it needs is not a direct dependency:
113
- ${s.join(`
114
- `)}`,fix:`Add to each service's package.json devDependencies: ${r.join(", ")}`}}function dt(e){try{return JSON.parse(G(e,"utf-8"))}catch{return null}}function To(e){let s=dt(e)?.compilerOptions;return s?.allowSyntheticDefaultImports===!0||s?.esModuleInterop===!0}function Eo(){let e=h()??process.cwd(),t=v(e).filter(n=>n.config?.appType==="frontend");if(t.length===0)return{name:"Frontend Docker Preflight",didPass:!0,isSkipped:!0,message:"No frontend services to check"};let s=[];for(let n of t){C(x(n.path,"public"))||s.push(`${n.name}: no public/ folder, but the Docker build copies it. Create it: mkdir -p ${De($e(e,x(n.path,"public")))} && touch ${De($e(e,x(n.path,"public",".gitkeep")))}`);let r=x(n.path,F,"Dockerfile.app.autogenerated"),o=x(n.path,F,"tsconfig.docker.autogenerated.json");if(C(r)){let i=G(r,"utf-8"),a=/\bEXPOSE\s+80\b/.test(i),c=n.config?.stack,l=c?x(e,"services",c,F,"docker-compose.app.autogenerated.yml"):"";if(a&&l&&C(l)){let d=G(l,"utf-8"),m=n.name.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),u=new RegExp(`traefik\\.http\\.services\\.${m}\\.loadbalancer\\.server\\.port=(\\d+)`),k=d.match(u)?.[1];k&&k!=="80"&&s.push(`${n.name}: nginx runtime exposes 80, but generated Traefik routes to ${k}`)}}if(C(o)&&!To(o)){let i=x(n.path,"src","main.tsx");if(C(i)){let a=G(i,"utf-8"),c=[/import\s+\w+\s+from\s+["']react["']/,/import\s+\w+\s+from\s+["']react-dom\/client["']/].filter(l=>l.test(a)).map(l=>l.source.includes("react-dom")?"react-dom/client":"react");c.length>0&&s.push(`${n.name}: Docker tsconfig does not allow synthetic default imports, but src/main.tsx default-imports ${c.join(" and ")}`)}}}return s.length===0?{name:"Frontend Docker Preflight",didPass:!0,message:`${b(t.length,"frontend")} ${t.length===1?"has":"have"} compatible Docker build/routing metadata`}:{name:"Frontend Docker Preflight",didPass:!1,message:`Frontend Docker metadata has issues that will make \`flisk up\` fail or route 404:
115
- ${s.join(`
116
- `)}`,fix:'Regenerate with a current Flisk (`flisk config regenerate`) and ensure frontend nginx runtimes route to port 80. For React entrypoints, prefer named imports: `import { StrictMode } from "react"` and `import { createRoot } from "react-dom/client"`.'}}var Ro=90,Oo=80;function _o(e){return e.split(`
117
- `).map(t=>t.trim()).filter(Boolean).map(t=>{let[s,n,r]=t.split(",");return{name:s??"unknown",memPct:Number.parseFloat((n??"").replace(/%/g,"")),cpuPct:Number.parseFloat((r??"").replace(/%/g,""))}}).filter(t=>Number.isFinite(t.memPct)&&Number.isFinite(t.cpuPct))}var jo=Object.assign(function(){let t;try{t=so(w("docker")??"docker",["stats","--no-stream","--format","{{.Name}},{{.MemPerc}},{{.CPUPerc}}"],{stdio:"pipe",encoding:"utf-8",timeout:O,windowsHide:process.platform==="win32"})}catch(o){return{name:"Container Resource Health",didPass:!0,isSkipped:!0,message:pt(o)?"Docker not responding - skipped container resource check":"Docker not running - skipped container resource check"}}let s=_o(t);if(s.length===0)return{name:"Container Resource Health",didPass:!0,isSkipped:!0,message:"No running containers to check"};let n=s.filter(o=>o.memPct>=Ro&&o.cpuPct>=Oo);if(n.length===0)return{name:"Container Resource Health",didPass:!0,message:`${b(s.length,"container")} within normal resource usage`};let r=n.map(o=>`${o.name} (mem ${o.memPct.toFixed(0)}%, cpu ${o.cpuPct.toFixed(0)}%)`).join(`
118
- `);return{name:"Container Resource Health",didPass:!1,message:`${b(n.length,"container")} pinned near its memory limit while burning CPU -- likely stuck in a boot-time retry/fetch loop, not a normal crash:
119
- ${r}`,fix:"Check the container's boot logs for a network fetch loop (e.g. `bunx <pkg>` re-downloading a CLI on every start instead of using one already installed): docker logs <container>. If a Prisma auto-migration is the cause, set AUTO_MIGRATE=false in the project's .env as a temporary unblock."}},{requiresContainerRuntime:!0,checkName:"Container Resource Health"}),Ao=5e3;async function Fo(e,t){let s=h()??process.cwd(),n=Ae(v(s),t);return n.length===0?{name:"Service Health",didPass:!0,isSkipped:!0,message:"No routable services to ping"}:He(await ie(n,e))}async function Io(e,t){let s=h()??process.cwd(),n=v(s),r=Nt(Ct(n,t),{timeoutSeconds:Math.ceil(e/1e3)});return r.length===0?We([]):(await ie(Ae(n,t),e)).some(i=>i.status!==void 0)?We(await Et(r,Tt(s))):{name:"Service Smoke",didPass:!0,isSkipped:!0,message:"No services responded - skipped smoke checks"}}async function Lo(e,t){let s=h()??process.cwd(),n=v(s),r=$t(n,t);return r.length===0?{name:"Advertised Endpoints",didPass:!0,isSkipped:!0,message:"No routable services to check"}:es(await ie(r,e))}function Mo(e,t){if(e.length===0){console.log(`${f.green("\u2713")} Nothing to repair
120
- `);return}for(let s of e){let n=s.status==="planned"?`${f.cyan("would run")}`:s.status==="applied"?f.green("ran"):s.status==="failed"?f.red("failed"):f.gray("skipped"),r=s.status==="planned"?"":` (${s.reason})`;console.log(`${n}: ${s.command}${r}`)}t&&console.log(f.gray(`
121
- Dry run: nothing was changed.`)),console.log("")}function be(e){for(let t of un(e))!t.didPass&&!t.isSkipped&&!t.isWarning?(console.log(`${f.red("\u2717")} ${t.message}`),t.fix&&console.log(`${f.blue("\u2139")} Fix: ${t.fix}`)):t.isWarning?(console.log(`${f.yellow("\u26A0")} ${f.yellow(t.message)}`),t.fix&&console.log(`${f.blue("\u2139")} Fix: ${t.fix}`)):t.isSkipped?(console.log(`${f.gray("\u25CB")} ${f.gray(t.message)}`),t.fix&&console.log(`${f.blue("\u2139")} ${t.fix}`)):console.log(`${f.green("\u2713")} ${t.message}`)}function Ho(e,t,s,n={},r){let o=r??[...e,...t];n.all?(console.log(f.bold("Machine")),be(e),console.log(`
122
- ${f.bold("Project")}`),s?t.length>0&&be(t):console.log(`${f.gray("\u25CB")} ${f.gray("Not in a Flisk project, so project checks were skipped")}`),console.log(""),console.log(lo(o))):(be(o),console.log(""),o.every(a=>a.didPass||a.isSkipped||a.isWarning)&&!s&&(console.log(`${f.gray("\u25CB")} ${f.gray("Not in a Flisk project, so project checks were skipped")}`),console.log("")))}var Wo=new no("doctor").description("Check environment readiness for Flisk").option("--all","Run every check and show Machine and Project sections with a summary",!1).option("--json","Output a versioned JSON readiness report",!1).exitOverride(e=>{e.exitCode===0&&process.exit(0),Wo.opts().json&&console.log(JSON.stringify(L([],!1,[{code:"USAGE",message:e.message}]))),process.exit(2)}).option("--no-ping","Skip pinging running services' /health endpoints").option("--strict","Fail warnings such as a WSL project under /mnt/c").option("--fix","Repair this project's unused Docker networks and leftover doctor probes",!1).option("--dry-run","With --fix: list the repairs without changing anything",!1).option("--scope <scope>","With --fix: project (default) or machine, which also removes unused networks of other Flisk projects","project").option("-y, --yes","With --fix: apply every repair without asking",!1).option("--ping-timeout <ms>","Per-service ping timeout in milliseconds",String(Ao)).action(async e=>{let t=Number(e.pingTimeout);if(!/^\d+$/.test(e.pingTimeout)||!Number.isSafeInteger(t)||t<=0){let p="--ping-timeout must be a positive integer number of milliseconds";e.json&&console.log(JSON.stringify(L([],!1,[{code:"USAGE",message:p}]))),console.error(p),process.exit(2);return}let s=!["project","machine"].includes(e.scope),n=e.scope!=="project"&&!e.fix;if(s||n||(e.dryRun||e.yes)&&!e.fix){let p=s?`--scope must be project or machine, got "${e.scope}"`:n?"--scope only applies with --fix":`${e.dryRun?"--dry-run":"--yes"} only applies with --fix`;e.json&&console.log(JSON.stringify(L([],!1,[{code:"USAGE",message:p}]))),console.error(p),process.exit(2);return}let r=Xs(),o=r?.resources??null;e.json||console.log(o?"Flisk doctor checks what this project's local processes need; its services declare commands, so Docker and Tilt are not checked.":"Flisk doctor checks your local Docker + Tilt development environment; it does not check cluster deployments.");let i=ns();if(process.platform==="win32"){let P=[{name:"Native Windows support",didPass:!1,message:rn,fix:"Use WSL2 Ubuntu with Docker Desktop integration. Guide: docs/wsl2.md"},Zs(),en(),await tn()],V=null;try{V=await Ot()}catch{}e.json?console.log(JSON.stringify(L(P,!!h(),[],V,{...i,canUp:!1}))):(be(P),console.log("")),console.error(rn),process.exit(1);return}if(i.kind==="webcontainer"){let p=L([{name:"Host",didPass:!1,message:Ue,fix:`Use a machine with Docker. Guide: ${le}`}],!!h(),[],void 0,i);e.json&&console.log(JSON.stringify(p)),console.error(Ue),process.exit(1);return}e.json||(console.log(`
123
- ${f.bold("\u{1F50D} Flisk Doctor")}
124
- `),console.log(`Checking environment...
125
- `));let a=h()??r?.projectRoot??null,c;if(e.fix&&o)c=[];else if(e.fix){try{let p;if(a)try{p=At(a).project.name}catch{}let P=a?ws(a):[],V=await ks(p,void 0,{tiltPids:P,machine:e.scope==="machine"});c=await ys(V,{dryRun:e.dryRun,confirm:e.yes||!e.json&&process.stdin.isTTY?async E=>e.yes||await Ft({message:`Run \`${E.command}\` (${E.check}, ${E.reason})?`}):void 0})}catch(p){c=[{id:"plan-fixes",check:"Docker networks",scope:"project",command:"docker network ls",status:"failed",reason:`could not list Docker networks: ${(p instanceof Error?p.message:String(p)).split(/\r?\n/)[0]}`}]}e.json||Mo(c,e.dryRun)}let l,d;if(!o)try{l=await ae(a??process.cwd())}catch(p){l=null,d=p instanceof Error?p.message:String(p)}let m=[()=>l?{name:"Host Ports",didPass:!0,message:`HTTP ${l.ingressHttp}, HTTPS ${l.ingressHttps}, Postgres ${l.postgres}`}:{name:"Host Ports",didPass:!1,message:d??"Could not select host ports",fix:"Set FLISK_HTTP_PORT, FLISK_HTTPS_PORT, or FLISK_POSTGRES_PORT to available host ports."},()=>l?Me(sn,void 0,[l.ingressHttp,l.ingressHttps]):{name:"Ingress Ports",didPass:!1,message:"Ingress port plan is unavailable"},po,ft,()=>us(),R("Docker OS",ht),mt,R("Docker Compose",Z),R("Docker Versions",ee),ho,()=>on(h()??process.cwd(),e.strict),()=>oo(process.env,e.strict),R("Docker Network Pool",()=>Kt()),()=>Xe()],u=[()=>cn(),ko,bo,()=>wo(e.strict),Gs,Do,()=>ts(),No,Eo,yo,()=>Mt(),()=>X(),()=>we(),()=>ve(),()=>xe(),()=>Pe(void 0,e.strict),()=>Ie(),()=>Ht(),()=>Vt(),()=>Wt(),()=>ce(),()=>Lt(),()=>Bt(),()=>Ut(),()=>Gt(),go,async()=>{let p=h()??process.cwd(),P=Qt(p)?await jt(p):!0;return Le(sn,p,void 0,P)},R("Container Resource Health",jo),R("Crash-looping containers",()=>{let p=h()??process.cwd(),P=[];try{P=je(p)}catch{return{name:"Crash-looping containers",didPass:!0,isSkipped:!0,message:"Could not read .env - skipped crash-loop check"}}return ls(oe(),P)}),R("Tilt Resources",Ve)],g=o?[()=>Fs(o),()=>Is(o),()=>on(h()??process.cwd(),e.strict),()=>Xe()]:m,k=o?[()=>cn(),()=>X(),()=>we(),()=>Ls(o),()=>ve(),()=>xe(),()=>Pe(void 0,e.strict),()=>Ie()]:u;if(e.ping&&o&&a&&k.push(()=>Ms(a,o,t)),e.ping&&!o){let p=a?_t(a,l?.ingressHttp):l?.ingressHttp;k.push(R("Service Health",()=>Fo(t,p))),k.push(R("Service Smoke",()=>Io(t,p))),k.push(R("Advertised Endpoints",()=>Lo(t,p)))}let S=!!a,D=[],N=[],y,T=[];if(e.json||!e.all){let p=await Ws(g,S?k:[]);y=p.checks,T=p.errors}else{let p=async(E,$n=!1)=>{let Cn=E.map(se=>{let Ee=async()=>{try{return await se()}catch(Re){return T.push({code:"INTERNAL",message:Re instanceof Error?Re.message:String(Re)}),{name:se.checkName??"Doctor check",didPass:!1,isSkipped:!1,message:"Check failed with an internal error"}}};return Ee.requiresContainerRuntime=se.requiresContainerRuntime,Ee.checkName=se.checkName,Ee});return co(Cn,{all:!!e.all,containerRuntimeFailed:$n})};D=await p(g);let P=D.some(E=>!E.didPass&&!E.isSkipped&&!E.isWarning),V=D.some(E=>E.name===$&&!E.didPass);S&&(e.all||!P)&&(N=await p(k,V)),y=[...D,...N]}if(rs(i.kind))for(let p of y)p.name===$&&!p.didPass&&(p.fix=ss);let _=L(un(y),S,T,l,i);c&&(_.data.actions=c),c&&Ss(c)&&(_.data.ready=!1);let q=Vs(_),gt=_.data.ready;if(e.json){let p;try{p=je(h()??process.cwd())}catch(P){if(!(P instanceof bt))throw P;console.log(JSON.stringify(L([],S,[...T,{code:"ENV_UNREADABLE",message:P.message}]))),console.error(P.message),process.exit(1)}console.log(JSON.stringify(Dt(_,p)));for(let P of T)console.error(re(P.message,p));q!==0&&process.exit(q);return}for(let p of T)console.error(`${f.red("\u2717")} Doctor check failed: ${p.message}`);Ho(D,N,S,e,y);let Dn=io(S,gt);console.log(`${gt?f.green(f.bold("\u2713")):f.red(f.bold("\u2717"))} ${Dn}`),q!==0&&process.exit(q)});function mn(e){return Ce(e,W)}var te=`${W[0]}.${W[1]}+`;import{execFileSync as hn}from"node:child_process";import{accessSync as gn,constants as kn,readdirSync as Vo}from"node:fs";import{delimiter as Uo,join as K}from"node:path";var A="flisk",yn="@flisk-sh/flisk",Bo="https://github.com/flisk-sh/flisk",Sn="@tdk-landscape/tdk-cli-core";function wn(e=A,t=process.env){let s=t.PATH??t.Path??"";for(let n of s.split(Uo)){if(!n)continue;let r=[K(n,e),K(n,`${e}.cmd`),K(n,`${e}.exe`)];for(let o of r)try{return gn(o,kn.X_OK),!0}catch{}}return!1}function Go(e=process.env){let t=e.HOME??e.USERPROFILE??"";if(!t)return"flisk-workspace";let s=K(t,"Documents");try{return gn(s,kn.F_OK),K(s,"flisk")}catch{return K(t,"flisk")}}function Ko(e){try{return Vo(e).every(t=>t.startsWith("."))}catch{return!0}}function vn(e=process.env,t=process.cwd()){if(!wn(A,e))return`The \`${A}\` command is not on PATH (this run is not a global install). Install it with \`npm install -g ${yn}\`, then run \`${A} project --yes\`. If npm reports EEXIST for \`flisk\`, the old package still owns that command: run \`npm uninstall -g ${Sn}\` first. The current release is published from ${Bo}.`;if(Ko(t))return`Next: ${A} project --yes`;let s=Go(e);return`Next: mkdir -p ${s} && cd ${s} && ${A} project --yes
126
- (${A} project writes .flisk/ and the master configs into the current folder, so it starts clean in a folder of its own. Or clone a starter: ${A} project example)`}function Fc(e=process.env){if(wn(A,e))return"present";let t=e.npm_execpath?.includes("bun")?"bun":"npm",s=t==="bun"?"bun remove -g":"npm uninstall -g";try{hn(s,[Sn],{stdio:"ignore",timeout:12e4})}catch{}try{return hn(t,["install","-g",yn],{stdio:"ignore",timeout:3e5}),"installed"}catch{return"failed"}}var zo=["node","bun"];function bn(e){return e==="scaffold"?Yo:e==="native"?zo:Qo}var Xo=["docker","compose","tilt"],Yo=["node","bun","ports"],Qo=["node",...Xo,"bun","ports"];function Zo(e,t){return{id:e,ok:t.didPass||!!t.isSkipped,message:t.message,fix:t.fix}}function Pn(e){let[t=0,s=0]=String(e).split(".").map(n=>Number.parseInt(n,10));return!Number.isFinite(t)||!Number.isFinite(s)||t<22||t===22&&s<12}async function Ne(e,t,s){try{return Zo(e,await t())}catch{return{id:e,ok:!1,message:s}}}function ei(e){return e.isSkipped?{...e,didPass:!1,isSkipped:!1,message:`Could not verify Docker Engine/Compose minimum versions: ${e.message}`,fix:e.fix??"Start Docker and ensure Docker Engine 25+ and Compose 2.20.2+ are installed."}:e}function ti(e){try{return v(e).some(t=>t.config?.featuresEnabled?.includes("prisma")??!1)}catch{return!1}}async function si(e,t){try{let s=await ae(e,t==="scaffold"?{inspectDocker:!1}:{});return{id:"ports",ok:!0,message:`HTTP ${s.ingressHttp}, HTTPS ${s.ingressHttps}, Postgres ${s.postgres}`}}catch(s){return{id:"ports",ok:!1,message:s instanceof Error?s.message:String(s),fix:"Set FLISK_HTTP_PORT, FLISK_HTTPS_PORT, or FLISK_POSTGRES_PORT to available host ports."}}}async function ni(e={}){let t=e.cwd??process.cwd(),s=h(t),n=!!s,r=s?v(s):[],i=ne(r)?"native":e.scope??"runtime",a=[],c=process.versions.node;if(a.push({id:"node",ok:!Pn(c),message:c,fix:Pn(c)?`Install Node.js 22.12+ (current: ${c}). https://nodejs.org`:void 0}),i==="runtime"){let y=B();y.isColima&&a.push({id:"colima",ok:y.running,message:y.running?`Colima VM is running${y.arch?` (${y.arch})`:""}`:y.problem??"The Colima VM is not running",fix:y.running?void 0:y.fix}),a.push(await Ne("docker",ft,"Docker daemon is not running")),a.push(await Ne("compose",async()=>{let[T,_]=await Promise.all([Z(),ee()]);return T.didPass?ei(_):T},"Docker Engine or Compose version check failed")),a.push(await Ne("tilt",mt,"Tilt CLI not found"))}let l=s?de(r):!0,d=!1,m=l?w("bun"):void 0;if(m)try{let y=xn(m,["--version"],{encoding:"utf-8",timeout:1e4}).trim();d=mn(y)}catch{}if(a.push(l?{id:"bun",ok:d,message:d?`Bun ${te} is available on PATH`:`Bun ${te} not found on PATH`,fix:d?void 0:"curl -fsSL https://bun.sh/install | bash"}:{id:"bun",ok:!0,message:"Bun not needed: no generated JS services"}),i!=="native"&&a.push(await si(s??t,i)),s&&i==="runtime"){let y=await Ne("nats",()=>ce(s),"NATS broker check failed");if(y.ok||a.push(y),ti(s)){let T=!1;try{let _=JSON.parse(qo(Jo(s,".flisk",U),"utf-8")),Te=String(_?.project?.name??"").replace(/-/g,"_");Te&&(T=xn(w("docker")??"docker",["ps","--filter",`label=com.docker.compose.project=${Te}`,"--filter","label=com.docker.compose.service=postgres","--filter","health=healthy","--format","{{.Names}}"],{encoding:"utf-8",timeout:3e3}).trim().length>0)}catch{}T||a.push({id:"prisma",ok:!1,message:"A Prisma resource is configured, but the project Postgres service is not healthy",fix:"Run `flisk up` to start the bundled Postgres, then check `flisk doctor`."})}}let u=a.filter(y=>!y.ok).slice(0,8),g=bn(i),k=u.some(y=>g.includes(y.id)),S=u.some(y=>y.id==="bun"),D=u.length?k?i==="native"?"Cannot start the local processes. Fix the machine checks below.":i==="scaffold"?S?`Project setup blocked. Bun ${te} must be installed on PATH.
127
- Fix the machine checks below.`:"Project setup blocked. Fix the machine checks below.":S?`Cold start blocked. Bun ${te} must be installed on PATH.
128
- Prisma/NATS configuration is project-specific and generated after \`flisk project\`. Fix the machine checks below.`:"Cold start blocked. Bun/Prisma/NATS are not the first failure.\nThey are generated after `flisk project`. Fix the machine checks below.":"Machine is ready. Project wiring is not.":"",N=u.length?"":n?"Environment ready for Flisk":`This machine is ready for Flisk.
129
- ${vn()}`;return{ok:u.length===0,inProject:n,scope:i,items:u,header:D,footer:N}}function ri(e,t={}){let s=[e.header];for(let n of e.items)s.push(`FAIL ${n.id.padEnd(7)} ${n.message}`),n.fix&&s.push(` ${n.fix}`);return e.footer&&(t.includeSuccessFooter??!0)&&s.push(e.footer),s.filter(Boolean).join(`
130
- `)}async function Zc(e={}){let t=await ni({scope:e.scope}),s=bn(t.scope);t.items.some(r=>s.includes(r.id))&&(console.error(ri(t)),process.exit(1))}function el(e=w){return{hasDockerCli:!!e("docker"),hasTiltCli:!!e("tilt")}}function tl(e){let t=[];return e.hasDockerCli||t.push(`Docker is not installed. ${ut.dockerMissing}`),e.hasTiltCli||t.push(`Tilt is not installed. ${ut.tiltMissing}`),t.push("Docker and Tilt are only needed to start the stack: start them, then run `flisk doctor` (readiness) and `flisk up`."),t}export{Fr as a,Ir as b,ca as c,Ue as d,ss as e,ns as f,rs as g,Be as h,pe as i,fi as j,bi as k,Di as l,$i as m,ze as n,sr as o,ir as p,Ri as q,Ze as r,Ye as s,et as t,Os as u,tt as v,ke as w,ye as x,I as y,Yi as z,Qi as A,Zi as B,$s as C,ea as D,qs as E,zs as F,Fa as G,Ia as H,Wo as I,Sn as J,Fc as K,zo as L,Xo as M,Yo as N,Qo as O,Pn as P,ei as Q,ti as R,ni as S,ri as T,Zc as U,el as V,tl as W};
@@ -1,2 +0,0 @@
1
- import{createRequire as __fliskRequire}from'node:module';const require=__fliskRequire(import.meta.url);
2
- import{g as a,h as b,i as c,j as d,k as e}from"./chunk-CDKEHFW6.js";import"./chunk-QEBN6ZVQ.js";import"./chunk-MUDE4DZD.js";import"./chunk-GPUJ55MI.js";import"./chunk-CAEMWNPF.js";import"./chunk-ETXZ5LUG.js";import"./chunk-C4UFBXRD.js";import"./chunk-KJZUBHLO.js";export{c as cloneProjectTemplate,a as enableDiscoveredStacks,e as projectCommand,b as templateTargetDir,d as writeDefaultProjectConfig};