@stacksjs/buddy 0.70.28 → 0.70.29

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 (2) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -300,7 +300,7 @@ us-east-1
300
300
  ${z}
301
301
  `,q=await z5(X,{cwd:W5.projectPath(),stdin:"pipe",input:H});if(q.isErr)await h3("While running the cloud command, there was an issue",{startTime:$,useSeconds:!0},q.error),s0.exit(y7.FatalError);if(Q?.quiet)return;await h3("Exited",{startTime:$,useSeconds:!0})}m7();import{chmodSync as f5}from"fs";import g0 from"process";import{runAction as v5}from"@stacksjs/actions";import{bold as S5,cyan as E5,dim as N5,intro as C5,log as n,onUnknownSubcommand as h5,runCommand as c7}from"@stacksjs/cli";import{Action as D5}from"@stacksjs/enums";import{resolve as k3}from"@stacksjs/path";import{isFolder as b5}from"@stacksjs/storage";import{ExitCode as o0}from"@stacksjs/types";import{useOnline as y5}from"@stacksjs/utils";function uz(Q){let $={name:"The name of the project",command:"Create a new Stacks project",ui:"Are you building a UI?",components:"Are you building UI components?",webComponents:"Automagically built optimized custom elements/web components?",vue:"Automagically built a Vue component library?",views:"How about views?",functions:"Are you developing functions/composables?",api:"Are you building an API?",database:"Do you need a database?",notifications:"Do you need notifications? e.g. email, SMS, push or chat notifications",cache:"Do you need caching?",email:"Do you need email?",project:"Target a specific project",verbose:"Enable verbose output"};Q.command("new [name]",$.command).alias("create [name]").option("-n, --name [name]",$.name,{default:!1}).option("-u, --ui",$.ui,{default:!0}).option("-c, --components",$.components,{default:!0}).option("-w, --web-components",$.webComponents,{default:!0}).option("-v, --vue",$.vue,{default:!0}).option("-p, --views",$.views,{default:!0}).option("-f, --functions",$.functions,{default:!0}).option("-a, --api",$.api,{default:!0}).option("-d, --database",$.database,{default:!0}).option("-ca, --cache",$.cache,{default:!1}).option("-e, --email",$.email,{default:!1}).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J,Y)=>{n.debug("Running `buddy new <name>` ...",Y);let G=await C5("buddy new");J=J??Y.name;let z=k3(g0.cwd(),J);k5(z),x5();let W=await g5(J,z,Y);if(W.isErr)n.error(W.error),g0.exit(o0.FatalError);if(await m5(z,Y),u5(z),await c5(z,Y),G){let X=performance.now()-G;n.success(N5(`[${X.toFixed(2)}ms] Completed`))}n.info(S5("Welcome to the Stacks Framework! \u269B\uFE0F")),n.info("To learn more, visit https://stacksjs.com"),g0.exit(o0.Success)}),h5(Q,"new")}function k5(Q){if(b5(Q))n.error(`Path ${Q} already exists`),g0.exit(o0.FatalError)}function x5(){if(!y5())n.info("It appears you are disconnected from the internet."),n.info("The Stacks setup requires a brief internet connection for setup."),n.info("For instance, it installs your dependencies from."),g0.exit(o0.FatalError)}async function g5(Q,$,J){n.info("Setting up your stack.");let Y=await c7(`bunx --bun @stacksjs/gitit stacks ${Q}`,J);return n.success(`Successfully scaffolded your project at ${E5($)}`),Y}function u5(Q){for(let $ of["buddy","bootstrap"])try{f5(k3(Q,$),493)}catch{}}async function m5(Q,$){n.info("Ensuring your environment is ready..."),await T$(),await I$(Q),n.success("Environment is ready")}async function c5(Q,$){n.info("Installing & setting up Stacks"),n.info("Running bun install...");let J=await c7("bun install",{...$,cwd:Q});if(J?.isErr)n.error(J.error),g0.exit(o0.FatalError);if(n.info("Copying .env.example \u2192 .env"),J=await c7("cp .env.example .env",{...$,cwd:Q}),J?.isErr)n.error(J.error),g0.exit(o0.FatalError);n.info("Generating application key...");let Y=await v5(D5.KeyGenerate,{...$,cwd:Q});if(Y.isErr)n.error(Y.error),g0.exit(o0.FatalError);if(n.info("Initializing git repository..."),J=await c7("git init",{...$,cwd:Q}),J.isErr)n.error(J.error),g0.exit(o0.FatalError);n.success("Installed & set-up \uD83D\uDE80")}import{existsSync as j0,readFileSync as t0,readdirSync as o5,statSync as t5}from"fs";import{homedir as A$}from"os";import{join as _$}from"path";import E from"process";import{runAction as e5}from"@stacksjs/actions";import{italic as Q7,onUnknownSubcommand as $6,outro as l7,prompts as j$}from"@stacksjs/cli";import{app as p3,email as c,cloud as l3}from"@stacksjs/config";import{runAction as l5}from"@stacksjs/actions";import{config as g3}from"@stacksjs/config";import{Action as d5}from"@stacksjs/enums";import{err as rz,handleError as nz,ok as az}from"@stacksjs/error-handling";import{log as Z0}from"@stacksjs/logging";import{path as p5}from"@stacksjs/path";import{fs as x3}from"@stacksjs/storage";import{Route53Client as r5,Route53DomainsClient as u3}from"@stacksjs/ts-cloud";function n5(Q){if(!Q)return;return Q.replace(/^https?:\/\//,"").replace(/\/$/,"")}function a5(Q){if(!/^[a-zA-Z0-9.-]+$/.test(Q))throw Error(`Invalid nameserver value: ${Q}`);return Q}function T2(Q){try{let $=p5.projectConfigPath("dns.ts"),Y=x3.readFileSync($,"utf-8").replace(/nameservers: \[.*?\]/s,`nameservers: [${Q.map((G)=>`'${a5(G)}'`).join(", ")}]`);x3.writeFileSync($,Y,"utf-8"),Z0.info("Nameservers have been set.")}catch($){Z0.error("Error updating nameservers:",$)}}async function i5(Q){if(Z0.debug("Getting nameservers for domain:",Q),!Q)return[];try{return(await new u3().getDomainDetail({DomainName:Q}))?.Nameservers?.map((Y)=>Y.Name)||[]}catch($){return Z0.debug("Domain not registered via Route53 Domains:",$),[]}}async function s5(Q,$){if(!$)$=g3.app.url;let J=await i5($);if(!J||J.length===0)return Z0.debug("Domain is not registered via Route53 Domains - skipping automatic nameserver update"),T2(Q),!1;if(JSON.stringify(J.sort())!==JSON.stringify(Q.sort())){Z0.info("Updating your domain nameservers to match the ones in your hosted zone..."),Z0.debug("Hosted zone nameservers:",Q),Z0.debug("Domain nameservers:",J);try{return await new u3().updateDomainNameservers({DomainName:$,Nameservers:Q.map((G)=>({Name:G}))}),T2(Q),Z0.info("Nameservers updated."),!0}catch(Y){return Z0.debug("Failed to update nameservers via Route53 Domains:",Y),T2(Q),!1}}return Z0.success("Your nameservers are up to date."),!0}async function m3(Q){if(Z0.debug("Checking if domain has been added to cloud..."),!Q)Q=g3.app.url;if(Q=n5(Q),!Q)return!1;Z0.debug("domainName:",Q);let $=new r5,J=await $.listHostedZonesByName({DNSName:Q});if(Z0.debug("Existing hosted zones:",J),!J||!J.HostedZones||J.HostedZones.length===0)return!1;let Y=J.HostedZones.find((G)=>G.Name===`${Q}.`);if(Y){Z0.debug("Hosted zone found:",Y);let z=(await $.getHostedZone({Id:Y.Id})).DelegationSet?.NameServers||[];return await s5(z,Q),!0}return!1}async function c3(Q){return await l5(d5.DomainsAdd,Q)}m7();import{env as Q6}from"@stacksjs/env";import{Action as J6}from"@stacksjs/enums";import{path as E0}from"@stacksjs/path";import{ExitCode as K0}from"@stacksjs/types";var R={info:(...Q)=>console.log("\u2139",...Q),success:(...Q)=>console.log("\u2713",...Q),warn:(...Q)=>console.log("\u26A0",...Q),error:(...Q)=>console.error("\u2717",...Q),debug:(...Q)=>{if(E.argv.includes("--verbose")||E.argv.includes("-v"))console.log("\uD83D\uDD0D",...Q)}},r3="github.com/mail-os/mail",Y6=`${r3}@0.1.0`,n3="linux-x86_64",I2=["mail","mail-x86_64-linux","mail-x86_64-linux-gnu"];function G6(Q,$,J=8){let Y=new Set($),G=[];if(!j0(Q))return G;function z(W,X){if(X>J)return;for(let H of o5(W)){if(H===".git"||H==="node_modules")continue;let q=_$(W,H),Z=t5(q);if(Z.isDirectory())z(q,X+1);else if(Z.isFile()&&Y.has(H))G.push(q)}}return z(Q,0),G}function A2(Q){let $=t0(Q).slice(0,4);return $[0]===127&&$[1]===69&&$[2]===76&&$[3]===70}function z6(){let Q=_$(A$(),"Code","Tools","pantry","packages","ts-pantry","bin","cli.ts");if(j0(Q))return{command:"bun",args:[Q]};let $=E0.projectPath("pantry/.bin/pantry");if(j0($))return{command:$,args:[]};let J=_$(A$(),".local","share","pantry","global","bin","pantry");if(j0(J))return{command:J,args:[]};return{command:"pantry",args:[]}}async function W6(){let{execFileSync:Q}=await import("child_process"),$=z6();Q($.command,[...$.args,"install",Y6,"--install-dir",E0.projectPath("pantry"),"--platform",n3,"--quiet"],{cwd:E0.projectPath(),stdio:E.argv.includes("--verbose")||E.argv.includes("-v")?"inherit":"pipe",env:E.env})}async function X6(){let Q=[...I2.map(($)=>E0.projectPath(`pantry/.bin/${$}`)),...I2.map(($)=>_$(A$(),".local","share","pantry","global","bin",$))];for(let $ of Q)if(j0($)&&A2($))return $;for(let $ of[E0.projectPath("pantry"),_$(A$(),".local","share","pantry")])for(let J of G6($,I2))if(A2(J))return J;return null}async function q6(Q=!1){let $=E0.projectPath();await T$(),await I$($),await u7({cwd:$,verbose:Q}),await g7($)}function H6(){let Q=_$(A$(),".aws","credentials"),$=_$(A$(),".aws","config");if(!j0(Q))return{};try{let Y=t0(Q,"utf-8").split(`
302
302
  `),G=["default","stacks"],z="",W={},X={};for(let q of Y){let Z=q.trim(),K=Z.match(/^\[(.+)\]$/);if(K){z=K[1],X[z]={};continue}let _=Z.match(/^(\w+)\s*=\s*(.+)$/);if(_&&z){let[,U,L]=_;if(U==="aws_access_key_id")X[z].accessKeyId=L;else if(U==="aws_secret_access_key")X[z].secretAccessKey=L}}for(let q of G)if(X[q]?.accessKeyId&&X[q]?.secretAccessKey){W=X[q],R.debug(`Using AWS credentials from ~/.aws/credentials [${q}] profile`);break}if(!W.accessKeyId){for(let[q,Z]of Object.entries(X))if(Z.accessKeyId&&Z.secretAccessKey){W=Z,R.debug(`Using AWS credentials from ~/.aws/credentials [${q}] profile`);break}}let H;if(j0($)){let Z=t0($,"utf-8").match(/region\s*=\s*(.+)/);if(Z)H=Z[1].trim()}return{...W,region:H}}catch(J){return R.debug("Failed to read AWS credentials file:",J),{}}}async function d3(Q,$,J,Y){J.info("Setting up email DNS records...");try{let{SESClient:G}=await import("@stacksjs/ts-cloud"),{Route53Client:z}=await import("@stacksjs/ts-cloud"),W=new G($),X=new z($);J.info(`Getting DKIM tokens for ${Q}...`);let q=(await W.getEmailIdentity(Q)).DkimAttributes?.Tokens||[];if(q.length===0){J.warn("No DKIM tokens found - domain may not be set up in SES yet");return}J.info(`Found ${q.length} DKIM tokens`);let K=(await X.listHostedZones()).HostedZones?.find((F)=>F.Name===`${Q}.`);if(!K){J.warn(`Hosted zone not found for ${Q} - DNS records must be added manually`),J.info("DKIM records needed:");for(let F of q)J.info(` CNAME: ${F}._domainkey.${Q} -> ${F}.dkim.amazonses.com`);J.info(` MX: ${Q} -> 10 inbound-smtp.${$}.amazonaws.com`);return}let _=K.Id?.replace("/hostedzone/","");J.info(`Found hosted zone: ${_}`);for(let F of q){let O=`${F}._domainkey.${Q}`,M=`${F}.dkim.amazonses.com`;try{await X.changeResourceRecordSets({HostedZoneId:_,ChangeBatch:{Changes:[{Action:"UPSERT",ResourceRecordSet:{Name:O,Type:"CNAME",TTL:300,ResourceRecords:[{Value:M}]}}]}}),J.success(`Added DKIM record: ${F}._domainkey`)}catch(T){J.warn(`Failed to add DKIM record: ${T.message}`)}}let U=Y?.mailSubdomain||"mail",L=Y?.mode==="server"?`10 ${U}.${Q}`:`10 inbound-smtp.${$}.amazonaws.com`;try{await X.changeResourceRecordSets({HostedZoneId:_,ChangeBatch:{Changes:[{Action:"UPSERT",ResourceRecordSet:{Name:Q,Type:"MX",TTL:300,ResourceRecords:[{Value:L}]}}]}}),J.success(`Added MX record: ${L}`)}catch(F){J.warn(`Failed to add MX record: ${F.message}`)}try{await X.changeResourceRecordSets({HostedZoneId:_,ChangeBatch:{Changes:[{Action:"UPSERT",ResourceRecordSet:{Name:Q,Type:"TXT",TTL:300,ResourceRecords:[{Value:'"v=spf1 include:amazonses.com ~all"'}]}}]}}),J.success("Added SPF record")}catch(F){J.warn(`Failed to add SPF record: ${F.message}`)}try{await X.changeResourceRecordSets({HostedZoneId:_,ChangeBatch:{Changes:[{Action:"UPSERT",ResourceRecordSet:{Name:`_dmarc.${Q}`,Type:"TXT",TTL:300,ResourceRecords:[{Value:`"v=DMARC1;p=quarantine;pct=25;rua=mailto:dmarcreports@${Q}"`}]}}]}}),J.success("Added DMARC record")}catch(F){J.warn(`Failed to add DMARC record: ${F.message}`)}try{let O=`${E.env.APP_NAME?.toLowerCase().replace(/[^a-z0-9]/g,"-")||"stacks"}-email-rules`;await W.setActiveReceiptRuleSet(O),J.success(`Activated email receipt rule set: ${O}`)}catch(F){J.warn(`Failed to activate receipt rule set: ${F.message}`)}J.success("Email DNS records configured!"),J.info("Note: DKIM verification may take 5-15 minutes to complete")}catch(G){J.warn(`Failed to set up email DNS records: ${G.message}`),J.info("You can manually set up DNS records using: buddy email:verify")}}async function Z6(Q,$,J,Y){try{let{DynamoDBClient:G}=await import("@stacksjs/ts-cloud"),z=await import("crypto"),W=new G(J),X=`${Q}-mail-users`,H=c?.mailboxes||[];if(H.length===0){let q=`admin@${$}`,Z=z.randomBytes(16).toString("hex"),K=z.createHash("sha256").update(Z).digest("hex");try{await W.putItem({TableName:X,Item:{email:{S:q},passwordHash:{S:K},createdAt:{S:new Date().toISOString()},displayName:{S:"Admin"}}}),Y.success(`Created default mail user: ${q}`),Y.info(`Password: ${Z}`),Y.info("Save this password - it will not be shown again!")}catch(_){if(_.message?.includes("ConditionalCheckFailedException")||_.message?.includes("already exists"))Y.debug("Default mail user already exists");else throw _}}else for(let q of H){let Z=q,K=typeof q==="string"?`${q}@${$}`:`${Z.name||Z.address?.split("@")[0]}@${$}`,_=typeof q==="object"&&Z.password?Z.password:z.randomBytes(16).toString("hex"),U=z.createHash("sha256").update(_).digest("hex");try{if(await W.putItem({TableName:X,Item:{email:{S:K},passwordHash:{S:U},createdAt:{S:new Date().toISOString()},displayName:{S:typeof q==="object"?Z.displayName||Z.name||K:q}}}),Y.success(`Created mail user: ${K}`),typeof q!=="object"||!Z.password)Y.info(` Password: ${_}`)}catch(L){if(L.message?.includes("ConditionalCheckFailedException"))Y.debug(`Mail user ${K} already exists`);else Y.warn(`Failed to create mail user ${K}: ${L.message}`)}}}catch(G){Y.warn(`Failed to create mail users: ${G.message}`)}}async function j2(Q,$,J){try{let{S3Client:Y}=await import("@stacksjs/ts-cloud"),G=new Y($);if(J==="serverless"){let W=E0.frameworkPath("core/mail-server/server.ts");if(j0(W)){let H=t0(W,"utf-8");await G.putObject({bucket:Q,key:"mail-server/server.ts",body:H,contentType:"text/typescript"}),R.success("Uploaded serverless mail server code to S3")}let X=E0.frameworkPath("core/mail-server/package.json");if(j0(X)){let H=t0(X,"utf-8");await G.putObject({bucket:Q,key:"mail-server/package.json",body:H,contentType:"application/json"})}return}let z=!1;try{await W6();let W=await X6();if(W&&j0(W)&&A2(W)){R.info(`Uploading Pantry mail binary: ${W}`);let X=t0(W);await G.putObject({bucket:Q,key:"mail-server/smtp-server",body:X,contentType:"application/octet-stream"}),R.success("Uploaded Linux x86_64 mail server binary to S3"),z=!0}}catch(W){R.debug(`Pantry mail install failed: ${W.message}`)}if(!z)R.warn(`No Pantry-provided ${n3} mail binary found. Release ${r3}, then run the Pantry binary sync for that package.`)}catch(Y){R.debug(`Could not upload mail server to S3 (bucket may not exist yet): ${Y.message}`)}}function FW(Q){let $={deploy:"Deploy your project",project:"Target a specific project",production:"Deploy to production",development:"Deploy to development",staging:"Deploy to staging",yes:"Confirm all prompts by default",domain:"Specify a domain to deploy to",verbose:"Enable verbose output"};Q.command("deploy [env]",$.deploy).option("--domain",$.domain,{default:void 0}).option("-p, --project [project]",$.project,{default:!1}).option("--prod",$.production,{default:!0}).option("--dev",$.development,{default:!1}).option("--yes",$.yes,{default:!1}).option("--staging",$.staging,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J,Y)=>{R.debug("Running `buddy deploy` ...",Y),await q6(Y.verbose===!0);let G=J||"production";if(E.env.AWS_ACCESS_KEY_ID&&E.env.AWS_SECRET_ACCESS_KEY)delete E.env.AWS_PROFILE;let z=performance.now();console.log(""),console.log("\uD83D\uDE80 Deploy"),console.log("");let W;if(G==="production"||G==="prod"){let Z=E0.projectPath(".env.production");if(j0(Z)){let _=t0(Z,"utf-8").match(/^APP_URL=(.+)$/m);if(_)W=_[1].trim(),R.debug("Using APP_URL from .env.production:",W)}}let X=Q6.APP_URL,H=Y.domain||W||X||p3.url;if((Y.prod||G==="production"||G==="prod")&&!Y.yes)await K6();if(!H)R.info("No domain found in your .env.production or ./config/app.ts"),R.info("Please ensure your domain is properly configured."),R.info("For more info, check out the docs or join our Discord."),E.exit(K0.FatalError);R.info(`Deploying to ${Q7(H)} (${G})`),await F6(Y),Y.domain=await _6(H,Y,z);let q=await e5(J6.Deploy,Y);if(q.isErr)await l7("While running the `buddy deploy`, there was an issue",{startTime:z,useSeconds:!0},q.error),E.exit(K0.FatalError);await l7("Project deployed.",{startTime:z,useSeconds:!0})}),$6(Q,"deploy")}async function K6(){if(!await j$.confirm({message:"Are you sure you want to deploy to production?",initial:!0}))R.info("Aborting deployment..."),E.exit(K0.InvalidArgument)}async function _6(Q,$,J){if(R.debug("Configuring domain...",Q),!Q)R.info("We could not identify a domain to deploy to."),R.warn("Please set your .env or ./config/app.ts properly."),R.info("Alternatively, specify a domain to deploy via the `--domain` flag."),console.log(""),R.info(" \u27A1\uFE0F Example: `buddy deploy --domain example.com`"),console.log(""),E.exit(K0.FatalError);if(Q.includes("localhost"))R.info("You are deploying to a local environment."),R.warn("Please set your .env or ./config/app.ts properly. The domain we are deploying cannot be a `localhost` domain."),R.info("Alternatively, specify a domain to deploy via the `--domain` flag."),console.log(""),R.info(" \u27A1\uFE0F Example: `buddy deploy --domain example.com`"),console.log(""),E.exit(K0.FatalError);if(await m3(Q))return R.info("Domain is properly configured"),R.info("Your cloud is deploying..."),R.info(`${Q7("This may take a while...")}`),Q;console.log(""),R.info(` \uD83D\uDC4B It appears to be your first ${Q7(Q)} deployment.`),console.log(""),R.info(Q7("Let\u2019s ensure it is all connected properly.")),R.info(Q7("One moment...")),console.log("");let Y=await c3({...$,deploy:!0,startTime:J});if(Y.isErr)await l7("While running the `buddy deploy`, there was an issue",{startTime:J,useSeconds:!0},Y.error),E.exit(K0.FatalError);await l7("Added your domain.",{startTime:J,useSeconds:!0}),E.exit(K0.Success)}async function U6(){let Q=await j$.text({message:"AWS Access Key ID:",validate:(G)=>G.length>0?!0:"Access Key ID is required"});if(!Q)R.info("Deployment cancelled"),E.exit(K0.Success);let $=await j$.password({message:"AWS Secret Access Key:",validate:(G)=>G.length>0?!0:"Secret Access Key is required"});if(!$)R.info("Deployment cancelled"),E.exit(K0.Success);let J=await j$.text({message:"AWS Region:",initial:"us-east-1"});if(!J)R.info("Deployment cancelled"),E.exit(K0.Success);let{setEnv:Y}=await import("@stacksjs/env");await Y("AWS_ACCESS_KEY_ID",Q,{file:".env.production",encrypt:!0}),await Y("AWS_SECRET_ACCESS_KEY",$,{file:".env.production",encrypt:!0}),await Y("AWS_REGION",J||"us-east-1",{file:".env.production"}),E.env.AWS_ACCESS_KEY_ID=Q,E.env.AWS_SECRET_ACCESS_KEY=$,E.env.AWS_REGION=J||"us-east-1",R.success("AWS credentials saved securely to .env.production"),console.log("")}function L6(){let Q=E.env.APP_ENV||E.env.NODE_ENV||"production",$=[E0.projectPath(`.env.${Q}`),E0.projectPath(".env")];for(let J of $){if(!j0(J))continue;try{let G=t0(J,"utf-8").split(`
303
- `),z,W,X,H;for(let q of G){let Z=q.trim();if(Z.startsWith("#")||!Z.includes("="))continue;let[K,..._]=Z.split("="),U=_.join("=").trim();if(K==="AWS_ACCESS_KEY_ID"&&U)z=U;else if(K==="AWS_SECRET_ACCESS_KEY"&&U)W=U;else if(K==="AWS_REGION"&&U)X=U;else if(K==="AWS_ACCOUNT_ID"&&U)H=U}if(z&&W)return R.debug(`Found AWS credentials in ${J}`),{accessKeyId:z,secretAccessKey:W,region:X,accountId:H}}catch(Y){R.debug(`Failed to read ${J} file:`,Y)}}return{}}async function F6(Q){let $=!1;try{R.info("Ensuring AWS cloud stack exists...");let J=E.env.AWS_ACCESS_KEY_ID&&E.env.AWS_SECRET_ACCESS_KEY;if(!J){let F=L6();if(F.accessKeyId&&F.secretAccessKey){if(E.env.AWS_ACCESS_KEY_ID=F.accessKeyId,E.env.AWS_SECRET_ACCESS_KEY=F.secretAccessKey,F.region&&!E.env.AWS_REGION)E.env.AWS_REGION=F.region;if(F.accountId&&!E.env.AWS_ACCOUNT_ID)E.env.AWS_ACCOUNT_ID=F.accountId;J=!0;let O=E.env.APP_ENV||E.env.NODE_ENV||"production";R.success(`Using AWS credentials from .env.${O}`)}}if(!J){let F=H6();if(F.accessKeyId&&F.secretAccessKey){if(E.env.AWS_ACCESS_KEY_ID=F.accessKeyId,E.env.AWS_SECRET_ACCESS_KEY=F.secretAccessKey,F.region&&!E.env.AWS_REGION)E.env.AWS_REGION=F.region;J=!0,R.success("Using AWS credentials from ~/.aws/credentials")}}if(!J){if(R.info("AWS credentials not found in .env or ~/.aws/credentials."),R.info("You can either:"),R.info(" 1. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in .env.production"),R.info(" 2. Add credentials to ~/.aws/credentials"),R.info(" 3. Configure them interactively below"),console.log(""),Q?.yes)R.info("Skipping credential setup (--yes flag provided)"),E.exit(K0.FatalError);let F=await j$.confirm({message:"Would you like to configure AWS credentials now?",initial:!0});if(R.debug("setupCredentials response:",F,typeof F),F===void 0||F===!1){if(F===void 0)console.log(""),R.info("Deployment cancelled"),E.exit(K0.Success);return console.log(""),R.info("Skipping cloud infrastructure check"),R.info("You can configure AWS credentials later by running: buddy configure:aws"),!0}await U6()}else R.success("AWS credentials found");let Y=(E.env.APP_NAME||p3.name||"stacks").toLowerCase().replace(/[^a-z0-9-]/g,"-"),G=`${Y}-cloud`,{AWSCloudFormationClient:z}=await import("@stacksjs/ts-cloud"),W=new z(E.env.AWS_REGION||"us-east-1"),X=!1,H=!1;try{let F=(await W.describeStacks({stackName:G})).Stacks?.[0];if(F){X=!0,R.success("Cloud stack exists");let{AWSCloudFormationClient:O}=await import("@stacksjs/ts-cloud"),T=await new O(E.env.AWS_REGION||"us-east-1").listStackResources(G),f=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="EmailBucket"),P=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="OutboundEmailLambda"),I=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="EmailConversionLambda"),j=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="EmailNotificationTopic"),A=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="MailApiLambda"),h=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="MailUsersTable"),V=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="MailServerInstance"),N=F.Outputs?.find((i)=>i.OutputKey==="EmailDomain")?.OutputValue,B=(c?.from?.address?.includes("@")?c.from.address.split("@")[1]:void 0)||"stacksjs.com";if(!f&&c?.server?.scan!==void 0)R.info("Email infrastructure not found in stack, will update..."),H=!0;else if(N&&N!==B)R.info(`Email domain changed: ${N} -> ${B}, will update...`),H=!0;else if(f&&(!P||!I||!j))R.info("Email infrastructure incomplete, will update..."),H=!0;else if(f&&(!A||!h))R.info("Mail API infrastructure missing, will update..."),H=!0;else if(f&&!V&&c?.server?.enabled)R.info("Mail server EC2 instance missing, will update..."),H=!0;let v=(F.Outputs||[]).find((i)=>i.OutputKey==="MailServerMode")?.OutputValue,B0=c?.server?.mode||"serverless";if(v&&v!==B0)R.info(`Mail server mode changed: ${v} -> ${B0}, will update...`),H=!0;if(V&&c?.server?.enabled){if(E.env.FORCE_MAIL_UPDATE==="true")R.info("Forcing mail server update..."),H=!0}if(!H)return!0}}catch(F){R.debug(`Stack not found: ${F.message}`)}if(!X)R.info("Cloud stack not found, will be created by deploy action");return!0}catch(J){if(!$){if(R.error("Error checking cloud infrastructure"),R.error(`Error: ${J.message||J}`),Q?.verbose)console.error(J)}E.exit(K0.FatalError)}}import x from"process";import{bold as J7,cyan as m0,dim as Q0,green as Y7,intro as P2,log as N0,onUnknownSubcommand as V6,outro as U$,prompts as B6,runCommand as R6}from"@stacksjs/cli";import{Action as i3}from"@stacksjs/enums";import{libsPath as w6}from"@stacksjs/path";import{ExitCode as u0}from"@stacksjs/types";var a3="0.70.28";var f2;async function A0(){if(!f2)f2=await import("@stacksjs/actions");return f2}function AW(Q){let $={dev:"Start development server",frontend:"Start the frontend development server",components:"Start the Components development server",desktop:"Start the Desktop App development server",dashboard:"Start the Dashboard development server",api:"Start the local API development server",email:"Start the Email development server",docs:"Start the Documentation development server",systemTray:"Start the System Tray development server",interactive:"Get asked which development server to start",select:"Which development server are you trying to start?",withLocalhost:"Include the localhost URL in the output",project:"Target a specific project",verbose:"Enable verbose output"};Q.command("dev [server]",$.dev).option("-f, --frontend",$.frontend).option("-a, --api",$.api).option("-e, --email",$.email).option("-c, --components",$.components).option("-d, --dashboard",$.dashboard).option("-k, --desktop",$.desktop).option("-o, --docs",$.docs).option("-s, --system-tray",$.systemTray).option("-i, --interactive",$.interactive,{default:!1}).option("-l, --with-localhost",$.withLocalhost,{default:!1}).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J,Y)=>{let G=Bun.nanoseconds(),z=J||(Y.frontend?"frontend":void 0)||(Y.api?"api":void 0)||(Y.components?"components":void 0)||(Y.dashboard?"dashboard":void 0)||(Y.desktop?"desktop":void 0)||(Y.systemTray||Y["system-tray"]?"system-tray":void 0)||(Y.docs?"docs":void 0);if(z){let W={...Y},X=await A0();switch(z){case"frontend":await X.runFrontendDevServer(W);break;case"api":await X.runApiDevServer(W);break;case"components":await X.runComponentsDevServer(W);break;case"dashboard":await X.runDashboardDevServer(W);break;case"desktop":await X.runDesktopDevServer(W);break;case"system-tray":await X.runSystemTrayDevServer(W);break;case"docs":await X.runDocsDevServer(W);break;default:N0.error(`Unknown server: ${z}`),x.exit(u0.InvalidArgument)}}else if(P6(Y)){let X=(await B6({type:"select",name:"value",message:$.select,choices:[{value:"all",title:"All"},{value:"frontend",title:"Frontend"},{value:"api",title:"Backend"},{value:"dashboard",title:"Dashboard"},{value:"desktop",title:"Desktop"},{value:"email",title:"Email"},{value:"components",title:"Components"},{value:"docs",title:"Documentation"}]})).value;if(X==="components")await(await A0()).runComponentsDevServer(Y);else if(X==="api")await(await A0()).runApiDevServer(Y);else if(X==="dashboard")await(await A0()).runDashboardDevServer(Y);else if(X==="docs")await(await A0()).runDocsDevServer(Y);else N0.error("Invalid option during interactive mode"),x.exit(u0.InvalidArgument)}else await M6(Y,G);U$("Exited",{startTime:G,useSeconds:!0}),x.exit(u0.Success)}),Q.command("dev:components",$.components).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{let Y=await P2("buddy dev:components"),G=await R6("bun run dev",{cwd:w6("components/vue")});if(J.verbose)N0.info("buddy dev:components result",G);if(G.isErr)await U$("While running the dev:components command, there was an issue",{startTime:Y,useSeconds:!0},G.error),x.exit(u0.FatalError);console.log(""),await U$("Exited",{startTime:Y,useSeconds:!0}),x.exit(u0.Success)}),Q.command("dev:docs",$.docs).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{let Y=await P2("buddy dev:docs"),G=await(await A0()).runAction(i3.DevDocs,J);if(G.isErr)await U$("While running the dev:docs command, there was an issue",{startTime:Y,useSeconds:!0},G.error),x.exit(u0.FatalError);console.log(""),await U$("Exited",{startTime:Y,useSeconds:!0}),x.exit(u0.Success)}),Q.command("dev:desktop",$.desktop).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{let Y=await P2("buddy dev:desktop"),G=await(await A0()).runAction(i3.DevDesktop,J);if(G.isErr)await U$("While running the dev:desktop command, there was an issue",{startTime:Y,useSeconds:!0},G.error),x.exit(u0.FatalError);console.log(""),await U$("Exited",{startTime:Y,useSeconds:!0}),x.exit(u0.Success)}),Q.command("dev:api",$.api).option("-p, --project [project]",$.project,{default:!1}).option("--no-watch-types","Skip the model/config type-regeneration watcher",{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{let Y=await A0();if(J.watchTypes!==!1)Y.watchTypes(J).catch((G)=>{N0.warn("[dev:api] type watcher exited:",G)});await Y.runApiDevServer(J)}),Q.command("dev:frontend",$.frontend).alias("dev:pages").alias("dev:views").option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{await(await A0()).runFrontendDevServer(J)}),Q.command("dev:dashboard",$.dashboard).alias("dev:admin").option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{await(await A0()).runDashboardDevServer(J)}),Q.command("dev:system-tray",$.systemTray).alias("dev:tray").option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{await(await A0()).runSystemTrayDevServer(J)}),V6(Q,"dev")}async function M6(Q,$){let J=x.env.APP_URL,Y=Number(x.env.PORT)||3000,G=Number(x.env.PORT_API)||3008,z=Number(x.env.PORT_DOCS)||3006,W=Number(x.env.PORT_ADMIN)||3002,X=x.env.STACKS_DEV_DASHBOARD==="1",H=J&&J!=="localhost"&&!J.includes("localhost:"),q=H?J.replace(/^https?:\/\//,""):null,Z=q?`api.${q}`:null,K=q?`docs.${q}`:null,_=q?`dashboard.${q}`:null,U=q?`https://${q}`:`http://localhost:${Y}`,L=Z?`https://${Z}`:`http://localhost:${G}`,F=K?`https://${K}`:`http://localhost:${z}`,O=_?`https://${_}`:`http://localhost:${W}`;if(console.log(),console.log(` ${J7(m0("stacks"))} ${Q0(`v${a3}`)}`),console.log(),console.log(` ${Y7("\u279C")} ${J7("Frontend")}: ${m0(U)}`),console.log(` ${Y7("\u279C")} ${J7("API")}: ${m0(L)}`),console.log(` ${Y7("\u279C")} ${J7("Docs")}: ${m0(F)}`),X)console.log(` ${Y7("\u279C")} ${J7("Dashboard")}: ${m0(O)}`);if(Q.verbose&&q){if(console.log(` ${Q0("\u279C")} ${Q0("Proxy")}: ${Q0(`localhost:${Y} \u2192 ${q}`)}`),console.log(` ${Q0("\u279C")} ${Q0("Proxy")}: ${Q0(`localhost:${G} \u2192 ${Z}`)}`),console.log(` ${Q0("\u279C")} ${Q0("Proxy")}: ${Q0(`localhost:${z} \u2192 ${K}`)}`),X)console.log(` ${Q0("\u279C")} ${Q0("Proxy")}: ${Q0(`localhost:${W} \u2192 ${_}`)}`)}if(console.log(),x.env.STACKS_PROXY_MANAGED="1",x.env.STACKS_DEV_NO_KILL!=="1")try{await Bun.spawn(["pkill","-9","-f","bun --watch storage/framework/core/actions/src/dev/"],{stdout:"ignore",stderr:"ignore"}).exited,await new Promise((V)=>setTimeout(V,200))}catch{}let M=!1,T=1500,f=()=>{if(M)return;M=!0;try{x.kill(-x.pid,"SIGTERM")}catch{try{x.kill(0,"SIGTERM")}catch{}}setTimeout(()=>{try{x.kill(0,"SIGKILL")}catch{x.exit(1)}},T).unref()};x.on("SIGINT",f),x.on("SIGTERM",f);let P={...Q,quiet:!0},I=await A0(),j=[{name:"Frontend",port:Y},{name:"API",port:G},{name:"Docs",port:z},...X?[{name:"Dashboard",port:W}]:[]],A=30000,h=!1;Promise.all(j.map((V)=>j6(V.port,A))).then(async(V)=>{if(h)return;h=!0;let N=V.map((B,v)=>B?null:j[v].name).filter((B)=>B!==null);if($){let B=(Bun.nanoseconds()-$)/1e6,v=N.length?`ready in ${(B/1000).toFixed(1)}s \u2014 ${N.join(", ")} did not bind within ${A/1000}s`:`ready in ${(B/1000).toFixed(1)}s`;console.log(` ${Q0(v)}
303
+ `),z,W,X,H;for(let q of G){let Z=q.trim();if(Z.startsWith("#")||!Z.includes("="))continue;let[K,..._]=Z.split("="),U=_.join("=").trim();if(K==="AWS_ACCESS_KEY_ID"&&U)z=U;else if(K==="AWS_SECRET_ACCESS_KEY"&&U)W=U;else if(K==="AWS_REGION"&&U)X=U;else if(K==="AWS_ACCOUNT_ID"&&U)H=U}if(z&&W)return R.debug(`Found AWS credentials in ${J}`),{accessKeyId:z,secretAccessKey:W,region:X,accountId:H}}catch(Y){R.debug(`Failed to read ${J} file:`,Y)}}return{}}async function F6(Q){let $=!1;try{R.info("Ensuring AWS cloud stack exists...");let J=E.env.AWS_ACCESS_KEY_ID&&E.env.AWS_SECRET_ACCESS_KEY;if(!J){let F=L6();if(F.accessKeyId&&F.secretAccessKey){if(E.env.AWS_ACCESS_KEY_ID=F.accessKeyId,E.env.AWS_SECRET_ACCESS_KEY=F.secretAccessKey,F.region&&!E.env.AWS_REGION)E.env.AWS_REGION=F.region;if(F.accountId&&!E.env.AWS_ACCOUNT_ID)E.env.AWS_ACCOUNT_ID=F.accountId;J=!0;let O=E.env.APP_ENV||E.env.NODE_ENV||"production";R.success(`Using AWS credentials from .env.${O}`)}}if(!J){let F=H6();if(F.accessKeyId&&F.secretAccessKey){if(E.env.AWS_ACCESS_KEY_ID=F.accessKeyId,E.env.AWS_SECRET_ACCESS_KEY=F.secretAccessKey,F.region&&!E.env.AWS_REGION)E.env.AWS_REGION=F.region;J=!0,R.success("Using AWS credentials from ~/.aws/credentials")}}if(!J){if(R.info("AWS credentials not found in .env or ~/.aws/credentials."),R.info("You can either:"),R.info(" 1. Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in .env.production"),R.info(" 2. Add credentials to ~/.aws/credentials"),R.info(" 3. Configure them interactively below"),console.log(""),Q?.yes)R.info("Skipping credential setup (--yes flag provided)"),E.exit(K0.FatalError);let F=await j$.confirm({message:"Would you like to configure AWS credentials now?",initial:!0});if(R.debug("setupCredentials response:",F,typeof F),F===void 0||F===!1){if(F===void 0)console.log(""),R.info("Deployment cancelled"),E.exit(K0.Success);return console.log(""),R.info("Skipping cloud infrastructure check"),R.info("You can configure AWS credentials later by running: buddy configure:aws"),!0}await U6()}else R.success("AWS credentials found");let Y=(E.env.APP_NAME||p3.name||"stacks").toLowerCase().replace(/[^a-z0-9-]/g,"-"),G=`${Y}-cloud`,{AWSCloudFormationClient:z}=await import("@stacksjs/ts-cloud"),W=new z(E.env.AWS_REGION||"us-east-1"),X=!1,H=!1;try{let F=(await W.describeStacks({stackName:G})).Stacks?.[0];if(F){X=!0,R.success("Cloud stack exists");let{AWSCloudFormationClient:O}=await import("@stacksjs/ts-cloud"),T=await new O(E.env.AWS_REGION||"us-east-1").listStackResources(G),f=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="EmailBucket"),P=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="OutboundEmailLambda"),I=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="EmailConversionLambda"),j=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="EmailNotificationTopic"),A=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="MailApiLambda"),h=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="MailUsersTable"),V=T.StackResourceSummaries?.some((i)=>i.LogicalResourceId==="MailServerInstance"),N=F.Outputs?.find((i)=>i.OutputKey==="EmailDomain")?.OutputValue,B=(c?.from?.address?.includes("@")?c.from.address.split("@")[1]:void 0)||"stacksjs.com";if(!f&&c?.server?.scan!==void 0)R.info("Email infrastructure not found in stack, will update..."),H=!0;else if(N&&N!==B)R.info(`Email domain changed: ${N} -> ${B}, will update...`),H=!0;else if(f&&(!P||!I||!j))R.info("Email infrastructure incomplete, will update..."),H=!0;else if(f&&(!A||!h))R.info("Mail API infrastructure missing, will update..."),H=!0;else if(f&&!V&&c?.server?.enabled)R.info("Mail server EC2 instance missing, will update..."),H=!0;let v=(F.Outputs||[]).find((i)=>i.OutputKey==="MailServerMode")?.OutputValue,B0=c?.server?.mode||"serverless";if(v&&v!==B0)R.info(`Mail server mode changed: ${v} -> ${B0}, will update...`),H=!0;if(V&&c?.server?.enabled){if(E.env.FORCE_MAIL_UPDATE==="true")R.info("Forcing mail server update..."),H=!0}if(!H)return!0}}catch(F){R.debug(`Stack not found: ${F.message}`)}if(!X)R.info("Cloud stack not found, will be created by deploy action");return!0}catch(J){if(!$){if(R.error("Error checking cloud infrastructure"),R.error(`Error: ${J.message||J}`),Q?.verbose)console.error(J)}E.exit(K0.FatalError)}}import x from"process";import{bold as J7,cyan as m0,dim as Q0,green as Y7,intro as P2,log as N0,onUnknownSubcommand as V6,outro as U$,prompts as B6,runCommand as R6}from"@stacksjs/cli";import{Action as i3}from"@stacksjs/enums";import{libsPath as w6}from"@stacksjs/path";import{ExitCode as u0}from"@stacksjs/types";var a3="0.70.29";var f2;async function A0(){if(!f2)f2=await import("@stacksjs/actions");return f2}function AW(Q){let $={dev:"Start development server",frontend:"Start the frontend development server",components:"Start the Components development server",desktop:"Start the Desktop App development server",dashboard:"Start the Dashboard development server",api:"Start the local API development server",email:"Start the Email development server",docs:"Start the Documentation development server",systemTray:"Start the System Tray development server",interactive:"Get asked which development server to start",select:"Which development server are you trying to start?",withLocalhost:"Include the localhost URL in the output",project:"Target a specific project",verbose:"Enable verbose output"};Q.command("dev [server]",$.dev).option("-f, --frontend",$.frontend).option("-a, --api",$.api).option("-e, --email",$.email).option("-c, --components",$.components).option("-d, --dashboard",$.dashboard).option("-k, --desktop",$.desktop).option("-o, --docs",$.docs).option("-s, --system-tray",$.systemTray).option("-i, --interactive",$.interactive,{default:!1}).option("-l, --with-localhost",$.withLocalhost,{default:!1}).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J,Y)=>{let G=Bun.nanoseconds(),z=J||(Y.frontend?"frontend":void 0)||(Y.api?"api":void 0)||(Y.components?"components":void 0)||(Y.dashboard?"dashboard":void 0)||(Y.desktop?"desktop":void 0)||(Y.systemTray||Y["system-tray"]?"system-tray":void 0)||(Y.docs?"docs":void 0);if(z){let W={...Y},X=await A0();switch(z){case"frontend":await X.runFrontendDevServer(W);break;case"api":await X.runApiDevServer(W);break;case"components":await X.runComponentsDevServer(W);break;case"dashboard":await X.runDashboardDevServer(W);break;case"desktop":await X.runDesktopDevServer(W);break;case"system-tray":await X.runSystemTrayDevServer(W);break;case"docs":await X.runDocsDevServer(W);break;default:N0.error(`Unknown server: ${z}`),x.exit(u0.InvalidArgument)}}else if(P6(Y)){let X=(await B6({type:"select",name:"value",message:$.select,choices:[{value:"all",title:"All"},{value:"frontend",title:"Frontend"},{value:"api",title:"Backend"},{value:"dashboard",title:"Dashboard"},{value:"desktop",title:"Desktop"},{value:"email",title:"Email"},{value:"components",title:"Components"},{value:"docs",title:"Documentation"}]})).value;if(X==="components")await(await A0()).runComponentsDevServer(Y);else if(X==="api")await(await A0()).runApiDevServer(Y);else if(X==="dashboard")await(await A0()).runDashboardDevServer(Y);else if(X==="docs")await(await A0()).runDocsDevServer(Y);else N0.error("Invalid option during interactive mode"),x.exit(u0.InvalidArgument)}else await M6(Y,G);U$("Exited",{startTime:G,useSeconds:!0}),x.exit(u0.Success)}),Q.command("dev:components",$.components).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{let Y=await P2("buddy dev:components"),G=await R6("bun run dev",{cwd:w6("components/vue")});if(J.verbose)N0.info("buddy dev:components result",G);if(G.isErr)await U$("While running the dev:components command, there was an issue",{startTime:Y,useSeconds:!0},G.error),x.exit(u0.FatalError);console.log(""),await U$("Exited",{startTime:Y,useSeconds:!0}),x.exit(u0.Success)}),Q.command("dev:docs",$.docs).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{let Y=await P2("buddy dev:docs"),G=await(await A0()).runAction(i3.DevDocs,J);if(G.isErr)await U$("While running the dev:docs command, there was an issue",{startTime:Y,useSeconds:!0},G.error),x.exit(u0.FatalError);console.log(""),await U$("Exited",{startTime:Y,useSeconds:!0}),x.exit(u0.Success)}),Q.command("dev:desktop",$.desktop).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{let Y=await P2("buddy dev:desktop"),G=await(await A0()).runAction(i3.DevDesktop,J);if(G.isErr)await U$("While running the dev:desktop command, there was an issue",{startTime:Y,useSeconds:!0},G.error),x.exit(u0.FatalError);console.log(""),await U$("Exited",{startTime:Y,useSeconds:!0}),x.exit(u0.Success)}),Q.command("dev:api",$.api).option("-p, --project [project]",$.project,{default:!1}).option("--no-watch-types","Skip the model/config type-regeneration watcher",{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{let Y=await A0();if(J.watchTypes!==!1)Y.watchTypes(J).catch((G)=>{N0.warn("[dev:api] type watcher exited:",G)});await Y.runApiDevServer(J)}),Q.command("dev:frontend",$.frontend).alias("dev:pages").alias("dev:views").option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{await(await A0()).runFrontendDevServer(J)}),Q.command("dev:dashboard",$.dashboard).alias("dev:admin").option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{await(await A0()).runDashboardDevServer(J)}),Q.command("dev:system-tray",$.systemTray).alias("dev:tray").option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J)=>{await(await A0()).runSystemTrayDevServer(J)}),V6(Q,"dev")}async function M6(Q,$){let J=x.env.APP_URL,Y=Number(x.env.PORT)||3000,G=Number(x.env.PORT_API)||3008,z=Number(x.env.PORT_DOCS)||3006,W=Number(x.env.PORT_ADMIN)||3002,X=x.env.STACKS_DEV_DASHBOARD==="1",H=J&&J!=="localhost"&&!J.includes("localhost:"),q=H?J.replace(/^https?:\/\//,""):null,Z=q?`api.${q}`:null,K=q?`docs.${q}`:null,_=q?`dashboard.${q}`:null,U=q?`https://${q}`:`http://localhost:${Y}`,L=Z?`https://${Z}`:`http://localhost:${G}`,F=K?`https://${K}`:`http://localhost:${z}`,O=_?`https://${_}`:`http://localhost:${W}`;if(console.log(),console.log(` ${J7(m0("stacks"))} ${Q0(`v${a3}`)}`),console.log(),console.log(` ${Y7("\u279C")} ${J7("Frontend")}: ${m0(U)}`),console.log(` ${Y7("\u279C")} ${J7("API")}: ${m0(L)}`),console.log(` ${Y7("\u279C")} ${J7("Docs")}: ${m0(F)}`),X)console.log(` ${Y7("\u279C")} ${J7("Dashboard")}: ${m0(O)}`);if(Q.verbose&&q){if(console.log(` ${Q0("\u279C")} ${Q0("Proxy")}: ${Q0(`localhost:${Y} \u2192 ${q}`)}`),console.log(` ${Q0("\u279C")} ${Q0("Proxy")}: ${Q0(`localhost:${G} \u2192 ${Z}`)}`),console.log(` ${Q0("\u279C")} ${Q0("Proxy")}: ${Q0(`localhost:${z} \u2192 ${K}`)}`),X)console.log(` ${Q0("\u279C")} ${Q0("Proxy")}: ${Q0(`localhost:${W} \u2192 ${_}`)}`)}if(console.log(),x.env.STACKS_PROXY_MANAGED="1",x.env.STACKS_DEV_NO_KILL!=="1")try{await Bun.spawn(["pkill","-9","-f","bun --watch storage/framework/core/actions/src/dev/"],{stdout:"ignore",stderr:"ignore"}).exited,await new Promise((V)=>setTimeout(V,200))}catch{}let M=!1,T=1500,f=()=>{if(M)return;M=!0;try{x.kill(-x.pid,"SIGTERM")}catch{try{x.kill(0,"SIGTERM")}catch{}}setTimeout(()=>{try{x.kill(0,"SIGKILL")}catch{x.exit(1)}},T).unref()};x.on("SIGINT",f),x.on("SIGTERM",f);let P={...Q,quiet:!0},I=await A0(),j=[{name:"Frontend",port:Y},{name:"API",port:G},{name:"Docs",port:z},...X?[{name:"Dashboard",port:W}]:[]],A=30000,h=!1;Promise.all(j.map((V)=>j6(V.port,A))).then(async(V)=>{if(h)return;h=!0;let N=V.map((B,v)=>B?null:j[v].name).filter((B)=>B!==null);if($){let B=(Bun.nanoseconds()-$)/1e6,v=N.length?`ready in ${(B/1000).toFixed(1)}s \u2014 ${N.join(", ")} did not bind within ${A/1000}s`:`ready in ${(B/1000).toFixed(1)}s`;console.log(` ${Q0(v)}
304
304
  `)}if(x.env.STACKS_PRINT_ROUTES==="1")await I6(G).catch(()=>{})}).catch(()=>{}),await Promise.all([I.runFrontendDevServer(P).catch((V)=>{if(Q.verbose)N0.error(`Frontend: ${V}`)}),I.runApiDevServer(P).catch((V)=>{if(Q.verbose)N0.error(`API: ${V}`)}),I.runDocsDevServer(P).catch((V)=>{if(Q.verbose)N0.error(`Docs: ${V}`)}),X?I.runDashboardDevServer(P).catch((V)=>{if(Q.verbose)N0.error(`Dashboard: ${V}`)}):Promise.resolve(),H?A6(Q).catch((V)=>{if(Q.verbose)N0.warn(`Proxy: ${V}`)}):Promise.resolve()])}var T6={GET:m0,POST:Y7,PUT:m0,PATCH:m0,DELETE:m0,OPTIONS:Q0};async function I6(Q){try{let $=new AbortController,J=setTimeout(()=>$.abort(),1500),Y=await fetch(`http://127.0.0.1:${Q}/__routes`,{signal:$.signal}).catch(()=>null);if(clearTimeout(J),!Y||!Y.ok)return;let G=await Y.json();if(!Array.isArray(G)||G.length===0)return;let z=[...G].sort((X,H)=>X.path.localeCompare(H.path)||X.method.localeCompare(H.method)),W=Math.max(...z.map((X)=>X.path.length));console.log(` ${Q0("Registered routes")}
305
305
  `);for(let X of z){let q=(T6[X.method.toUpperCase()]??Q0)(X.method.toUpperCase().padEnd(6)),Z=X.path.padEnd(W),K=X.name?` ${Q0(`(${X.name})`)}`:"";console.log(` ${q} ${Z}${K}`)}console.log()}catch{}}async function j6(Q,$){let J=Date.now()+$;while(Date.now()<J){if(await new Promise((G)=>{let z=new AbortController,W=setTimeout(()=>z.abort(),800);fetch(`http://127.0.0.1:${Q}/__health`,{signal:z.signal,redirect:"manual"}).then(()=>{clearTimeout(W),G(!0)}).catch(()=>{clearTimeout(W),G(!1)})}))return!0;await new Promise((G)=>setTimeout(G,250))}return!1}async function A6(Q){let $=x.env.APP_URL;if(!$||$==="localhost"||$.includes("localhost:"))return;let J=$.replace(/^https?:\/\//,""),Y=`api.${J}`,G=`docs.${J}`,z=`dashboard.${J}`,W=Number(x.env.PORT)||3000,X=Number(x.env.PORT_API)||3008,H=Number(x.env.PORT_DOCS)||3006,q=Number(x.env.PORT_ADMIN)||3002,Z=`${x.env.HOME}/.stacks/ssl`,K=Q.verbose??!1,_=x.env.STACKS_DEV_DASHBOARD==="1";try{let{startProxies:U}=await import("@stacksjs/rpx");await U({proxies:[{from:`localhost:${W}`,to:J,cleanUrls:!1,pathRewrites:[{from:"/api",to:`localhost:${X}`,stripPrefix:!1}]},{from:`localhost:${X}`,to:Y,cleanUrls:!1},{from:`localhost:${H}`,to:G,cleanUrls:!1},..._?[{from:`localhost:${q}`,to:z,cleanUrls:!1}]:[]],https:{basePath:Z,validityDays:825},regenerateUntrustedCerts:!1,verbose:K})}catch(U){if(Q.verbose)N0.warn("Reverse proxy not available, skipping HTTPS"),N0.warn(String(U))}}function P6(Q){return Q.interactive}import s3 from"process";import{log as o3,onUnknownSubcommand as f6}from"@stacksjs/cli";import{config as v6}from"@stacksjs/config";import{DnsClient as S6,formatOutput as E6}from"@stacksjs/dnsx";import{ExitCode as t3}from"@stacksjs/types";function CW(Q){let $={dns:"Lists the DNS records for a domain",query:"Host name or IP address to query",type:"Type of the DNS record being queried (A, MX, NS\u2026)",nameserver:"Address of the nameserver to send packets to",class:"Network class of the DNS record being queried (IN, CH, HS)",udp:"Use the DNS protocol over UDP",tcp:"Use the DNS protocol over TCP",tls:"Use the DNS-over-TLS protocol",https:"Use the DNS-over-HTTPS protocol",short:"Short mode: display nothing but the first result",json:"Display the output as JSON",project:"Target a specific project",verbose:"Enable verbose output"};Q.command("dns [domain]",$.dns).option("-q, --query <query>",$.query).option("-t, --type <type>",$.type,{default:"A"}).option("-n, --nameserver <nameserver>",$.nameserver).option("--class <class>",$.class).option("-U, --udp",$.udp).option("-T, --tcp",$.tcp).option("-S, --tls",$.tls).option("-H, --https",$.https).option("-1, --short",$.short,{default:!1}).option("-J, --json",$.json,{default:!1}).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(J,Y)=>{o3.debug("Running `buddy dns [domain]` ...",Y);let G=J||v6.app.url;try{let z=new S6({domains:[G],type:Y.type,nameserver:Y.nameserver,class:Y.class,udp:Y.udp,tcp:Y.tcp,tls:Y.tls,https:Y.https,short:Y.short,json:Y.json,verbose:Y.verbose}),W=performance.now(),X=await z.query(),H=performance.now()-W,q=E6(X,{json:Y.json??!1,short:Y.short??!1,showDuration:H,colors:{enabled:!0},rawSeconds:!1});console.log(q)}catch(z){o3.error(`DNS query failed: ${z instanceof Error?z.message:String(z)}`),s3.exit(t3.FatalError)}s3.exit(t3.Success)}),f6(Q,"dns")}import c0 from"process";import{bold as e3,dim as v2,green as $1,intro as N6,log as R0,onUnknownSubcommand as C6,red as h6,yellow as Q1}from"@stacksjs/cli";import{storage as J1}from"@stacksjs/storage";async function G7(Q,$,J){let Y=Date.now();try{let G=new AbortController,z=setTimeout(()=>G.abort(),2000),W=await Promise.race([J(),new Promise((X,H)=>G.signal.addEventListener("abort",()=>H(Error("timed out (>2s)"))))]);clearTimeout(z),Q.push({name:$,status:"pass",message:`${W} (${Date.now()-Y}ms)`})}catch(G){Q.push({name:$,status:"fail",message:G instanceof Error?G.message:String(G)})}}function kW(Q){Q.command("doctor","Run health checks on your Stacks installation").action(async()=>{R0.debug("Running `buddy doctor` ..."),await N6("buddy doctor");let $=[],J=c0.versions.bun;if(J)if(Number.parseInt(J.split(".")[0]||"0",10)>=1)$.push({name:"Bun Runtime",status:"pass",message:`v${J}`});else $.push({name:"Bun Runtime",status:"warn",message:`v${J} (v1.0+ recommended)`});else $.push({name:"Bun Runtime",status:"fail",message:"Not found"});let Y=c0.versions.node;if(Y)if(Number.parseInt(Y.split(".")[0]||"0",10)>=18)$.push({name:"Node.js",status:"pass",message:`v${Y}`});else $.push({name:"Node.js",status:"warn",message:`v${Y} (v18+ recommended)`});try{let q=await J1.readPackageJson("./package.json");if(q.name)$.push({name:"package.json",status:"pass",message:`Found: ${q.name}`})}catch{$.push({name:"package.json",status:"fail",message:"Not found in current directory"})}try{await J1.readTextFile(".env"),$.push({name:".env file",status:"pass",message:"Found"})}catch{$.push({name:".env file",status:"warn",message:"Not found (optional)"})}let G=c0.env.APP_KEY;if(G&&G.length>0)$.push({name:"APP_KEY",status:G.length>=32?"pass":"warn",message:G.length>=32?"Set (\u226532 chars)":`Set but short (${G.length} chars; \u226532 recommended)`});else $.push({name:"APP_KEY",status:"fail",message:"Not set \u2014 features that depend on it (encrypted columns, signed URLs, env decryption) will refuse to run. Run `buddy key:generate`."});await G7($,"Database",async()=>{let{db:q}=await import("@stacksjs/database");return await q.unsafe?.("SELECT 1"),"Reachable"}),await G7($,"Cache",async()=>{let{cache:q}=await import("@stacksjs/cache"),Z=`__doctor_${Date.now()}`;await q.set(Z,1,5);let K=await q.get(Z);if(await q.del(Z),K!==1)throw Error("round-trip failed");return"Round-trip ok"}),await G7($,"Queue driver",async()=>{let{config:q}=await import("@stacksjs/config");return`Driver: ${q.queue?.default??"sync"}`}),await G7($,"Mail driver",async()=>{let{config:q}=await import("@stacksjs/config");return`Driver: ${q.email?.default??"log"}`});let z=Boolean(c0.env.AWS_ACCESS_KEY_ID&&c0.env.AWS_SECRET_ACCESS_KEY),W=Boolean(c0.env.AWS_PROFILE)||Boolean(c0.env.AWS_ROLE_ARN);if(z||W)$.push({name:"AWS credentials",status:"pass",message:z?"Static keys in env":"IAM role configured"});else $.push({name:"AWS credentials",status:"warn",message:"Not configured (cloud / SES / S3 commands will fail)"});await G7($,".env decryption",async()=>{let q=await import("fs");if(!q.existsSync(".env"))return"No .env (skipped)";let Z=q.readFileSync(".env","utf8");if(!/(?:^|=)(?:enc|encrypted):/m.test(Z))return"No encrypted values";let K=c0.env.DOTENV_PRIVATE_KEY;if(!K)throw Error("Encrypted values present but DOTENV_PRIVATE_KEY is unset");let{parse:_}=await import("@stacksjs/env"),{errors:U}=_(Z,{privateKey:K});if(U.length>0)throw Error(U.join("; "));return"All encrypted values decrypt cleanly"}),R0.info(""),R0.info(e3("Health Check Results:")),R0.info(v2("\u2500".repeat(60))),R0.info("");let X=!1,H=!1;for(let q of $){let Z="",K=(_)=>_;if(q.status==="pass")Z="\u2713",K=$1;else if(q.status==="warn")Z="\u26A0",K=Q1,H=!0;else Z="\u2717",K=h6,X=!0;R0.info(`${K(Z)} ${e3(q.name.padEnd(20))} ${v2(q.message)}`)}if(R0.info(""),R0.info(v2("\u2500".repeat(60))),R0.info(""),X)R0.error("Some critical checks failed. Please address the issues above."),c0.exit(1);else if(H)R0.info(Q1("Some checks have warnings. Your system should work but may have issues."));else R0.success($1("All checks passed! Your Stacks installation looks healthy."));R0.info("")}),C6(Q,"doctor")}import e0 from"process";import{runAction as Y1}from"@stacksjs/actions";import{bgCyan as D6,bold as b6,intro as S2,italic as L$,log as d7,onUnknownSubcommand as y6,outro as $$,prompts as G1}from"@stacksjs/cli";import{config as z1}from"@stacksjs/config";import{addDomain as k6}from"@stacksjs/dns";import{Action as W1}from"@stacksjs/enums";import{ExitCode as Q$}from"@stacksjs/types";function rW(Q){let $={purchase:"Purchase a domain",add:"Add a domain to your cloud",remove:"Remove a domain from your cloud",skip:"Skip the confirmation prompt",project:"Target a specific project",verbose:"Enable verbose output"},J=z1.dns.contactInfo;Q.command("domains:purchase <domain>",$.purchase).option("--years <years>","Number of years to purchase the domain for",{default:1}).option("--privacy","Enable privacy protection",{default:!0}).option("--auto-renew","Enable auto-renew",{default:!0}).option("--first-name <firstName>","Registrant first name",{default:J?.firstName}).option("--last-name <lastName>","Registrant last name",{default:J?.lastName}).option("--organization <organization>","Registrant organization name",{default:J?.organizationName}).option("--address-line1 <address>","Registrant address line 1",{default:J?.addressLine1}).option("--address-line2 <address>","Registrant address line 2",{default:J?.addressLine2}).option("--city <city>","Registrant city",{default:J?.city}).option("--state <state>","Registrant state",{default:J?.state}).option("--country <country>","Registrant country code",{default:J?.countryCode}).option("--zip <zip>","Registrant zip",{default:J?.zip}).option("--phone <phone>","Registrant phone",{default:J?.phoneNumber}).option("--email <email>","Registrant email",{default:J?.email}).option("--admin-first-name <firstName>","Admin first name",{default:J?.admin?.firstName||J?.firstName}).option("--admin-last-name <lastName>","Admin last name",{default:J?.admin?.lastName||J?.lastName}).option("--admin-organization <organization>","Admin organization",{default:J?.admin?.organizationName||J?.organizationName}).option("--admin-address-line1 <address>","Admin address line 1",{default:J?.admin?.addressLine1||J?.addressLine1}).option("--admin-address-line2 <address>","Admin address line 2",{default:J?.admin?.addressLine2||J?.addressLine2}).option("--admin-city <city>","Admin city",{default:J?.admin?.city||J?.city}).option("--admin-state <state>","Admin state",{default:J?.admin?.state||J?.state}).option("--admin-country <country>","Admin country code",{default:J?.admin?.countryCode||J?.countryCode}).option("--admin-zip <zip>","Admin zip",{default:J?.admin?.zip||J?.zip}).option("--admin-phone <phone>","Admin phone number",{default:J?.admin?.phoneNumber||J?.phoneNumber}).option("--admin-email <email>","Admin email",{default:J?.admin?.email||J?.email}).option("--tech-first-name <firstName>","Tech first name",{default:J?.tech?.firstName||J?.firstName}).option("--tech-last-name <lastName>","Tech last name",{default:J?.tech?.lastName||J?.lastName}).option("--tech-organization <organization>","Tech organization name",{default:J?.tech?.organizationName||J?.organizationName}).option("--tech-address-line1 <address>","Tech address line 1",{default:J?.tech?.addressLine1||J?.addressLine1}).option("--tech-address-line2 <address>","Tech address line 2",{default:J?.tech?.addressLine2||J?.addressLine2}).option("--tech-city <city>","Tech city",{default:J?.tech?.city||J?.city}).option("--tech-state <state>","Tech state",{default:J?.tech?.state||J?.state}).option("--tech-country <country>","Tech country",{default:J?.tech?.countryCode||J?.countryCode}).option("--tech-zip <zip>","Tech zip",{default:J?.tech?.zip||J?.zip}).option("--tech-phone <phone>","Tech phone",{default:J?.tech?.phoneNumber||J?.phoneNumber}).option("--tech-email <email>","Tech email",{default:J?.tech?.email||J?.email}).option("--privacy-admin","Enable privacy protection for admin",{default:J?.privacyAdmin||J?.privacy||!0}).option("--privacy-tech","Enable privacy protection for tech",{default:J?.privacyTech||J?.privacy||!0}).option("--privacy-registrant","Enable privacy protection for registrant",{default:J?.privacyRegistrant||J?.privacy||!0}).option("--contact-type <type>","Contact type",{default:"person"}).option("-p, --project [project]",$.project,{default:!1}).option("--verbose",$.verbose,{default:!1}).action(async(Y,G)=>{d7.debug("Running `buddy domains:purchase <domain>` ...",G),G.domain=Y;let z=await S2("buddy domains:purchase"),W=await Y1(W1.DomainsPurchase,G);if(W.isErr)await $$("While running the domains:purchase command, there was an issue",{startTime:z,useSeconds:!0},W.error),e0.exit(Q$.FatalError);let{confirm:X}=await G1({name:"confirm",type:"confirm",message:`Would you like to set ${Y} as your APP_URL?`});if(!X)await $$(`Alrighty! ${L$(Y)} was added to your account.`,{startTime:z,useSeconds:!0,type:"success"}),d7.info(`Please note, you may need to validate your email address. Check your ${L$(G.registrantEmail)} inbox.`),e0.exit(Q$.Success);let{writeEnv:H}=await import("@stacksjs/env");H("APP_URL",Y);let q=`Great! ${L$(Y)} was added to your account.`;q+=`
306
306
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/buddy",
3
3
  "type": "module",
4
- "version": "0.70.28",
4
+ "version": "0.70.29",
5
5
  "description": "Meet Buddy. The Stacks runtime.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [