@stacksjs/ts-cloud 0.7.52 → 0.7.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/cli.js CHANGED
@@ -7517,7 +7517,8 @@ This password is now in your deploy log. Set TS_CLOUD_UI_PASSWORD to a value of
7517
7517
  `}function $W($,J){return[`cat > /etc/systemd/system/${$}.service <<'TS_CLOUD_UNIT_EOF'`,J.replace(/\n$/,""),"TS_CLOUD_UNIT_EOF"]}function vD($,J){return`/etc/cron.d/${$}-${J}-scheduler`}function JW($){let{slug:J,siteName:Y,site:Z}=$,G=`${$.appBase??`/var/www/${Y}`}/current`,W=XK(u9(Z)),z={current:G},U=[],X=[];(Z.queues||[]).forEach((L,M)=>{let E=Math.max(1,L.processes??1);for(let q=0;q<E;q++){let T=bD(J,Y,X.length);X.push(T),U.push(...$W(T,eG({description:`${Y} queue worker ${M}.${q} (managed by ts-cloud)`,workingDir:G,execStart:W.wrapExec(W.queueWorkerCommand(L,z)),environment:W.execEnv,stopWaitSecs:L.stopWaitSecs??90})))}}),(Z.daemons||[]).forEach((L,M)=>{let E=Math.max(1,L.processes??1);for(let q=0;q<E;q++){let T=`${yD(J,Y,L,M)}-${q}`;X.push(T),U.push(...$W(T,eG({description:`${Y} daemon ${L.name||L.command} (managed by ts-cloud)`,workingDir:L.directory||G,execStart:W.wrapExec(L.command),environment:W.execEnv,restart:L.restart,user:L.user})))}});let V=Z.scheduler,B=V===!0||typeof V==="object"&&V!==null,O=`${J}-${Y}-scheduler`;if(B&&W.schedulerMode==="daemon")X.push(O),U.push(...$W(O,eG({description:`${Y} scheduler (managed by ts-cloud)`,workingDir:G,execStart:W.wrapExec(W.schedulerCommand(z)),environment:W.execEnv})));let A=X.map((L)=>`${L}.service`).join(" ");U.push("systemctl daemon-reload",`TS_CLOUD_DESIRED="${A}"`,`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E '^${HK(J)}-${HK(Y)}-((queue|daemon)-.*|scheduler)\\.service$' || true); do`,' case " $TS_CLOUD_DESIRED " in',' *" $unit "*) ;;',' *) systemctl stop "$unit" 2>/dev/null || true; systemctl disable "$unit" 2>/dev/null || true; rm -f "/etc/systemd/system/$unit" ;;'," esac","done","systemctl daemon-reload");for(let L of X)U.push(`systemctl enable ${L}.service`,`systemctl restart ${L}.service`);let j=vD(J,Y);if(B&&W.schedulerMode==="cron"){let L=typeof V==="object"&&V!==null?V:void 0,M=`${W.schedulerCommand(z)} >> /dev/null 2>&1`;if(L?.heartbeatUrl){let q=L.heartbeatMethod||"GET",T=q==="GET"?"":`-X ${q} `;M+=` && curl -fsS -m 10 ${T}${hD(L.heartbeatUrl)} >/dev/null 2>&1`}let E=`* * * * * root ${M}
7518
7518
  `.replace(/%/g,"\\%");U.push(`cat > ${j} <<'TS_CLOUD_CRON_EOF'`,E.replace(/\n$/,""),"TS_CLOUD_CRON_EOF",`chmod 644 ${j}`)}else U.push(`rm -f ${j}`);return U}function YW($){return!!($.queues?.length||$.daemons?.length||$.scheduler)}var KK=G0(()=>{oG()});function u5($){return($.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"site").slice(0,28)}function BK($){return`web_${u5($)}`}function uD($){let J=u5($),Y=0;for(let Z=0;Z<J.length;Z++)Y=Y*31+J.charCodeAt(Z)>>>0;return fD+Y%gD}function ZW($){return`127.0.0.1:${uD($)}`}function dD($){let J=u5($.siteName),Y=BK($.siteName),Z=$.maxChildren??10;return[`; ts-cloud-managed pool for ${$.siteName} (site isolation)`,`[${J}]`,`user = ${Y}`,`group = ${Y}`,`listen = ${ZW($.siteName)}`,"pm = dynamic",`pm.max_children = ${Z}`,"pm.start_servers = 2","pm.min_spare_servers = 1","pm.max_spare_servers = 3","pm.max_requests = 500","catch_workers_output = yes",`php_admin_value[open_basedir] = ${$.appBase}:/tmp`,""].join(`
7519
7519
  `)}function OK($){let J=BK($.siteName),Y=u5($.siteName),Z=`${VK}/${Y}.conf`;return[`getent group ${J} >/dev/null || groupadd --system ${J}`,`id -u ${J} >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin -g ${J} ${J}`,`usermod -aG ${J} www-data 2>/dev/null || true`,`chown -R ${J}:${J} ${$.appBase} || true`,`chmod -R g+rX ${$.appBase} || true`,`chmod 600 ${$.appBase}/shared/.env 2>/dev/null || true`,`mkdir -p ${VK}`,`cat > ${Z} <<'TS_CLOUD_FPMPOOL_EOF'`,dD($).replace(/\n$/,""),"TS_CLOUD_FPMPOOL_EOF",`(cd ${c1} && pantry restart php-fpm) 2>/dev/null || true`]}var VK="/var/lib/pantry/php-fpm/pool.d",fD=9100,gD=400;var AK=G0(()=>{T2()});import{copyFileSync as cD}from"node:fs";import{tmpdir as mD}from"node:os";import{join as lD}from"node:path";async function QW($,J,Y=d5){let{config:Z,environment:Q,siteName:G,site:W,slug:z,sha:U,runtime:X,localTarballPath:H}=J,K=`releases/${G}/${U}.tar.gz`,V=f0(Z,Q),B=await $.getComputeOutputs({config:Z,environment:Q}),O=await $.findComputeTargets({slug:z,environment:Q,role:"app",stackName:V});if(O.length===0)return{success:!1,error:$.name==="aws"?`Stack '${V}' has no EC2 instances tagged Project=${z} Environment=${Q} Role=app, and storage/cloud/state/${V}.json pins no live instance. For a shared box, record its instanceId there.`:`No Hetzner servers labeled ts-cloud/project=${z} ts-cloud/environment=${Q} ts-cloud/role=app, and storage/cloud/state/${V}.json pins no live server. For a shared box, record its serverId there.`};let A=B.servicesPrivateIp?DF(B.servicesPrivateIp,l0(Z)):JF(l0(Z)),j=Object.keys(A).length>0?{...A,...W.env||{}}:W.env||{};if(t8(W)){let s=Z.infrastructure?.compute,m=W.phpVersion??s?.php?.default??s?.php?.versions?.[0],x=o1(z,G),n=Object.keys(A).length>0?{...W,env:j}:W,u=ZK({siteName:G,site:n,releaseId:U,appBase:x,defaultPhpVersion:m}),o=!b$(s),i=tG(W)==="custom"&&W.ssl?.certPath&&W.ssl?.keyPath?{certPath:W.ssl.certPath,keyPath:W.ssl.keyPath}:void 0,I=W.isolation?OK({siteName:G,appBase:x}):[],b=o?BG({siteName:G,domain:W.domain||G,aliases:W.aliases,type:W.type,appDir:`${x}/current`,webDirectory:W.webDirectory,phpVersion:m,fastcgiPass:W.isolation?ZW(G):void 0,redirects:W.redirects,ssl:i,auth:W.auth&&W.auth.enabled!==!1&&W.auth.password?{username:W.auth.username||"admin",password:W.auth.password,realm:W.auth.realm}:void 0,serverSnippet:VG(W.nginx,s?.nginxTemplates),clientMaxBodySize:W.nginx?.clientMaxBodySize,hsts:W.ssl?.hsts,tlsProtocols:W.ssl?.tlsProtocols,security:W.security}):[],J0=o?sG(W):[],z0=YW(W)?JW({slug:z,siteName:G,site:W,phpVersion:m,appBase:x}):[],A0=o?YK(W):[];Y.step(`Deploying PHP site '${G}' to ${O.length} target(s)...`);let Y0=await $.runRemoteDeploy({targets:O,commands:[...uG(x,z),...u,...I,...b,...J0,...z0,...A0],comment:`ts-cloud deploy ${z}/${G}@${U}`,tags:{Project:z,Environment:Q,Role:"app"}}),W0=FF(Z.notifications,W.notifications);if(!Y0.success)return await TG(W0,"deploy-failed",`❌ Deploy of ${z}/${G}@${U} failed: ${Y0.error||"unknown error"}`),{success:!1,error:Y0.error||"Remote PHP deploy failed",instanceCount:Y0.instanceCount,perInstance:Y0.perInstance};let P0=W.domain?` → https://${W.domain}`:"";return await TG(W0,"deploy",`✅ Deployed ${z}/${G}@${U}${P0}`),{success:!0,instanceCount:Y0.instanceCount,perInstance:Y0.perInstance}}if(!H)return{success:!1,error:`Site '${G}' requires a release tarball but none was provided`};let L=await $.uploadRelease({config:Z,environment:Q,localPath:H,remoteKey:K,targets:O}),M=U6(z,G,U),E=$.name==="aws"?dG(B.deployBucketName,K,Z.project.region||"us-east-1",M):cG(L.artifactRef,M),q=t0(W),T=o1(z,G),D=q==="server-static"?h5({siteName:G,slug:z,appDir:T,artifactFetch:E,releaseId:U,preStartCommands:W.preStart}):k5({siteName:G,slug:z,appDir:T,artifactFetch:E,releaseId:U,execStart:I5(W.start,X),envEntries:j,port:W.port,preStartCommands:W.preStart,sharedPaths:W.sharedPaths,zeroDowntime:W.zeroDowntime!==!1,healthCheckPath:W.healthCheck?.path}),N=Z.infrastructure?.compute,C=q==="server-static"&&!b$(N)&&!!W.domain,S=C?BG({siteName:G,domain:W.domain,aliases:W.aliases,type:W.type==="spa"?"spa":"static",appDir:`${T}/current`,webDirectory:"",redirects:W.redirects,auth:W.auth&&W.auth.enabled!==!1&&W.auth.password?{username:W.auth.username||"admin",password:W.auth.password,realm:W.auth.realm}:void 0,serverSnippet:VG(W.nginx,N?.nginxTemplates),clientMaxBodySize:W.nginx?.clientMaxBodySize,hsts:W.ssl?.hsts,tlsProtocols:W.ssl?.tlsProtocols,security:W.security}):[],k=C?sG(W):[],f=YW(W)?JW({slug:z,siteName:G,site:W,appBase:T}):[],g=[...x5(T,{releaseId:U,commit:U,branch:W.repository?.branch??"main"}),...uG(T,z),...D,...S,...k,...f,...b5()];Y.step(`Deploying to ${O.length} target(s)...`);let c=await $.runRemoteDeploy({targets:O,commands:g,comment:`ts-cloud deploy ${z}/${G}@${U}`,tags:{Project:z,Environment:Q,Role:"app"}});if(!c.success)return{success:!1,error:c.error||"Remote deploy failed",instanceCount:c.instanceCount,perInstance:c.perInstance};return{success:!0,instanceCount:c.instanceCount,perInstance:c.perInstance}}async function pD($,J,Y){let{config:Z,environment:Q}=J;if(!Z.cloud?.attachTo)return!0;let G=Z.infrastructure?.compute,W=l0(Z),z=q9(W,G?.managedServices??{});if(z.length===0)return!0;let U=Z.project.slug,X=f0(Z,Q),H=await $.findComputeTargets({slug:U,environment:Q,role:"app",stackName:X});if(H.length===0)return!0;Y.step(`Ensuring database '${W?.name}' + role exist on the shared box...`);let K=await $.runRemoteDeploy({targets:H,commands:z,comment:`ts-cloud ensure database ${U}/${W?.name}`,tags:{Project:U,Environment:Q,Role:"app"}});if(!K.success)return Y.error(`Ensuring database '${W?.name}' failed: ${K.error||"unknown error"}`),!1;return Y.success(`Database '${W?.name}' is ready on ${K.instanceCount} target(s)`),!0}async function c5($){let{config:J,environment:Y,driver:Z,sha:Q,runtime:G,tarballForSite:W,logger:z=d5}=$,U=J.project.slug,X=$.cwd??process.cwd(),H=P$(J);g9(J,{cwd:X,logger:{info:z.info,warn:z.warn}});let K=H?[]:aG(J);if($.rpxConfig&&$.rpxConfig!==J&&!P$($.rpxConfig))g9($.rpxConfig,{cwd:X,logger:{info:()=>{},warn:()=>{}}});let V=null,B=new Map;if(K.length>0)if(V=X6(J.sites?.[K[0]],{cwd:X,slug:U,sha:Q,siteName:K[0],logger:{info:z.info,warn:z.warn}}),!V){z.warn("Management dashboard: no artifact available — skipping dashboard site(s) for this deploy.");for(let M of K)if(J.sites)delete J.sites[M]}else for(let M of K){if(M===K[0]){B.set(M,V);continue}try{let E=lD(mD(),`${U}-${M}-${Q}.tar.gz`);cD(V,E),B.set(M,E)}catch{B.set(M,V)}}let O=J.sites||{},A=Object.entries(O).filter(([M,E])=>{if(!E)return!1;let q=t0(E);if(q==="bucket")return z.warn(`Site '${M}' targets a bucket — skipping (handled by the static-site path, not compute).`),!1;if(q==="redirect")return!1;return!0});if(A.length===0)return!0;if(!await pD(Z,$,z))return!1;let j=(M)=>B.get(M)??W(M);for(let[M,E]of A){z.step(`Deploying site: ${M}`);let q=t8(E)?void 0:j(M),T=await QW(Z,{config:J,environment:Y,siteName:M,site:E,slug:U,sha:Q,runtime:G,localTarballPath:q},z);if(!T.success){if(z.error(`Deploy of '${M}' failed: ${T.error||"unknown error"}`),T.perInstance)for(let D of T.perInstance)z.error(` ${D.instanceId}: ${D.status}${D.error?` — ${D.error}`:""}`);return!1}if(z.success(`Deployed ${U}/${M}@${Q} to ${T.instanceCount} target(s)`),T.perInstance)for(let D of T.perInstance)z.info(` ✓ ${D.instanceId}: ${D.status}`)}if(!await jK($))return!1;return!0}async function jK($){let{config:J,environment:Y,driver:Z,logger:Q=d5}=$,G=$.rpxConfig??J,W=G.infrastructure?.compute?.proxy??J.infrastructure?.compute?.proxy;if(W?.engine!=="rpx")return!0;let z=G.sites||{},U=J.project.slug,X=f0(J,Y),H=await Z.findComputeTargets({slug:U,environment:Y,role:"lb",stackName:X});if(H.length>0){let j=(await Z.findComputeTargets({slug:U,environment:Y,role:"app",stackName:X})).map((E)=>({privateIp:E.privateIp,publicIp:E.publicIp})),L=D9(z,j,{proxy:W,slug:U});if(L.proxies.length===0)return Q.warn("rpx gateway: no server sites with a domain to route — skipping gateway reload."),!0;Q.step(`Reloading the load balancer's rpx gateway with ${L.proxies.length} route(s)...`);let M=await Z.runRemoteDeploy({targets:H,commands:s8({config:L,slug:U}),comment:`ts-cloud rpx gateway reload ${U}`,tags:{Project:U,Environment:Y,Role:"lb"}});if(!M.success)return Q.error(`rpx gateway reload failed: ${M.error||"unknown error"}`),!1;return Q.success(`rpx gateway reloaded on ${M.instanceCount} load balancer(s)`),!0}let K=X$(z,{proxy:W,slug:U});if(K.proxies.length===0)return Q.warn("rpx gateway: no server sites with a domain to route — skipping gateway reload."),!0;let V=await Z.findComputeTargets({slug:U,environment:Y,role:"app",stackName:X});if(V.length===0)return Q.warn("rpx gateway: no compute targets found — skipping gateway reload."),!0;Q.step(`Reloading rpx gateway with ${K.proxies.length} route(s)...`);let B=H$({proxy:W,config:K,slug:U}),O=await Z.runRemoteDeploy({targets:V,commands:B,comment:`ts-cloud rpx gateway reload ${U}`,tags:{Project:U,Environment:Y,Role:"app"}});if(!O.success)return Q.error(`rpx gateway reload failed: ${O.error||"unknown error"}`),!1;return Q.success(`rpx gateway reloaded on ${O.instanceCount} target(s)`),!0}async function GW($){let{config:J,environment:Y,driver:Z,logger:Q=d5}=$,G=$.rpxConfig??J,W=G.infrastructure?.compute?.proxy??J.infrastructure?.compute?.proxy;if(W?.engine!=="rpx"||!W.onDemandTls)return!0;if(X$(G.sites||{},{proxy:W,slug:J.project.slug}).proxies.length===0)return!0;let U=J.project.slug,X=f0(J,Y),H="lb",K=await Z.findComputeTargets({slug:U,environment:Y,role:H,stackName:X});if(K.length===0)H="app",K=await Z.findComputeTargets({slug:U,environment:Y,role:H,stackName:X});if(K.length===0)return Q.warn("rpx TLS: no gateway targets found - skipping certificate issuance."),!0;Q.step("Issuing rpx TLS certificates after DNS reconciliation...");let V=await Z.runRemoteDeploy({targets:K,commands:[`systemctl start ${EG(U)}`],comment:`ts-cloud rpx TLS issuance ${U}`,tags:{Project:U,Environment:Y,Role:H}});if(!V.success)return Q.error(`rpx TLS issuance failed: ${V.error||"unknown error"}`),!1;return Q.success(`rpx TLS certificates reconciled on ${V.instanceCount} gateway target(s)`),!0}var d5;var WW=G0(async()=>{mG();sF();T9();QK();KK();O5();AK();z6();await O2([x0(),H6(),U$(),xG(),w9()]);d5={info:()=>{},warn:()=>{},error:()=>{},step:()=>{},success:()=>{}}});var d$=G0(async()=>{R5();SG();fG();fF();mG();await O2([wG(),bG(),WW(),w9()]);kF()});function $w($){return`'${$.split("'").join("'\\''")}'`}function _K($){let J=$.user||"root",Y=$.script.join(`
7520
- `),Z=J==="root"?'bash -l "$TS_CLOUD_RECIPE"':`runuser -l ${$w(J)} -c "bash $TS_CLOUD_RECIPE"`;return["set -uo pipefail",`echo "__TS_CLOUD_RECIPE_BEGIN__ ${$.name} (user=${J})"`,"TS_CLOUD_RECIPE=$(mktemp)",`cat > "$TS_CLOUD_RECIPE" <<'TS_CLOUD_RECIPE_EOF'`,Y,"TS_CLOUD_RECIPE_EOF",'chmod +r "$TS_CLOUD_RECIPE"',Z,"TS_CLOUD_RC=$?",'rm -f "$TS_CLOUD_RECIPE"',`echo "__TS_CLOUD_RECIPE_END__ ${$.name} exit=$TS_CLOUD_RC"`,"exit $TS_CLOUD_RC"]}var K6={};u0(K6,{runComputeRecipe:()=>Yw,rollbackComputeSite:()=>UW,restoreDatabaseBackup:()=>XW,getComputeDeployHistory:()=>Jw});async function m5($){return await $.driver.findComputeTargets({slug:$.slug,environment:$.environment,role:$.role||"app"})}async function UW($,J){let Y=$.logger||zW,Z=await m5($);if(Z.length===0)return{success:!1,error:`No '${$.role||"app"}' servers found for ${$.slug}/${$.environment}.`};let Q=o1($.slug,J.siteName),G=g$(Q,J.to||"unused"),W=["set -uo pipefail",...gF(G,{...J.to?{to:J.to}:{},unitBase:`${$.slug}-${J.siteName}`}),`(cd ${c1} && pantry restart php-fpm) 2>/dev/null || true`,`(cd ${G.current} && eval "$(cd ${c1} && pantry env 2>/dev/null)" && php artisan queue:restart) 2>/dev/null || true`];Y.step(`Rolling back ${J.siteName}${J.to?` to ${J.to}`:" to the previous release"} on ${Z.length} server(s)...`);let z=await $.driver.runRemoteDeploy({targets:Z,commands:W,comment:`ts-cloud rollback ${$.slug}/${J.siteName}`,tags:{Project:$.slug,Environment:$.environment,Role:$.role||"app"}});if(!z.success)Y.error(`Rollback failed: ${z.error||"unknown error"}`);else Y.success(`Rolled back ${J.siteName}.`);return{success:z.success,error:z.error,perInstance:z.perInstance}}async function Jw($,J){let Y=await m5($);if(Y.length===0)return{success:!1,error:`No '${$.role||"app"}' servers found for ${$.slug}/${$.environment}.`};let Z=gG(o1($.slug,J.siteName)),Q=Math.max(1,J.limit??20),G=[`[ -f ${Z} ] && tail -n ${Q} ${Z} || echo "no deploy history for ${J.siteName}"`],W=await $.driver.runRemoteDeploy({targets:Y,commands:G,comment:`ts-cloud deploy:history ${$.slug}/${J.siteName}`,tags:{Project:$.slug,Environment:$.environment,Role:$.role||"app"}});return{success:W.success,error:W.error,perInstance:W.perInstance}}async function Yw($,J){let Y=$.logger||zW,Z=await m5($);if(Z.length===0)return{success:!1,error:`No '${$.role||"app"}' servers found for ${$.slug}/${$.environment}.`};Y.step(`Running recipe '${J.name}' as ${J.user||"root"} on ${Z.length} server(s)...`);let Q=await $.driver.runRemoteDeploy({targets:Z,commands:_K({name:J.name,script:J.script,user:J.user}),comment:`ts-cloud recipe ${J.name}`,tags:{Project:$.slug,Environment:$.environment,Role:$.role||"app"}});if(!Q.success)Y.error(`Recipe '${J.name}' failed: ${Q.error||"unknown error"}`);else Y.success(`Recipe '${J.name}' completed.`);return{success:Q.success,error:Q.error,perInstance:Q.perInstance}}async function XW($,J){let Y=$.logger||zW;if(!J.database?.name)return{success:!1,error:"No appDatabase configured to restore."};let Z=await m5($);if(Z.length===0)return{success:!1,error:`No '${$.role||"app"}' servers found for ${$.slug}/${$.environment}.`};Y.step(`Restoring database '${J.database.name}'${J.from?` from ${J.from}`:" from the latest backup"} on ${Z.length} server(s)...`);let Q=await $.driver.runRemoteDeploy({targets:Z,commands:VF(J.database,{from:J.from}),comment:`ts-cloud db:restore ${$.slug}/${J.database.name}`,tags:{Project:$.slug,Environment:$.environment,Role:$.role||"app"}});if(!Q.success)Y.error(`Restore failed: ${Q.error||"unknown error"}`);else Y.success(`Restored ${J.database.name}.`);return{success:Q.success,error:Q.error,perInstance:Q.perInstance}}var zW;var d9=G0(async()=>{$6();T2();z6();await U$();zW={info:()=>{},warn:()=>{},error:()=>{},step:()=>{},success:()=>{}}});var MK={};u0(MK,{inferQuickDeployProvider:()=>Qw,buildQuickDeployCi:()=>zw});function Zw($){let J=$.sites||{};for(let Y of Object.values(J))if(Y?.repository?.url)return{provider:Y.repository.provider,branch:Y.repository.branch};return}function Qw($){let J=$.trim().toLowerCase();if(J.includes("github.com"))return"github";if(J.includes("gitlab.com"))return"gitlab";if(J.includes("bitbucket.org"))return"bitbucket";return}function LK($){return`'${$.replace(/'/g,"'\\''")}'`}function Gw($){return $.replace(/'/g,"''")}function Ww($,J){let Y=["bunx --bun @stacksjs/ts-cloud deploy","--env",LK($)];if(J.site)Y.push("--site",LK(J.site));if(J.skipDnsVerification)Y.push("--skip-dns-verification");return Y.push("--yes"),Y.join(" ")}function zw($,J="production",Y={}){let Z=Zw($),Q=Y.provider||Z?.provider;if(Q!=="github"&&Q!=="gitlab"&&Q!=="bitbucket")return null;let W=$.environments?.[J]?.deployBranch||Z?.branch||"main",z=Ww(J,Y),U=JSON.stringify(W),X=JSON.stringify(J),H=Y.setup==="pantry"?` - name: Setup Pantry
7520
+ `),Z=J==="root"?'bash -l "$TS_CLOUD_RECIPE"':`runuser -l ${$w(J)} -c "bash $TS_CLOUD_RECIPE"`;return["set -uo pipefail",`echo "__TS_CLOUD_RECIPE_BEGIN__ ${$.name} (user=${J})"`,"TS_CLOUD_RECIPE=$(mktemp)",`cat > "$TS_CLOUD_RECIPE" <<'TS_CLOUD_RECIPE_EOF'`,Y,"TS_CLOUD_RECIPE_EOF",'chmod +r "$TS_CLOUD_RECIPE"',Z,"TS_CLOUD_RC=$?",'rm -f "$TS_CLOUD_RECIPE"',`echo "__TS_CLOUD_RECIPE_END__ ${$.name} exit=$TS_CLOUD_RC"`,"exit $TS_CLOUD_RC"]}var K6={};u0(K6,{runComputeRecipe:()=>Yw,rollbackComputeSite:()=>UW,restoreDatabaseBackup:()=>XW,getComputeDeployHistory:()=>Jw});async function m5($){return await $.driver.findComputeTargets({slug:$.slug,environment:$.environment,role:$.role||"app"})}async function UW($,J){let Y=$.logger||zW,Z=await m5($);if(Z.length===0)return{success:!1,error:`No '${$.role||"app"}' servers found for ${$.slug}/${$.environment}.`};let Q=o1($.slug,J.siteName),G=g$(Q,J.to||"unused"),W=["set -uo pipefail",...gF(G,{...J.to?{to:J.to}:{},unitBase:`${$.slug}-${J.siteName}`}),`(cd ${c1} && pantry restart php-fpm) 2>/dev/null || true`,`(cd ${G.current} && eval "$(cd ${c1} && pantry env 2>/dev/null)" && php artisan queue:restart) 2>/dev/null || true`];Y.step(`Rolling back ${J.siteName}${J.to?` to ${J.to}`:" to the previous release"} on ${Z.length} server(s)...`);let z=await $.driver.runRemoteDeploy({targets:Z,commands:W,comment:`ts-cloud rollback ${$.slug}/${J.siteName}`,tags:{Project:$.slug,Environment:$.environment,Role:$.role||"app"}});if(!z.success)Y.error(`Rollback failed: ${z.error||"unknown error"}`);else Y.success(`Rolled back ${J.siteName}.`);return{success:z.success,error:z.error,perInstance:z.perInstance}}async function Jw($,J){let Y=await m5($);if(Y.length===0)return{success:!1,error:`No '${$.role||"app"}' servers found for ${$.slug}/${$.environment}.`};let Z=gG(o1($.slug,J.siteName)),Q=Math.max(1,J.limit??20),G=[`[ -f ${Z} ] && tail -n ${Q} ${Z} || echo "no deploy history for ${J.siteName}"`],W=await $.driver.runRemoteDeploy({targets:Y,commands:G,comment:`ts-cloud deploy:history ${$.slug}/${J.siteName}`,tags:{Project:$.slug,Environment:$.environment,Role:$.role||"app"}});return{success:W.success,error:W.error,perInstance:W.perInstance}}async function Yw($,J){let Y=$.logger||zW,Z=await m5($);if(Z.length===0)return{success:!1,error:`No '${$.role||"app"}' servers found for ${$.slug}/${$.environment}.`};Y.step(`Running recipe '${J.name}' as ${J.user||"root"} on ${Z.length} server(s)...`);let Q=await $.driver.runRemoteDeploy({targets:Z,commands:_K({name:J.name,script:J.script,user:J.user}),comment:`ts-cloud recipe ${J.name}`,tags:{Project:$.slug,Environment:$.environment,Role:$.role||"app"}});if(!Q.success)Y.error(`Recipe '${J.name}' failed: ${Q.error||"unknown error"}`);else Y.success(`Recipe '${J.name}' completed.`);return{success:Q.success,error:Q.error,perInstance:Q.perInstance}}async function XW($,J){let Y=$.logger||zW;if(!J.database?.name)return{success:!1,error:"No appDatabase configured to restore."};let Z=await m5($);if(Z.length===0)return{success:!1,error:`No '${$.role||"app"}' servers found for ${$.slug}/${$.environment}.`};Y.step(`Restoring database '${J.database.name}'${J.from?` from ${J.from}`:" from the latest backup"} on ${Z.length} server(s)...`);let Q=await $.driver.runRemoteDeploy({targets:Z,commands:VF(J.database,{from:J.from}),comment:`ts-cloud db:restore ${$.slug}/${J.database.name}`,tags:{Project:$.slug,Environment:$.environment,Role:$.role||"app"}});if(!Q.success)Y.error(`Restore failed: ${Q.error||"unknown error"}`);else Y.success(`Restored ${J.database.name}.`);return{success:Q.success,error:Q.error,perInstance:Q.perInstance}}var zW;var d9=G0(async()=>{$6();T2();z6();await U$();zW={info:()=>{},warn:()=>{},error:()=>{},step:()=>{},success:()=>{}}});var MK={};u0(MK,{inferQuickDeployProvider:()=>Qw,buildQuickDeployCi:()=>zw});function Zw($){let J=$.sites||{};for(let Y of Object.values(J))if(Y?.repository?.url)return{provider:Y.repository.provider,branch:Y.repository.branch};return}function Qw($){let J=$.trim().toLowerCase();if(J.includes("github.com"))return"github";if(J.includes("gitlab.com"))return"gitlab";if(J.includes("bitbucket.org"))return"bitbucket";return}function LK($){return`'${$.replace(/'/g,"'\\''")}'`}function Gw($){return $.replace(/'/g,"''")}function Ww($,J){let Y=["bunx --bun @stacksjs/ts-cloud deploy","--env",LK($)];if(J.site)Y.push("--site",LK(J.site));if(J.skipDnsVerification)Y.push("--skip-dns-verification");return Y.push("--yes"),Y.join(" ")}function zw($,J="production",Y={}){let Z=Zw($),Q=Y.provider||Z?.provider;if(Q!=="github"&&Q!=="gitlab"&&Q!=="bitbucket")return null;let G=$.environments?.[J],W=G?.deployBranch||Z?.branch||"main",z=Ww(J,Y),U=JSON.stringify(W),X=JSON.stringify(J),H=G?.domain?G.domain.startsWith("http")?G.domain:`https://${G.domain}`:void 0,K=H?`
7521
+ url: ${JSON.stringify(H)}`:"",V=Y.setup==="pantry"?` - name: Setup Pantry
7521
7522
  uses: home-lang/pantry/packages/action@main`:` - uses: oven-sh/setup-bun@v2
7522
7523
  - run: bun install --frozen-lockfile`;if(Q==="github")return{provider:Q,branch:W,path:".github/workflows/deploy.yml",content:`name: Deploy ${J}
7523
7524
  run-name: Deploy ${J} from \${{ github.ref_name }}
@@ -7539,10 +7540,10 @@ jobs:
7539
7540
  if: github.ref_name == '${Gw(W)}'
7540
7541
  runs-on: ubuntu-latest
7541
7542
  environment:
7542
- name: ${X}
7543
+ name: ${X}${K}
7543
7544
  steps:
7544
7545
  - uses: actions/checkout@v6
7545
- ${H}
7546
+ ${V}
7546
7547
  # Provide the provider credentials your config needs as repo secrets.
7547
7548
  - run: ${z}
7548
7549
  env:
@@ -7622,7 +7623,7 @@ Run \`${this.name??"cli"} --help\` for usage.`;m2.stderr.write(`${G}${Q}${W}
7622
7623
  `).length-1;this.output.write(D2.move(-999,$*-1))}render(){let $=R4(this._render(this)??"",T4.stdout.columns,{hard:!0,trim:!1});if($===this._prevFrame)return;if(this.state==="initial")this.output.write(D2.hide);else{let J=FO(this._prevFrame,$);if(this.restoreCursor(),J&&J?.length===1){let Y=J[0];this.output.write(D2.move(0,Y)),this.output.write(FJ.lines(1));let Z=$.split(`
7623
7624
  `);this.output.write(Z[Y]),this._prevFrame=$,this.output.write(D2.move(0,Z.length-Y-1));return}if(J&&J?.length>1){let Y=J[0];this.output.write(D2.move(0,Y)),this.output.write(FJ.down());let Q=$.split(`
7624
7625
  `).slice(Y);this.output.write(Q.join(`
7625
- `)),this._prevFrame=$;return}this.output.write(FJ.down())}if(this.output.write($),this.state==="initial")this.state="active";this._prevFrame=$}}function jO($,J){if($===void 0)return 0;if(J.length===0)return 0;let Z=J.findIndex((Q)=>Q.value===$);return Z!==-1?Z:0}function _O($,J){return(J.label??String(J.value)).toLowerCase().includes($.toLowerCase())}function LO($,J){if(!J)return;if($)return J;return J[0]}class MO extends jJ{filteredOptions;multiple;isNavigating=!1;selectedValues=[];focusedValue;#$=0;#Y="";#Z;#J;get cursor(){return this.#$}get userInputWithCursor(){if(!this.userInput)return D6.inverse(D6.hidden("_"));if(this._cursor>=this.userInput.length)return`${this.userInput}█`;let $=this.userInput.slice(0,this._cursor),[J,...Y]=this.userInput.slice(this._cursor);return`${$}${D6.inverse(J)}${Y.join("")}`}get options(){if(typeof this.#J==="function")return this.#J();return this.#J}constructor($){super($);this.#J=$.options;let J=this.options;this.filteredOptions=[...J],this.multiple=$.multiple===!0,this.#Z=$.filter??_O;let Y;if($.initialValue&&Array.isArray($.initialValue))if(this.multiple)Y=$.initialValue;else Y=$.initialValue.slice(0,1);else if(!this.multiple&&this.options.length>0)Y=[this.options[0].value];if(Y)for(let Z of Y){let Q=J.findIndex((G)=>G.value===Z);if(Q!==-1)this.toggleSelected(Z),this.#$=Q}this.focusedValue=this.options[this.#$]?.value,this.on("key",(Z,Q)=>this.#Q(Z,Q)),this.on("userInput",(Z)=>this.#G(Z))}_isActionKey($,J){return $==="\t"||this.multiple&&this.isNavigating&&J.name==="space"&&$!==void 0&&$!==""}#Q($,J){let Y=J.name==="up",Z=J.name==="down",Q=J.name==="return";if(Y||Z){if(this.#$=Math.max(0,Math.min(this.#$+(Y?-1:1),this.filteredOptions.length-1)),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)this.selectedValues=[this.focusedValue];this.isNavigating=!0}else if(Q)this.value=LO(this.multiple,this.selectedValues);else if(this.multiple)if(this.focusedValue!==void 0&&(J.name==="tab"||this.isNavigating&&J.name==="space"))this.toggleSelected(this.focusedValue);else this.isNavigating=!1;else{if(this.focusedValue)this.selectedValues=[this.focusedValue];this.isNavigating=!1}}deselectAll(){this.selectedValues=[]}toggleSelected($){if(this.filteredOptions.length===0)return;if(this.multiple)if(this.selectedValues.includes($))this.selectedValues=this.selectedValues.filter((J)=>J!==$);else this.selectedValues=[...this.selectedValues,$];else this.selectedValues=[$]}#G($){if($!==this.#Y){this.#Y=$;let J=this.options;if($)this.filteredOptions=J.filter((Y)=>this.#Z($,Y));else this.filteredOptions=[...J];if(this.#$=jO(this.focusedValue,this.filteredOptions),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)if(this.focusedValue!==void 0)this.toggleSelected(this.focusedValue);else this.deselectAll()}}}class EO extends jJ{options;cursor=0;#$;getGroupItems($){return this.options.filter((J)=>J.group===$)}isGroupSelected($){let J=this.getGroupItems($),Y=this.value;if(Y===void 0)return!1;return J.every((Z)=>Y.includes(Z.value))}toggleValue(){let $=this.options[this.cursor];if(this.value===void 0)this.value=[];if($.group===!0){let J=String($.value),Y=this.getGroupItems(J);if(this.isGroupSelected(J))this.value=this.value.filter((Z)=>Y.findIndex((Q)=>Q.value===Z)===-1);else this.value=[...this.value,...Y.map((Z)=>Z.value)];this.value=Array.from(new Set(this.value))}else{let J=this.value.includes($.value);this.value=J?this.value.filter((Y)=>Y!==$.value):[...this.value,$.value]}}constructor($){super($,!1);let{options:J}=$;this.#$=$.selectableGroups!==!1,this.options=Object.entries(J).flatMap(([Y,Z])=>[{value:Y,group:!0,label:Y},...Z.map((Q)=>({...Q,group:Y}))]),this.value=[...$.initialValues??[]],this.cursor=Math.max(this.options.findIndex(({value:Y})=>Y===$.cursorAt),this.#$?0:1),this.on("cursor",(Y)=>{switch(Y){case"left":case"up":{this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;let Z=this.options[this.cursor]?.group===!0;if(!this.#$&&Z)this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;break}case"down":case"right":{this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;let Z=this.options[this.cursor]?.group===!0;if(!this.#$&&Z)this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;break}case"space":this.toggleValue();break}})}}var qO=CB();var I0=($,J)=>qO?$:J,aP=I0("◆","*"),tP=I0("■","x"),sP=I0("▲","x"),oP=I0("◇","o"),eP=I0("┌","T"),TO=I0("│","|"),$S=I0("└","—"),JS=I0("●",">"),YS=I0("○"," "),ZS=I0("◻","[•]"),QS=I0("◼","[+]"),GS=I0("◻","[ ]"),WS=I0("▪","•"),zS=I0("─","-"),US=I0("╮","+"),XS=I0("├","+"),HS=I0("╯","+"),FS=I0("●","•"),KS=I0("◆","*"),VS=I0("▲","!"),BS=I0("■","x");var OS={light:I0("─","-"),heavy:I0("━","="),block:I0("█","#")};function RO(){return`${D6.gray(TO)} `}var AS=RO();var _J="0.7.52";B0();import{existsSync as PJ}from"node:fs";import{mkdir as bO,writeFile as c4}from"node:fs/promises";function SJ($){$.command("init","Initialize a new ts-cloud project").option("--mode <mode>","Deployment mode: server or serverless").option("--name <name>","Project name").option("--region <region>","AWS Region").action(async(J)=>{if(w("Initializing ts-cloud Project"),PJ("cloud.config.ts")){if(!await p("cloud.config.ts already exists. Overwrite?",!1)){F("Initialization cancelled");return}}let Y=J?.name||await S0("Project name","my-app"),Z=J?.mode||await P6("Select deployment mode",["serverless","server"]),Q=J?.region||await P6("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),G=new P("Creating configuration file...");G.start();let W=`import { defineConfig } from '@ts-cloud/core'
7626
+ `)),this._prevFrame=$;return}this.output.write(FJ.down())}if(this.output.write($),this.state==="initial")this.state="active";this._prevFrame=$}}function jO($,J){if($===void 0)return 0;if(J.length===0)return 0;let Z=J.findIndex((Q)=>Q.value===$);return Z!==-1?Z:0}function _O($,J){return(J.label??String(J.value)).toLowerCase().includes($.toLowerCase())}function LO($,J){if(!J)return;if($)return J;return J[0]}class MO extends jJ{filteredOptions;multiple;isNavigating=!1;selectedValues=[];focusedValue;#$=0;#Y="";#Z;#J;get cursor(){return this.#$}get userInputWithCursor(){if(!this.userInput)return D6.inverse(D6.hidden("_"));if(this._cursor>=this.userInput.length)return`${this.userInput}█`;let $=this.userInput.slice(0,this._cursor),[J,...Y]=this.userInput.slice(this._cursor);return`${$}${D6.inverse(J)}${Y.join("")}`}get options(){if(typeof this.#J==="function")return this.#J();return this.#J}constructor($){super($);this.#J=$.options;let J=this.options;this.filteredOptions=[...J],this.multiple=$.multiple===!0,this.#Z=$.filter??_O;let Y;if($.initialValue&&Array.isArray($.initialValue))if(this.multiple)Y=$.initialValue;else Y=$.initialValue.slice(0,1);else if(!this.multiple&&this.options.length>0)Y=[this.options[0].value];if(Y)for(let Z of Y){let Q=J.findIndex((G)=>G.value===Z);if(Q!==-1)this.toggleSelected(Z),this.#$=Q}this.focusedValue=this.options[this.#$]?.value,this.on("key",(Z,Q)=>this.#Q(Z,Q)),this.on("userInput",(Z)=>this.#G(Z))}_isActionKey($,J){return $==="\t"||this.multiple&&this.isNavigating&&J.name==="space"&&$!==void 0&&$!==""}#Q($,J){let Y=J.name==="up",Z=J.name==="down",Q=J.name==="return";if(Y||Z){if(this.#$=Math.max(0,Math.min(this.#$+(Y?-1:1),this.filteredOptions.length-1)),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)this.selectedValues=[this.focusedValue];this.isNavigating=!0}else if(Q)this.value=LO(this.multiple,this.selectedValues);else if(this.multiple)if(this.focusedValue!==void 0&&(J.name==="tab"||this.isNavigating&&J.name==="space"))this.toggleSelected(this.focusedValue);else this.isNavigating=!1;else{if(this.focusedValue)this.selectedValues=[this.focusedValue];this.isNavigating=!1}}deselectAll(){this.selectedValues=[]}toggleSelected($){if(this.filteredOptions.length===0)return;if(this.multiple)if(this.selectedValues.includes($))this.selectedValues=this.selectedValues.filter((J)=>J!==$);else this.selectedValues=[...this.selectedValues,$];else this.selectedValues=[$]}#G($){if($!==this.#Y){this.#Y=$;let J=this.options;if($)this.filteredOptions=J.filter((Y)=>this.#Z($,Y));else this.filteredOptions=[...J];if(this.#$=jO(this.focusedValue,this.filteredOptions),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)if(this.focusedValue!==void 0)this.toggleSelected(this.focusedValue);else this.deselectAll()}}}class EO extends jJ{options;cursor=0;#$;getGroupItems($){return this.options.filter((J)=>J.group===$)}isGroupSelected($){let J=this.getGroupItems($),Y=this.value;if(Y===void 0)return!1;return J.every((Z)=>Y.includes(Z.value))}toggleValue(){let $=this.options[this.cursor];if(this.value===void 0)this.value=[];if($.group===!0){let J=String($.value),Y=this.getGroupItems(J);if(this.isGroupSelected(J))this.value=this.value.filter((Z)=>Y.findIndex((Q)=>Q.value===Z)===-1);else this.value=[...this.value,...Y.map((Z)=>Z.value)];this.value=Array.from(new Set(this.value))}else{let J=this.value.includes($.value);this.value=J?this.value.filter((Y)=>Y!==$.value):[...this.value,$.value]}}constructor($){super($,!1);let{options:J}=$;this.#$=$.selectableGroups!==!1,this.options=Object.entries(J).flatMap(([Y,Z])=>[{value:Y,group:!0,label:Y},...Z.map((Q)=>({...Q,group:Y}))]),this.value=[...$.initialValues??[]],this.cursor=Math.max(this.options.findIndex(({value:Y})=>Y===$.cursorAt),this.#$?0:1),this.on("cursor",(Y)=>{switch(Y){case"left":case"up":{this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;let Z=this.options[this.cursor]?.group===!0;if(!this.#$&&Z)this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;break}case"down":case"right":{this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;let Z=this.options[this.cursor]?.group===!0;if(!this.#$&&Z)this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;break}case"space":this.toggleValue();break}})}}var qO=CB();var I0=($,J)=>qO?$:J,aP=I0("◆","*"),tP=I0("■","x"),sP=I0("▲","x"),oP=I0("◇","o"),eP=I0("┌","T"),TO=I0("│","|"),$S=I0("└","—"),JS=I0("●",">"),YS=I0("○"," "),ZS=I0("◻","[•]"),QS=I0("◼","[+]"),GS=I0("◻","[ ]"),WS=I0("▪","•"),zS=I0("─","-"),US=I0("╮","+"),XS=I0("├","+"),HS=I0("╯","+"),FS=I0("●","•"),KS=I0("◆","*"),VS=I0("▲","!"),BS=I0("■","x");var OS={light:I0("─","-"),heavy:I0("━","="),block:I0("█","#")};function RO(){return`${D6.gray(TO)} `}var AS=RO();var _J="0.7.53";B0();import{existsSync as PJ}from"node:fs";import{mkdir as bO,writeFile as c4}from"node:fs/promises";function SJ($){$.command("init","Initialize a new ts-cloud project").option("--mode <mode>","Deployment mode: server or serverless").option("--name <name>","Project name").option("--region <region>","AWS Region").action(async(J)=>{if(w("Initializing ts-cloud Project"),PJ("cloud.config.ts")){if(!await p("cloud.config.ts already exists. Overwrite?",!1)){F("Initialization cancelled");return}}let Y=J?.name||await S0("Project name","my-app"),Z=J?.mode||await P6("Select deployment mode",["serverless","server"]),Q=J?.region||await P6("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),G=new P("Creating configuration file...");G.start();let W=`import { defineConfig } from '@ts-cloud/core'
7626
7627
 
7627
7628
  export default defineConfig({
7628
7629
  project: {
@@ -8159,7 +8160,7 @@ To proceed anyway, use --skip-security-scan flag`);return}y(`✓ Security scan p
8159
8160
  `);if(F(`Cluster: ${Q}`),F(`Service: ${G}`),F(`Repository: ${W}`),F(`Image Tag: ${z}`),F(`Dockerfile: ${U}`),!await p(`
8160
8161
  Deploy container now?`,!0)){F("Deployment cancelled");return}let B=new A9(Z),O=new Z5(Z),A=new P("Getting ECR credentials...");A.start();let j=await B.getAuthorizationToken();if(!j.authorizationData?.[0]){A.fail("Failed to get ECR credentials");return}let L=j.authorizationData[0],E=(L.proxyEndpoint||"").replace("https://","");A.succeed("ECR credentials obtained");let q=new P("Logging into ECR...");q.start();let T=L.authorizationToken||"",N=Buffer.from(T,"base64").toString("utf8").split(":")[1],{spawn:C}=await import("child_process"),S=C("docker",["login","--username","AWS","--password-stdin",E],{stdio:["pipe","pipe","pipe"]});S.stdin.write(N),S.stdin.end(),await new Promise((x,n)=>{S.on("close",(u)=>{if(u===0)x();else n(Error(`Docker login failed with code ${u}`))})}),q.succeed("Logged into ECR");let k=new P("Building Docker image...");k.start();let f=`${E}/${W}:${z}`,g=C("docker",["build","-t",f,"-f",U,X],{stdio:["pipe","pipe","pipe"]});await new Promise((x,n)=>{let u="";g.stderr.on("data",(o)=>{u+=o.toString()}),g.on("close",(o)=>{if(o===0)x();else n(Error(`Docker build failed: ${u}`))})}),k.succeed("Docker image built");let c=new P("Pushing image to ECR...");c.start();let s=C("docker",["push",f],{stdio:["pipe","pipe","pipe"]});await new Promise((x,n)=>{let u="";s.stderr.on("data",(o)=>{u+=o.toString()}),s.on("close",(o)=>{if(o===0)x();else n(Error(`Docker push failed: ${u}`))})}),c.succeed("Image pushed to ECR");let m=new P("Updating ECS service...");if(m.start(),await O.updateService({cluster:Q,service:G,forceNewDeployment:H}),m.succeed("ECS service updated"),K){let x=new P("Waiting for deployment to stabilize...");x.start(),await O.waitForServiceStable(Q,G),x.succeed("Deployment stabilized")}y(`
8161
8162
  Container deployment complete!`),F(`
8162
- Image: ${f}`),F(`Cluster: ${Q}`),F(`Service: ${G}`)}catch(Y){R(`Deployment failed: ${Y.message}`)}})}async function dw($){let J=process.cwd(),Y=`${J}/.env`,Z=`${J}/.env.local`,W=({stage:["stage","staging"],staging:["staging","stage"]}[$]||[$]).map((K)=>`${J}/.env.${K}`).find((K)=>k1(K));if(!W){if(k1(Y))d(`No .env.${$} found, falling back to .env`);else R(`No .env.${$} or .env file found`);return null}let z=W.split("/").pop();$0(`Loading environment file: ${z}`);let U=null,X=null;if(k1(Z)){U=`${Z}.bak`,A6(Z,U);let K=EW(Z,"utf-8");for(let B of K.split(`
8163
+ Image: ${f}`),F(`Cluster: ${Q}`),F(`Service: ${G}`)}catch(Y){R(`Deployment failed: ${Y.message}`)}})}async function dw($){let J=process.cwd(),Y=`${J}/.env`,Z=`${J}/.env.local`,W=({stage:["stage","staging"],staging:["staging","stage"]}[$]||[$]).map((K)=>`${J}/.env.${K}`).find((K)=>k1(K));if(!W){if(k1(Y))d(`No .env.${$} found, falling back to .env`);else F(`No .env.${$} or .env file found; using the existing process environment`);return null}let z=W.split("/").pop();$0(`Loading environment file: ${z}`);let U=null,X=null;if(k1(Z)){U=`${Z}.bak`,A6(Z,U);let K=EW(Z,"utf-8");for(let B of K.split(`
8163
8164
  `)){let O=B.trim();if(!O||O.startsWith("#"))continue;let A=O.indexOf("=");if(A===-1)continue;let j=O.slice(0,A).trim();delete process.env[j]}let{unlinkSync:V}=await import("node:fs");V(Z),F("Temporarily moved .env.local out of the way and purged its values from process.env")}if(k1(Y))X=`${Y}.bak`,A6(Y,X);A6(W,Y);let H=EW(W,"utf-8");for(let K of H.split(`
8164
8165
  `)){let V=K.trim();if(!V||V.startsWith("#"))continue;let B=V.indexOf("=");if(B===-1)continue;let O=V.slice(0,B).trim(),A=V.slice(B+1).trim();process.env[O]=A}return y(`Loaded ${z}`),async()=>{let{unlinkSync:K}=await import("node:fs");if(X)A6(X,`${J}/.env`),K(X),F("Restored .env");if(U)A6(U,`${J}/.env.local`),K(U),F("Restored .env.local")}}async function cw($,J,Y){if(!Y)return;let Z=QG($.sites||{});if(Z.size===0)return;if(!J){d("DNS: no app server IP resolved — skipping A-record reconciliation.");return}let Q;try{Q=y1(Y)}catch(z){d(`DNS: ${z instanceof Error?z.message:String(z)}`),F(`Point each site domain at ${J} manually.`);return}if(!Q){d(`DNS provider '${Y}' is not configured — skipping A-record reconciliation.`),F(`Point each site domain at ${J} manually.`);return}if(Q.provider==="route53"){let z=$.infrastructure?.dns?.hostedZoneId;if(z&&!Q.hostedZoneId)Q.hostedZoneId=z}let G=K1(Q),W=$.infrastructure?.dns?.domain;$0(`Reconciling DNS: ${Z.size} A record(s) → ${J} via ${Y}...`);for(let z of Z){let U=W&&z.endsWith(W)?W:z.split(".").slice(-2).join(".");try{let X=await G.upsertRecord(U,{name:z,type:"A",content:J,ttl:300});if(X.success)y(` ✓ ${z} A → ${J}`);else d(` ⚠ ${z}: ${X.message} — set the A record manually.`);if(X.success){let H=await GG(G,U,z,J);for(let K of H)d(` ⚠ ${z}: ${K}`);if(H.length===0)F(` ✓ ${z}: stale duplicate A records reconciled`)}}catch(X){d(` ⚠ ${z}: ${X instanceof Error?X.message:String(X)} — set the A record manually.`)}}}async function mw($,J,Y,Z,Q,G,W=!1){let z=$.sites||{},U=J?[J]:Object.keys(z);if(U.length===0){d("No sites configured in cloud.config.ts");return}let X=y1(Y);if(!X){R(`DNS provider '${Y}' is not configured. Please set the required environment variables.`),F(`
8165
8166
  For Cloudflare: CLOUDFLARE_API_TOKEN`),F("For Porkbun: PORKBUN_API_KEY, PORKBUN_SECRET_KEY"),F("For GoDaddy: GODADDY_API_KEY, GODADDY_API_SECRET");return}if(X.provider==="route53"){let K=$.infrastructure?.dns?.hostedZoneId;if(K&&!X.hostedZoneId)X.hostedZoneId=K}let H=K1(X);for(let K of U){let V=z[K];if(!V){R(`Site '${K}' not found in configuration`);continue}if(V.deploy==="server"){F(`Site '${K}' is deploy:'server' — handled by the compute path, skipping static (bucket) deploy.`);continue}let B=V.domain;if(!B){R(`Site '${K}' has no domain configured`);continue}if(w(`Deploying Site: ${K}`),F(`Domain: ${B}`),F(`Source: ${V.root}`),F(`DNS Provider: ${Y}`),V.build){$0(`Running build command: ${V.build}`);try{let{execSync:T}=await import("node:child_process");T(V.build,{stdio:"inherit",cwd:process.cwd()}),y("Build completed successfully")}catch(T){R(`Build failed: ${T.message}`);continue}}if(!k1(V.root)){if(R(`Source directory not found: ${V.root}`),!V.build)F('Run your build command first (e.g., bun run generate) or add a "build" option to your site config');continue}$0("Checking existing DNS records...");let O=await H.listRecords(B);if(O.success&&O.records.length>0){let T=B.split("."),D=T.length>2?T[0]:"@",N=T.slice(-2).join("."),C=O.records.find((S)=>S.type==="CNAME"&&(S.name===B||S.name===D||S.name===`${D}.${N}`));if(C){let S=C.content.includes("netlify"),k=C.content.includes("vercel");if(C.content.includes("cloudfront.net"))F(`Domain already points to CloudFront: ${C.content}`),F("Proceeding with file upload...");else{let g=S?"Netlify":k?"Vercel":"another provider";if(d(`