@stacksjs/ts-cloud 0.16.47 → 0.16.48
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/aws/index.js +1 -1
- package/dist/bin/{chunk-2gqhzn2r.js → chunk-1fj03993.js} +1 -1
- package/dist/bin/{chunk-t1mmfpn1.js → chunk-2pzh0wxc.js} +1 -1
- package/dist/bin/{chunk-yp3a0j9n.js → chunk-3pazrfsf.js} +5 -5
- package/dist/bin/{chunk-qq1f07te.js → chunk-4mecfcjk.js} +2 -2
- package/dist/bin/{chunk-dn1yybdy.js → chunk-akf9j8ps.js} +1 -1
- package/dist/bin/{chunk-nhqz2j90.js → chunk-cf3jn2ey.js} +1 -1
- package/dist/bin/{chunk-nmr484r7.js → chunk-g2kmr82n.js} +4 -4
- package/dist/bin/chunk-hhhrze6m.js +1 -0
- package/dist/bin/{chunk-rxy9fa64.js → chunk-nd8vnqk5.js} +79 -79
- package/dist/bin/{chunk-her9ny96.js → chunk-psmpw0ap.js} +1 -1
- package/dist/bin/{chunk-de69nra4.js → chunk-vhyp4xj0.js} +2 -2
- package/dist/bin/chunk-zjvg5pg8.js +1 -0
- package/dist/bin/cli.js +17 -17
- package/dist/bin/dashboard-server.js +1 -1
- package/dist/{chunk-6ea5d30w.js → chunk-22hfejjs.js} +1 -1
- package/dist/{chunk-bvzmbksp.js → chunk-25sw2s70.js} +1 -1
- package/dist/{chunk-1dbe68z7.js → chunk-2pns2rkf.js} +4 -4
- package/dist/{chunk-zwkm3pkb.js → chunk-6mp1fyxe.js} +1 -1
- package/dist/{chunk-j2jex596.js → chunk-8vtrjmmj.js} +51 -51
- package/dist/{chunk-jzpwfgg1.js → chunk-da62knsv.js} +15 -15
- package/dist/{chunk-wb7h8ag4.js → chunk-e8k29wen.js} +1 -1
- package/dist/{chunk-wmxw3pe4.js → chunk-fpkpwbv3.js} +5 -5
- package/dist/{chunk-v1bzy3tp.js → chunk-gh0ghb21.js} +2 -2
- package/dist/chunk-gqadvddh.js +1 -0
- package/dist/{chunk-tj852ns1.js → chunk-jhtp3m31.js} +2 -2
- package/dist/chunk-kpmxvpcv.js +1 -0
- package/dist/{chunk-j73ntebp.js → chunk-kr8aeva1.js} +13 -13
- package/dist/{chunk-46bazfbh.js → chunk-ndrpehf8.js} +84 -84
- package/dist/{chunk-79snvzcx.js → chunk-p7myt05d.js} +3 -3
- package/dist/{chunk-yxzdwe7j.js → chunk-vnarc5gc.js} +3 -3
- package/dist/{chunk-astzz50p.js → chunk-x0ep5ybv.js} +2 -2
- package/dist/{chunk-r86dh7h7.js → chunk-yme6q7k4.js} +2 -2
- package/dist/deploy/index.js +1 -1
- package/dist/deploy/server-dns.d.ts +7 -1
- package/dist/dns/index.js +1 -1
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/shared/rpx-gateway.d.ts +39 -2
- package/dist/index.js +3 -3
- package/dist/operations/index.js +1 -1
- package/dist/protection/index.js +1 -1
- package/dist/spend/index.js +1 -1
- package/dist/ui/404.html +1 -1
- package/dist/ui/accept-invitation.html +1 -1
- package/dist/ui/access-denied.html +1 -1
- package/dist/ui/account/automation.html +6 -6
- package/dist/ui/account/security.html +2 -2
- package/dist/ui/applications/compose.html +6 -6
- package/dist/ui/applications/new.html +2 -2
- package/dist/ui/data/backups.html +6 -6
- package/dist/ui/data/services.html +6 -6
- package/dist/ui/data/volumes.html +6 -6
- package/dist/ui/index.html +6 -6
- package/dist/ui/infrastructure/topology.html +6 -6
- package/dist/ui/integrations.html +1 -1
- package/dist/ui/operations/alerts.html +6 -6
- package/dist/ui/operations/configuration.html +6 -6
- package/dist/ui/operations/jobs.html +6 -6
- package/dist/ui/operations/maintenance.html +5 -5
- package/dist/ui/operations/observability.html +6 -6
- package/dist/ui/operations/previews.html +6 -6
- package/dist/ui/operations/queue.html +6 -6
- package/dist/ui/operations/regions.html +5 -5
- package/dist/ui/operations/releases.html +6 -6
- package/dist/ui/operations/spend.html +6 -6
- package/dist/ui/operations/workloads.html +6 -6
- package/dist/ui/security.html +2 -2
- package/dist/ui/server/actions.html +6 -6
- package/dist/ui/server/activity.html +2 -2
- package/dist/ui/server/capacity.html +6 -6
- package/dist/ui/server/database.html +6 -6
- package/dist/ui/server/deployments.html +5 -5
- package/dist/ui/server/diagnostics.html +2 -2
- package/dist/ui/server/firewall.html +6 -6
- package/dist/ui/server/fleet.html +6 -6
- package/dist/ui/server/logs.html +5 -5
- package/dist/ui/server/metrics.html +5 -5
- package/dist/ui/server/security.html +2 -2
- package/dist/ui/server/services.html +1 -1
- package/dist/ui/server/sites.html +6 -6
- package/dist/ui/server/ssh-keys.html +6 -6
- package/dist/ui/server/team.html +6 -6
- package/dist/ui/server/terminal.html +2 -2
- package/dist/ui/serverless/alarms.html +6 -6
- package/dist/ui/serverless/assets.html +2 -2
- package/dist/ui/serverless/cost.html +2 -2
- package/dist/ui/serverless/data.html +6 -6
- package/dist/ui/serverless/deployments.html +2 -2
- package/dist/ui/serverless/firewall.html +1 -1
- package/dist/ui/serverless/functions.html +6 -6
- package/dist/ui/serverless/logs.html +6 -6
- package/dist/ui/serverless/metrics.html +2 -2
- package/dist/ui/serverless/queues.html +6 -6
- package/dist/ui/serverless/secrets.html +6 -6
- package/dist/ui/serverless/traces.html +6 -6
- package/dist/ui/serverless.html +6 -6
- package/package.json +3 -3
- package/dist/bin/chunk-r1kg6yxp.js +0 -1
- package/dist/bin/chunk-vt3hjh9q.js +0 -1
- package/dist/chunk-4t0vj9a4.js +0 -1
- package/dist/chunk-s7q23exe.js +0 -1
package/dist/bin/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import{C,jt,n,r,a,Rr,e,d,i,s,R,u,Do,c,dn,Ce,Ms,Us,Fs,Bs,f}from"./chunk-6y6g726g.js";import{sa,H,$r,w,B,Bo,l,o,T,la,da,ua,xe,Be,tt,Vo,bn,xi,Sn,zo,fa,ha,ya,ba,va,j,b,L,rt,k,fe,qo,Ve,qt,_i,Ae,Go,Oi,Gt,At,Ca,Ta}from"./chunk-dj03v81n.js";import"./chunk-4yym8xm4.js";import{Oe,x,Pn,y,Dn,Hi,kn,es,Br,it,ke,Nn,xn,_n,On,$n,Ln,Mn,Un,ie,Zt,er,Bn,jr,jn,Vn,zn,Hn,qn,Gn,Wn,Kn,ot,tr,rr,Dt,Vr,nr,ir,qi,kt,Jn,ce,Xn,Qn,Yn,zr,Hr,Zn,qr,or,sr,st,Nt,xt,Gr,le,ei,ac,cc,ar,lc,q,Gi,ze,at,ct,ti,Wr,Kr,ri,ni,ii,Jr,ts,de,be,$e,Se,oi,cr}from"./chunk-
|
|
2
|
+
import{C,jt,n,r,a,Rr,e,d,i,s,R,u,Do,c,dn,Ce,Ms,Us,Fs,Bs,f}from"./chunk-6y6g726g.js";import{sa,H,$r,w,B,Bo,l,o,T,la,da,ua,xe,Be,tt,Vo,bn,xi,Sn,zo,fa,ha,ya,ba,va,j,b,L,rt,k,fe,qo,Ve,qt,_i,Ae,Go,Oi,Gt,At,Ca,Ta}from"./chunk-dj03v81n.js";import"./chunk-4yym8xm4.js";import{Oe,x,Pn,y,Dn,Hi,kn,es,Br,it,ke,Nn,xn,_n,On,$n,Ln,Mn,Un,ie,Zt,er,Bn,jr,jn,Vn,zn,Hn,qn,Gn,Wn,Kn,ot,tr,rr,Dt,Vr,nr,ir,qi,kt,Jn,ce,Xn,Qn,Yn,zr,Hr,Zn,qr,or,sr,st,Nt,xt,Gr,le,ei,ac,cc,ar,lc,q,Gi,ze,at,ct,ti,Wr,Kr,ri,ni,ii,Jr,ts,de,be,$e,Se,oi,cr}from"./chunk-g2kmr82n.js";import{p,ta}from"./chunk-psvqpztv.js";import{ae,h,Or}from"./chunk-znd507q9.js";import{Dr,Tt,kr,Nr,xr,_r,gn}from"./chunk-cz2wq0tf.js";import{S,Ot,pi,fs,hr,yc,bc,Sc,mi,gi,mt,nn,$t,on,gt,Je,Me,Xe,ft,yr,Ue,Qe,fi,hi,Ye,sn,an,so,co,vr,Lt,ht,Mt,ee,mo,Si,vi,Is}from"./chunk-ym9h629y.js";import{Ze,Ut,Tr,ks,Ns,Er,ge,Ci,wr,yo,bo,So,vo,Co,To,W,Ft,M,Ti}from"./chunk-q4dywv80.js";import{J,I,ko,Hs,qs,Vc,zc,Ai,vt,Ri}from"./chunk-3pazrfsf.js";import{v}from"./chunk-xdk0gmxt.js";import{Q}from"./chunk-q8b77zgw.js";import{Ct,g}from"./chunk-12x968en.js";import{qe,rs,ue,Wi,ai,Ki,Ji,Xr,Xi,Qi,ve,Z,Ge,Ne,Qr,lr,We,dr,Yr,dt,pe,ut,pr,mr,gr,Zr,fr,O,Zi,eo,ci,ns,en,is,os,pc,to,ss,as,cs,ls,pt,ds,ro,no}from"./chunk-nd8vnqk5.js";import"./chunk-gyvkravq.js";import{P}from"./chunk-vhyp4xj0.js";import{me}from"./chunk-z8k9mcn5.js";import{K,bt,Io}from"./chunk-4mecfcjk.js";import{oe,G,dc,uc,He}from"./chunk-cf3jn2ey.js";import{Qt,Rn,Yt,Vi,In,Zo}from"./chunk-zvbc92pc.js";import"./chunk-psmpw0ap.js";import{E}from"./chunk-01w4zbex.js";import{lt}from"./chunk-frzpqsbj.js";import{te,Te}from"./chunk-23m8kbwh.js";import{V}from"./chunk-xx9mrcfs.js";import"./chunk-nj9x0tb8.js";import{zi}from"./chunk-v09tfezh.js";import"./chunk-v7t6f8aw.js";import"./chunk-w29z877b.js";import{ui}from"./chunk-n7yefy09.js";import{Cr}from"./chunk-k343q24p.js";import{yt}from"./chunk-xe527q1k.js";import{F}from"./chunk-7qbxq0be.js";import{N}from"./chunk-kbwm0sqv.js";import{existsSync as Lu}from"node:fs";import Ea from"node:fs/promises";import zl from"node:os";import Na from"node:path";import lo from"node:process";import{EventEmitter as Fu}from"node:events";import Fi from"node:process";import Ul from"node:process";import Fa from"node:process";import js from"node:process";import Jo from"node:process";import Gl from"node:tty";import km,{stdin as Xh,stdout as ey}from"node:process";class Hl{configPath;config=null;events=[];retryCount=0;maxRetries=3;retryDelayMs=1000;constructor(){let D=zl.homedir(),t=Na.join(D,".config","clapp");this.configPath=Na.join(t,"telemetry.json")}async isEnabled(){if(lo.env.DO_NOT_TRACK==="1"||lo.env.DO_NOT_TRACK==="true")return!1;if(lo.env.NO_TELEMETRY==="1"||lo.env.NO_TELEMETRY==="true")return!1;return(await this.loadConfig()).enabled}async enable(){let D=await this.loadConfig();if(D.enabled=!0,!D.userId)D.userId=this.generateUserId();await this.saveConfig(D)}async disable(){let D=await this.loadConfig();D.enabled=!1,await this.saveConfig(D)}async track(D,t){if(!await this.isEnabled())return;let A={event:D,...t,timestamp:Date.now(),platform:zl.platform(),nodeVersion:lo.version};if(this.events.push(A),this.events.length>=10)await this.send()}async trackCommand(D,t){await this.track("command",{command:D,duration:t})}async trackError(D,t){await this.track("error",{error:D,command:t})}async send(){if(!await this.isEnabled()||this.events.length===0)return!0;try{this.events=[],this.retryCount=0;let t=await this.loadConfig();return t.lastSent=Date.now(),await this.saveConfig(t),!0}catch{if(this.retryCount<this.maxRetries){this.retryCount++;let t=this.retryDelayMs*2**(this.retryCount-1);return await this.sleep(t),this.send()}return this.events=[],this.retryCount=0,!1}}async flush(){if(this.events.length===0)return!0;return this.send()}sleep(D){return new Promise((t)=>setTimeout(t,D))}async status(){let D=await this.loadConfig();return{enabled:D.enabled,doNotTrack:lo.env.DO_NOT_TRACK==="1"||lo.env.DO_NOT_TRACK==="true",eventsQueued:this.events.length,lastSent:D.lastSent}}async loadConfig(){if(this.config)return this.config;try{if(Lu(this.configPath)){let D=await Ea.readFile(this.configPath,"utf-8");return this.config=JSON.parse(D),this.config}}catch{}return this.config={enabled:!1},this.config}async saveConfig(D){this.config=D;try{let t=Na.dirname(this.configPath);await Ea.mkdir(t,{recursive:!0}),await Ea.writeFile(this.configPath,JSON.stringify(D,null,2),"utf-8")}catch{}}generateUserId(){let D=Math.random().toString(36).substring(2,15),t=Date.now().toString(36);return`${D}-${t}`}}var Mh=new Hl;function La(D){return D.replace(/[<[].+/,"").trim()}function ju(D){let t=/<([^>]+)>/g,m=/\[([^\]]+)\]/g,A=[],_=(Y)=>{let X=!1,ne=Y[1];if(ne.startsWith("..."))ne=ne.slice(3),X=!0;return{required:Y[0].startsWith("<"),value:ne,variadic:X}},z;while(z=t.exec(D))A.push(_(z));let U;while(U=m.exec(D))A.push(_(U));return A}function Bu(D){let t={alias:{},boolean:[]};for(let[m,A]of D.entries()){if(A.names.length>1)t.alias[A.names[0]]=A.names.slice(1);if(A.isBoolean)if(A.negated){if(!D.some((z,U)=>U!==m&&z.names.some((Y)=>A.names.includes(Y))&&typeof z.required==="boolean"))t.boolean.push(A.names[0])}else t.boolean.push(A.names[0])}return t}function Wl(D){return D.reduce((t,m)=>t.length>=m.length?t:m,"")}function Oa(D,t){return D.length>=t?D:`${D}${" ".repeat(t-D.length)}`}function Ls(D){return D.replace(/([a-z])-([a-z])/g,(t,m,A)=>m+A.toUpperCase())}function _u(D,t,m){let A=0,_=t.length,z=D,U;for(;A<_;++A)U=z[t[A]],z=z[t[A]]=A===_-1?m:U!=null?U:!!~t[A+1].indexOf(".")||!(+t[A+1]>-1)?{}:[]}function Mu(D,t){for(let m of Object.keys(t)){let A=t[m];if(A.shouldTransform){if(D[m]=Array.prototype.concat.call([],D[m]),typeof A.transformFunction==="function")D[m]=D[m].map(A.transformFunction)}}}function zu(D){let t=/([^\\/]+)$/.exec(D);return t?t[1]:""}function Yo(D){return D.split(".").map((t,m)=>m===0?Ls(t):t).join(".")}class Qo extends Error{exitCode=2;isUsageError=!0;usage;constructor(D,t){super(D);if(this.name=this.constructor.name,t!==void 0)this.usage=t;if(typeof Error.captureStackTrace==="function")Error.captureStackTrace(this,this.constructor);else this.stack=Error(D).stack}format(D=!1){if(D&&this.stack)return`${this.message}
|
|
3
3
|
|
|
4
4
|
Stack trace:
|
|
5
5
|
${this.stack}`;return this.message}}function Uu(D){if(D instanceof Qo)return!0;return!!D&&typeof D==="object"&&D.name==="ClappError"&&typeof D.message==="string"}function Wu(){let{env:D}=Ul,{TERM:t,TERM_PROGRAM:m}=D;if(Ul.platform!=="win32")return t!=="linux";return Boolean(D.WT_SESSION)||Boolean(D.TERMINUS_SUBLIME)||D.ConEmuTask==="{cmd::Cmder}"||m==="Terminus-Sublime"||m==="vscode"||t==="xterm-256color"||t==="alacritty"||t==="rxvt-unicode"||t==="rxvt-unicode-256color"||D.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var _s="\x1B",cn=`${_s}[`;var Vl={to(D,t){if(!t)return`${cn}${D+1}G`;return`${cn}${t+1};${D+1}H`},move(D,t){let m="";if(D<0)m+=`${cn}${-D}D`;else if(D>0)m+=`${cn}${D}C`;if(t<0)m+=`${cn}${-t}A`;else if(t>0)m+=`${cn}${t}B`;return m},up:(D=1)=>`${cn}${D}A`,down:(D=1)=>`${cn}${D}B`,forward:(D=1)=>`${cn}${D}C`,backward:(D=1)=>`${cn}${D}D`,nextLine:(D=1)=>`${cn}E`.repeat(D),prevLine:(D=1)=>`${cn}F`.repeat(D),left:`${cn}G`,hide:`${cn}?25l`,show:`${cn}?25h`,save:`${_s}7`,restore:`${_s}8`};var Vh={screen:`${cn}2J`,up:(D=1)=>`${cn}1J`.repeat(D),down:(D=1)=>`${cn}J`.repeat(D),line:`${cn}2K`,lineEnd:`${cn}K`,lineStart:`${cn}1K`,lines(D){let t="";for(let m=0;m<D;m++)t+=this.line+(m<D-1?Vl.up():"");if(D)t+=Vl.left;return t}},qh={screen:`${_s}c`};function Vu(D,t){let m=[];for(let A=0;A<=t.length;A++)m[A]=[A];for(let A=0;A<=D.length;A++)m[0][A]=A;for(let A=1;A<=t.length;A++)for(let _=1;_<=D.length;_++)if(t.charAt(A-1)===D.charAt(_-1))m[A][_]=m[A-1][_-1];else m[A][_]=Math.min(m[A-1][_-1]+1,m[A][_-1]+1,m[A-1][_]+1);return m[t.length][D.length]}function Kl(D,t,m=2,A=3){return t.map((_)=>({cmd:_,distance:Vu(D,_)})).filter(({distance:_})=>_<=m).sort((_,z)=>_.distance-z.distance).slice(0,A).map(({cmd:_})=>_)}function qu(D,t={}){let m={_:[]},A=t.alias||{},_=new Set(t.boolean||[]),z={};for(let X of Object.keys(A))for(let ne of A[X])z[ne]=X;for(let X of _)if(A[X])for(let ne of A[X])_.add(ne);function U(X,ne){if(X===void 0)return ne;if(typeof ne==="boolean")return ne;return Array.isArray(X)?[...X,ne]:[X,ne]}function Y(X,ne){let re=z[X]||X,se=U(m[re],ne);if(m[re]=se,A[re])for(let he of A[re])m[he]=se;if(z[X]&&A[z[X]])for(let he of A[z[X]])m[he]=se;m[X]=se}for(let X=0;X<D.length;X++){let ne=D[X];if(ne==="--"){m._.push(...D.slice(X+1));break}if(ne.startsWith("--")){let re=ne.indexOf("=");if(re!==-1){let se=Ls(ne.slice(2,re)),he=ne.slice(re+1);Y(se,he)}else{let se=ne.slice(2);if(se.startsWith("no-")){let Pe=Ls(se.slice(3));Y(Pe,!1);continue}let he=Ls(se),we=z[he]||he;if(_.has(we)||_.has(he))Y(he,!0);else{let Pe=D[X+1];if(Pe!==void 0&&!Pe.startsWith("-"))Y(he,Pe),X++;else Y(he,!0)}}}else if(ne.startsWith("-")&&ne.length>1){let re=ne.slice(1);for(let se=0;se<re.length;se++){let he=re[se],we=z[he]||he;if(se===re.length-1&&!_.has(we)&&!_.has(he)){let Pe=D[X+1];if(Pe!==void 0&&!Pe.startsWith("-"))Y(he,Pe),X++;else Y(he,!0)}else Y(he,!0)}}else m._.push(ne)}return m}class Jl{rawName;description;name;names;isBoolean;required;config;negated;constructor(D,t,m){if(this.rawName=D,this.description=t,this.config=Object.assign({},m),D=D.replace(/\.\*/g,""),this.negated=!1,this.names=La(D).split(",").map((A)=>{let _=A.trim().replace(/^-{1,2}/,"");if(_.startsWith("no-"))this.negated=!0,_=_.replace(/^no-/,"");return Yo(_)}).sort((A,_)=>A.length>_.length?1:-1),this.name=this.names[this.names.length-1],this.negated&&this.config.default==null)this.config.default=!0;if(D.includes("<"))this.required=!0;else if(D.includes("["))this.required=!1;else this.isBoolean=!0}}var Hh=Fa.argv,Gu=`${Fa.platform}-${Fa.arch} bun-v${typeof Bun<"u"?Bun.version:"unknown"}`,ql=js.argv,Hu=`${js.platform}-${js.arch} node-${js.version}`;class ja{rawName;description;config;cli;options;aliasNames;name;namespace;args;commandAction;usageText;versionNumber;examples;helpCallback;globalCommand;beforeHooks;afterHooks;middleware;constructor(D,t,m,A){this.rawName=D,this.description=t,this.config=m,this.cli=A,this.options=[],this.aliasNames=[],this.name=La(D);let _=La(D),z=_.indexOf(":");if(z>0)this.namespace=_.substring(0,z),this.name=_.substring(z+1);if(this.args=ju(D),this.examples=[],this.beforeHooks=[],this.afterHooks=[],this.middleware=[],!m)this.config={}}usage(D){return this.usageText=D,this}allowUnknownOptions(){return this.config.allowUnknownOptions=!0,this}ignoreOptionDefaultValue(){return this.config.ignoreOptionDefaultValue=!0,this}version(D,t="-v, --version"){return this.versionNumber=D,this.option(t,"Display version number"),this}example(D){return this.examples.push(D),this}option(D,t,m){let A=new Jl(D,t,m);return this.options.push(A),this}alias(D){return this.aliasNames.push(D),this}action(D){return this.commandAction=D,this}before(D){return this.beforeHooks.push(D),this}after(D){return this.afterHooks.push(D),this}use(D){return this.middleware.push(D),this}isMatched(D){if(this.aliasNames.includes(D))return!0;if(this.namespace)return`${this.namespace}:${this.name}`===D;return this.name===D}get isDefaultCommand(){return this.name===""||this.aliasNames.includes("!")}get isGlobalCommand(){return this instanceof Ba}get displayName(){return this.namespace?`${this.namespace}:${this.name}`:this.name}hasOption(D){let t=Yo(D.split(".")[0]);return!!this.options.find((m)=>m.names.some((A)=>Yo(A)===t))}outputHelp(){let{name:D,commands:t}=this.cli,{versionNumber:m,options:A,helpCallback:_}=this.cli.globalCommand,z=[{body:`${D}${m?`/${m}`:""}`}];if(z.push({title:"Usage",body:` $ ${D} ${this.usageText||this.rawName}`}),(this.isGlobalCommand||this.isDefaultCommand)&&t.length>0){let ne=Wl(t.map((Pe)=>Pe.rawName)),re=new Map,se=[];for(let Pe of t)if(Pe.namespace){if(!re.has(Pe.namespace))re.set(Pe.namespace,[]);re.get(Pe.namespace).push(Pe)}else se.push(Pe);let he="";if(se.sort((Pe,ye)=>Pe.rawName<ye.rawName?-1:Pe.rawName>ye.rawName?1:0),se.length>0)he+=se.map((Pe)=>` ${Oa(Pe.rawName,ne.length)} ${Pe.description}`).join(`
|
|
@@ -28,7 +28,7 @@ Received ${m}, cleaning up...`),D)try{await D()}catch(A){console.error("Error du
|
|
|
28
28
|
`)),this.enableDidYouMean){let t=[];for(let A of this.commands){if(A.displayName)t.push(A.displayName);if(A.aliasNames)t.push(...A.aliasNames)}let m=Kl(D,t);if(m.length>0)console.log(Ko.yellow("Did you mean one of these?")),m.forEach((A)=>console.log(` ${Ko.dim("•")} ${this.name} ${A}`)),console.log("")}console.log(Ko.dim("Run"),`${this.name} --help`,Ko.dim("to see all available commands")),Fi.exit(1)}async parse(D=ql,t={}){let{run:m=!0,exitOnError:A=!1}=t;if(A)try{return await this.parse(D,{run:m})}catch(X){throw this.handleUsageError(X),X}if(this.rawArgs=D,!this.name)this.name=D[1]?zu(D[1]):"cli";let _=!0,z=D.slice(2),U=z[0];if(U&&!U.startsWith("-")){for(let X of this.commands)if(X.isMatched(U)){let ne=this.mri(z,X);_=!1;let re={...ne,args:ne.args.slice(1)};this.setParsedInfo(re,X,U),this.emit(`command:${U}`,X);break}if(_){let X=z[1];if(X&&!X.startsWith("-")){let ne=`${U}:${X}`;for(let re of this.commands)if(re.isMatched(ne)){let se=this.mri(z,re);_=!1;let he={...se,args:se.args.slice(2)};this.setParsedInfo(he,re,ne),this.emit(`command:${ne}`,re);break}}}}if(_){for(let X of this.commands)if(X.name===""){_=!1;let ne=this.mri(z,X);this.setParsedInfo(ne,X),this.emit("command:!",X);break}}if(_){let X=this.mri(D.slice(2));this.setParsedInfo(X)}if(this.options.verbose)this.isVerbose=!0;if(this.options.quiet)this.isQuiet=!0;if(this.options.debug)this.isDebug=!0;if(this.options.noInteraction||this.options.interaction===!1)this.isNoInteraction=!0;if(this.options.env)this.environment=String(this.options.env);if(this.options.dryRun)this.isDryRun=!0;if(this.options.force)this.isForce=!0;if(this.options.noEmoji||this.options.emoji===!1)this.useEmoji=!1;if(this.options.theme)this.theme=String(this.options.theme);if(this.options.noCache||this.options.cache===!1)this.isNoCache=!0;if(this.options.help&&this.showHelpOnExit)this.outputHelp(),m=!1,this.unsetMatchedCommand();if(this.options.version&&this.showVersionOnExit&&this.matchedCommandName==null)this.outputVersion(),m=!1,this.unsetMatchedCommand();let Y={args:this.args,options:this.options};if(m)await this.runMatchedCommand();if(!this.matchedCommand&&this.args[0]){if(this.emit("command:*"),!(this.listenerCount("command:*")>0))this.showCommandNotFound(this.args[0])}return Y}mri(D,t){let m=[...this.globalCommand.options,...t?t.options:[]],A=Bu(m),_=[],z=D.indexOf("--");if(z>-1)_=D.slice(z+1),D=D.slice(0,z);let U=qu(D,A),Y={_:U._};for(let he of Object.keys(U))if(he!=="_")Y[Yo(he)]=U[he];let X=Y._,ne={"--":_},re=t&&t.config.ignoreOptionDefaultValue?t.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,se=Object.create(null);for(let he of m){if(!re&&he.config.default!==void 0)for(let we of he.names)ne[we]=he.config.default;if(Array.isArray(he.config.type)){if(se[he.name]===void 0)se[he.name]={shouldTransform:!0,transformFunction:he.config.type[0]}}}for(let he of Object.keys(Y))if(he!=="_"){let we=he.split(".");_u(ne,we,Y[he]),Mu(ne,se)}return{args:X,options:ne}}async run(D=ql){return this.parse(D,{run:!0,exitOnError:!0})}handleUsageError(D){if(!Uu(D)||D.isUsageError===!1)return;let t=D.message||"command-line error",m=this.name?`${this.name}: `:"",A=/--help/.test(t)?"":D.usage?`
|
|
29
29
|
Usage: ${D.usage}`:`
|
|
30
30
|
Run \`${this.name??"cli"} --help\` for usage.`;Fi.stderr.write(`${m}${t}${A}
|
|
31
|
-
`),Fi.exit(D.exitCode??2)}async runMatchedCommand(){let{args:D,options:t,matchedCommand:m}=this;if(!m||!m.commandAction)return;m.checkUnknownOptions(),m.checkOptionValue(),m.checkRequiredArgs();let A=[];m.args.forEach((Y,X)=>{if(Y.variadic)A.push(D.slice(X));else A.push(D[X])}),A.push(t);let _={command:m,args:A,options:t};for(let Y of m.beforeHooks)await Y(_);let z,U=async()=>{let Y=m.commandAction.apply(this,A);if(Y instanceof Promise)z=await Y;else z=Y;return z};if(m.middleware.length>0){let Y=0,X=async()=>{if(Y<m.middleware.length){let ne=m.middleware[Y++];await ne({..._,next:X})}else await U()};await X()}else await U();for(let Y of m.afterHooks)await Y(_);return z}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return Fi.removeAllListeners("SIGINT"),Fi.removeAllListeners("SIGTERM"),this.signalHandlersSet=!1,this}destroy(){this.removeSignalHandlers(),this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.matchedCommand=void 0,this.matchedCommandName=void 0,this.removeAllListeners()}}class Yl{cache;enabled;cleanupInterval=null;hits=0;misses=0;constructor(){this.cache=new Map,this.enabled=!0,this.startCleanupInterval()}startCleanupInterval(){if(this.cleanupInterval)return;this.cleanupInterval=setInterval(()=>{this.cleanup()},30000),this.cleanupInterval.unref()}stopCleanup(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null}isEnabled(){return this.enabled}get(D){if(!this.enabled){this.misses++;return}let t=this.cache.get(D);if(!t){this.misses++;return}if(Date.now()-t.timestamp>t.ttl){this.cache.delete(D),this.misses++;return}return this.hits++,t.value}set(D,t,m=5000){if(!this.enabled)return;this.cache.set(D,{value:t,timestamp:Date.now(),ttl:m})}has(D){if(!this.enabled)return!1;let t=this.cache.get(D);if(!t)return!1;if(Date.now()-t.timestamp>t.ttl)return this.cache.delete(D),!1;return!0}delete(D){this.cache.delete(D)}clear(){this.cache.clear()}disable(){this.enabled=!1,this.clear()}enable(){this.enabled=!0}stats(){return{size:this.cache.size,enabled:this.enabled,hits:this.hits,misses:this.misses}}resetStats(){this.hits=0,this.misses=0}keys(){return Array.from(this.cache.keys())}cleanup(){let D=Date.now();for(let[t,m]of this.cache.entries())if(D-m.timestamp>m.ttl)this.cache.delete(t)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var Jh=new Yl;function Zu(){if("FORCE_COLOR"in Jo.env)return Jo.env.FORCE_COLOR!=="0";if("NO_COLOR"in Jo.env||Jo.env.TERM==="dumb")return!1;if(Jo.platform==="win32")return!0;return Gl.isatty(1)&&Gl.isatty(2)}var Zl=Zu();function rn(D,t){if(!Zl)return(m)=>m;return(m)=>D+m+t}var Qu=rn("\x1B[0m","\x1B[0m"),Xu=rn("\x1B[31m","\x1B[39m"),em=rn("\x1B[32m","\x1B[39m"),tm=rn("\x1B[33m","\x1B[39m"),nm=rn("\x1B[34m","\x1B[39m"),rm=rn("\x1B[35m","\x1B[39m"),im=rn("\x1B[36m","\x1B[39m"),om=rn("\x1B[37m","\x1B[39m"),sm=rn("\x1B[90m","\x1B[39m"),am=rn("\x1B[1m","\x1B[22m"),cm=rn("\x1B[3m","\x1B[23m"),lm=rn("\x1B[4m","\x1B[24m"),dm=rn("\x1B[2m","\x1B[22m"),um=rn("\x1B[7m","\x1B[27m"),mm=rn("\x1B[8m","\x1B[28m"),pm=rn("\x1B[9m","\x1B[29m"),fm=rn("\x1B[41m","\x1B[49m"),gm=rn("\x1B[42m","\x1B[49m"),hm=rn("\x1B[43m","\x1B[49m"),ym=rn("\x1B[44m","\x1B[49m"),vm=rn("\x1B[45m","\x1B[49m"),bm=rn("\x1B[46m","\x1B[49m"),wm=rn("\x1B[47m","\x1B[49m"),Sm=Zl,Cm={reset:Qu,red:Xu,green:em,yellow:tm,blue:nm,magenta:rm,cyan:im,white:om,gray:sm,bold:am,italic:cm,underline:lm,dim:dm,inverse:um,hidden:mm,strikethrough:pm,bgRed:fm,bgGreen:gm,bgYellow:hm,bgBlue:ym,bgMagenta:vm,bgCyan:bm,bgWhite:wm,isColorSupported:Sm},$m=Cm,Am=["up","down","left","right","space","enter","cancel"],ty={actions:new Set(Am),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};var ny=km.platform.startsWith("win"),ry=Symbol("clapp:cancel");var Rm=Wu();var tn=(D,t)=>Rm?D:t,iy=tn("◆","*"),oy=tn("■","x"),sy=tn("▲","x"),ay=tn("◇","o"),cy=tn("┌","T"),Im=tn("│","|"),ly=tn("└","—"),dy=tn("●",">"),uy=tn("○"," "),my=tn("◻","[•]"),py=tn("◼","[+]"),fy=tn("◻","[ ]"),gy=tn("▪","•"),hy=tn("─","-"),yy=tn("╮","+"),vy=tn("├","+"),by=tn("╯","+"),wy=tn("●","•"),Sy=tn("◆","*"),Cy=tn("▲","!"),$y=tn("■","x");var ky={light:tn("─","-"),heavy:tn("━","="),block:tn("█","#")};function Pm(){return`${$m.gray(Im)} `}var Ay=Pm();var Ma="0.16.
|
|
31
|
+
`),Fi.exit(D.exitCode??2)}async runMatchedCommand(){let{args:D,options:t,matchedCommand:m}=this;if(!m||!m.commandAction)return;m.checkUnknownOptions(),m.checkOptionValue(),m.checkRequiredArgs();let A=[];m.args.forEach((Y,X)=>{if(Y.variadic)A.push(D.slice(X));else A.push(D[X])}),A.push(t);let _={command:m,args:A,options:t};for(let Y of m.beforeHooks)await Y(_);let z,U=async()=>{let Y=m.commandAction.apply(this,A);if(Y instanceof Promise)z=await Y;else z=Y;return z};if(m.middleware.length>0){let Y=0,X=async()=>{if(Y<m.middleware.length){let ne=m.middleware[Y++];await ne({..._,next:X})}else await U()};await X()}else await U();for(let Y of m.afterHooks)await Y(_);return z}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return Fi.removeAllListeners("SIGINT"),Fi.removeAllListeners("SIGTERM"),this.signalHandlersSet=!1,this}destroy(){this.removeSignalHandlers(),this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.matchedCommand=void 0,this.matchedCommandName=void 0,this.removeAllListeners()}}class Yl{cache;enabled;cleanupInterval=null;hits=0;misses=0;constructor(){this.cache=new Map,this.enabled=!0,this.startCleanupInterval()}startCleanupInterval(){if(this.cleanupInterval)return;this.cleanupInterval=setInterval(()=>{this.cleanup()},30000),this.cleanupInterval.unref()}stopCleanup(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null}isEnabled(){return this.enabled}get(D){if(!this.enabled){this.misses++;return}let t=this.cache.get(D);if(!t){this.misses++;return}if(Date.now()-t.timestamp>t.ttl){this.cache.delete(D),this.misses++;return}return this.hits++,t.value}set(D,t,m=5000){if(!this.enabled)return;this.cache.set(D,{value:t,timestamp:Date.now(),ttl:m})}has(D){if(!this.enabled)return!1;let t=this.cache.get(D);if(!t)return!1;if(Date.now()-t.timestamp>t.ttl)return this.cache.delete(D),!1;return!0}delete(D){this.cache.delete(D)}clear(){this.cache.clear()}disable(){this.enabled=!1,this.clear()}enable(){this.enabled=!0}stats(){return{size:this.cache.size,enabled:this.enabled,hits:this.hits,misses:this.misses}}resetStats(){this.hits=0,this.misses=0}keys(){return Array.from(this.cache.keys())}cleanup(){let D=Date.now();for(let[t,m]of this.cache.entries())if(D-m.timestamp>m.ttl)this.cache.delete(t)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var Jh=new Yl;function Zu(){if("FORCE_COLOR"in Jo.env)return Jo.env.FORCE_COLOR!=="0";if("NO_COLOR"in Jo.env||Jo.env.TERM==="dumb")return!1;if(Jo.platform==="win32")return!0;return Gl.isatty(1)&&Gl.isatty(2)}var Zl=Zu();function rn(D,t){if(!Zl)return(m)=>m;return(m)=>D+m+t}var Qu=rn("\x1B[0m","\x1B[0m"),Xu=rn("\x1B[31m","\x1B[39m"),em=rn("\x1B[32m","\x1B[39m"),tm=rn("\x1B[33m","\x1B[39m"),nm=rn("\x1B[34m","\x1B[39m"),rm=rn("\x1B[35m","\x1B[39m"),im=rn("\x1B[36m","\x1B[39m"),om=rn("\x1B[37m","\x1B[39m"),sm=rn("\x1B[90m","\x1B[39m"),am=rn("\x1B[1m","\x1B[22m"),cm=rn("\x1B[3m","\x1B[23m"),lm=rn("\x1B[4m","\x1B[24m"),dm=rn("\x1B[2m","\x1B[22m"),um=rn("\x1B[7m","\x1B[27m"),mm=rn("\x1B[8m","\x1B[28m"),pm=rn("\x1B[9m","\x1B[29m"),fm=rn("\x1B[41m","\x1B[49m"),gm=rn("\x1B[42m","\x1B[49m"),hm=rn("\x1B[43m","\x1B[49m"),ym=rn("\x1B[44m","\x1B[49m"),vm=rn("\x1B[45m","\x1B[49m"),bm=rn("\x1B[46m","\x1B[49m"),wm=rn("\x1B[47m","\x1B[49m"),Sm=Zl,Cm={reset:Qu,red:Xu,green:em,yellow:tm,blue:nm,magenta:rm,cyan:im,white:om,gray:sm,bold:am,italic:cm,underline:lm,dim:dm,inverse:um,hidden:mm,strikethrough:pm,bgRed:fm,bgGreen:gm,bgYellow:hm,bgBlue:ym,bgMagenta:vm,bgCyan:bm,bgWhite:wm,isColorSupported:Sm},$m=Cm,Am=["up","down","left","right","space","enter","cancel"],ty={actions:new Set(Am),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};var ny=km.platform.startsWith("win"),ry=Symbol("clapp:cancel");var Rm=Wu();var tn=(D,t)=>Rm?D:t,iy=tn("◆","*"),oy=tn("■","x"),sy=tn("▲","x"),ay=tn("◇","o"),cy=tn("┌","T"),Im=tn("│","|"),ly=tn("└","—"),dy=tn("●",">"),uy=tn("○"," "),my=tn("◻","[•]"),py=tn("◼","[+]"),fy=tn("◻","[ ]"),gy=tn("▪","•"),hy=tn("─","-"),yy=tn("╮","+"),vy=tn("├","+"),by=tn("╯","+"),wy=tn("●","•"),Sy=tn("◆","*"),Cy=tn("▲","!"),$y=tn("■","x");var ky={light:tn("─","-"),heavy:tn("━","="),block:tn("█","#")};function Pm(){return`${$m.gray(Im)} `}var Ay=Pm();var Ma="0.16.48";function Ql(D,t,m=process.env){let A=t?.options.find((U)=>U.name==="profile");if((!A||/\baws\b/i.test(A.description))&&typeof D.profile==="string"&&D.profile)m.AWS_PROFILE=D.profile,delete m.AWS_ACCESS_KEY_ID,delete m.AWS_SECRET_ACCESS_KEY,delete m.AWS_SESSION_TOKEN;if(!t?.options.some((U)=>U.name==="region")&&typeof D.region==="string"&&D.region)m.AWS_REGION=D.region,m.AWS_DEFAULT_REGION=D.region}import{existsSync as za}from"node:fs";import{mkdir as xm,writeFile as Xl}from"node:fs/promises";function Ua(D){D.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(t)=>{if(i("Initializing ts-cloud Project"),za("cloud.config.ts")){if(!await u("cloud.config.ts already exists. Overwrite?",!1)){e("Initialization cancelled");return}}let m=t?.name||await R("Project name","my-app"),A=t?.mode||await Do("Select deployment mode",["serverless","server"]),_=t?.region||await Do("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),z=new s("Creating configuration file...");z.start();let U=`import { defineConfig } from '@ts-cloud/core'
|
|
32
32
|
|
|
33
33
|
export default defineConfig({
|
|
34
34
|
project: {
|
|
@@ -315,7 +315,7 @@ Resources to create: ${Ee}`);let Ke={};for(let je of Object.values(ne.Resources|
|
|
|
315
315
|
No changes detected - infrastructure is up to date`);return}if(e(`
|
|
316
316
|
Changes detected:
|
|
317
317
|
`),Ie.length>0){n(`Resources to add (${Ie.length}):`);for(let De of Ie){let Ee=we[De].Type;e(` + ${De} (${Ee})`)}console.log()}if(Le.length>0){r(`Resources to remove (${Le.length}):`);for(let De of Le){let Ee=he[De].Type;e(` - ${De} (${Ee})`)}console.log()}if(Re.length>0){a(`Resources to modify (${Re.length}):`);for(let De of Re){let Ee=we[De].Type;e(` ~ ${De} (${Ee})`)}console.log()}e("Summary:"),e(` - Add: ${Ie.length}`),e(` - Remove: ${Le.length}`),e(` - Modify: ${Re.length}`),e("\nRun `cloud deploy` to apply these changes")}catch(m){r(`Diff failed: ${m.message}`)}})}function Bi(D,t=0){let m=Number(D);return Number.isFinite(m)?m:t}function Ja(D){return Bi(D)*1024}function cp(D){let t=D?.date?.trim(),m=D?.time?.trim();if(!t||!m)return null;let A=new Date(`${t}T${m}Z`);return Number.isFinite(A.getTime())?A.toISOString():null}function lp(D,t,m){let A;try{A=JSON.parse(D)}catch{return[]}let _=(A?.sysstat?.hosts??[]).flatMap((U)=>U.statistics??[]),z=[];for(let U of _){let Y=cp(U.timestamp);if(!Y)continue;let X=new Date(Y).getTime();if(X<t.getTime()||X>m.getTime())continue;let ne=U["cpu-load"]?.find((se)=>se.cpu==="all")??U["cpu-load"]?.[0],re=Bi(ne?.idle,100);z.push({timestamp:Y,cpuUsedPercent:Math.max(0,Math.min(100,100-re)),cpuUserPercent:Bi(ne?.user),cpuSystemPercent:Bi(ne?.system),cpuIowaitPercent:Bi(ne?.iowait),memoryUsedPercent:Bi(U.memory?.["memused-percent"]),memoryUsedBytes:Ja(U.memory?.memused),memoryAvailableBytes:Ja(U.memory?.avail),swapUsedPercent:Bi(U.memory?.["swpused-percent"]),swapUsedBytes:Ja(U.memory?.swpused)})}return z.sort((U,Y)=>U.timestamp.localeCompare(Y.timestamp))}function dp(D){return Object.fromEntries(Object.entries(D.time_series??{}).map(([t,m])=>[t,(m.values??[]).map(([A,_])=>({timestamp:new Date(Number(A)*1000).toISOString(),value:Bi(_,Number.NaN)})).filter((A)=>Number.isFinite(A.value))]))}function pd(D){return D.length?D.reduce((t,m)=>t+m,0)/D.length:null}function Ya(D){return D.length?Math.max(...D):null}function up(D){return D.length?Math.min(...D):null}async function fd(D){let[t,m]=await Promise.all([D.client.getServerMetrics(D.serverId,{types:["cpu","disk","network"],start:D.from,end:D.to,step:D.step}),Ne(D.remote.host,"LC_ALL=C sadf -j -- -u -r -S",D.remote).catch(()=>"")]),A=lp(m,D.from,D.to),_=dp(t),U=(_.cpu??[]).map((se)=>Math.max(0,Math.min(100,se.value/Math.max(1,D.cores)))),Y=A.map((se)=>se.cpuUsedPercent),X=U.length?U:Y,ne=A.map((se)=>se.memoryUsedPercent),re=A.map((se)=>se.swapUsedPercent);return{from:D.from.toISOString(),to:D.to.toISOString(),cores:D.cores,host:A,provider:_,summary:{cpuAveragePercent:pd(X),cpuPeakPercent:Ya(X),memoryAveragePercent:pd(ne),memoryPeakPercent:Ya(ne),memoryMinimumAvailableBytes:up(A.map((se)=>se.memoryAvailableBytes)),swapPeakPercent:Ya(re)}}}function mp(D){if(D.length===0)throw Error("A server name cannot be empty.");if(D.length>253)throw Error(`'${D}' is longer than the 253 characters a hostname allows.`);for(let t of D.split(".")){if(t.length===0)throw Error(`'${D}' has an empty label — a hostname cannot contain '..'.`);if(t.length>63)throw Error(`'${t}' is longer than the 63 characters a hostname label allows.`);if(!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(t))throw Error(`'${t}' is not a valid hostname label. Use letters, digits and hyphens, not starting or ending with a hyphen.`)}}async function gd(D,t,m){if(mp(t),D===t)throw Error(`'${D}' is already named that.`);if((await m.takenNames()).some((z)=>z===t&&z!==D))throw Error(`'${t}' is already taken. Server names have to be unique within a provider project.`);let _=[];if(m.providerName&&m.renameProvider){let{providerName:z,renameProvider:U}=m;_.push({id:"provider",title:"Rename the server at the provider",change:{from:D,to:t},satisfied:async()=>await z()===t,apply:()=>U(t)})}if(m.stateName&&m.writeStateName){let{stateName:z,writeStateName:U}=m;_.push({id:"state-pin",title:"Update the recorded name in the local driver state",change:{from:D,to:t},satisfied:async()=>{let Y=await z();return Y===void 0||Y===t},apply:()=>U(t)})}if(m.remoteHostname&&m.setRemoteHostname){let{remoteHostname:z,setRemoteHostname:U}=m;_.push({id:"hostname",title:"Set the hostname on the box",change:{from:D,to:t},satisfied:async()=>await z()===t,apply:()=>U(t)})}return _.push({id:"inventory",title:"Rename the fleet inventory record",change:{from:D,to:t},satisfied:async()=>m.inventoryName()===t,apply:async()=>{await m.renameInventory(t)}}),{operation:"server:rename",target:D,steps:_}}function hd(D){let t=`'${D.replace(/'/g,`'"'"'`)}'`;return["set -eu",'TS_CLOUD_OLD="$(hostname)"',`hostnamectl set-hostname ${t} 2>/dev/null || { echo ${t} > /etc/hostname && hostname ${t}; }`,'if grep -q "^127.0.1.1" /etc/hosts; then',` TS_CLOUD_HOSTS="$(sed -E "s/^127\\.0\\.1\\.1.*/127.0.1.1\\t${D}/" /etc/hosts)"`,` printf '%s\\n' "$TS_CLOUD_HOSTS" > /etc/hosts`,"else",` printf '127.0.1.1\\t%s\\n' ${t} >> /etc/hosts`,"fi",'printf "%s -> %s\\n" "$TS_CLOUD_OLD" "$(hostname)"'].join(`
|
|
318
|
-
`)}async function pp(){let D=await Fe(),t=y(process.cwd(),D),m=new ke(t.store),A=new Nn(m,[new it("aws"),new it("hetzner"),new it("ssh")]);return{controlPlane:t,store:m,service:A}}async function bi(D){let t=await pp();try{return await D(t)}finally{t.controlPlane.store.close()}}var Mi=(D,t)=>{let m=D.store.list(D.controlPlane.project.id).find((A)=>A.id===t||A.name===t);if(!m)throw Error(`Server ${t} was not found.`);return m},yn=(D)=>{r(D instanceof Error?D.message:String(D)),process.exitCode=1};function Za(D,t){let m=Number.parseInt(D??"",10);return Number.isFinite(m)&&m>=0?m:t}function fp(D="3h"){let t=D.match(/^(\d+)(m|h)$/);if(!t)throw Error("--range must use minutes or hours, for example 30m or 3h.");let m=Number(t[1])*(t[2]==="h"?3600000:60000);if(m<300000||m>86400000)throw Error("--range must be between 5 minutes and 24 hours.");return m}function ps(D,t="%"){return D==null?"unavailable":`${D.toFixed(1)}${t}`}async function gp(D,t){let m=await Fe();if(m.cloud?.provider!=="hetzner")throw Error("server:monitoring currently requires the Hetzner provider.");let A=t.env??process.env.CLOUD_ENV??"production",_=T(m,A),z=await pe(_);if(!z?.serverId)throw Error(`No pinned Hetzner server was found for ${_}.`);if(![z.serverName,String(z.serverId),z.stackName].includes(D))throw Error(`Refusing to read ${D}: project state is pinned to ${z.serverName}.`);let U=lr(m),Y=new We({apiToken:dr(U.apiToken,m)}),X=await Y.getServer(z.serverId),ne=z.publicIp??X.public_net.ipv4?.ip;if(!ne)throw Error(`Server ${X.name} has no public IPv4 address.`);let re=new Date,se=new Date(re.getTime()-fp(t.range)),he=Math.min(3600,Math.max(60,Za(t.step,60))),we=await fd({client:Y,serverId:X.id,cores:Math.max(1,Number(X.server_type.cores)||1),from:se,to:re,step:he,remote:{host:ne,user:z.sshUser??U.sshUser,identityFile:U.sshPrivateKeyPath,connectTimeoutSec:15}});if(t.json){console.log(JSON.stringify({schemaVersion:1,server:X.name,result:we},null,2));return}c(["Server","Window","CPU average / peak","RAM average / peak","Minimum available","Swap peak"],[[X.name,`${we.from} to ${we.to}`,`${ps(we.summary.cpuAveragePercent)} / ${ps(we.summary.cpuPeakPercent)}`,`${ps(we.summary.memoryAveragePercent)} / ${ps(we.summary.memoryPeakPercent)}`,we.summary.memoryMinimumAvailableBytes==null?"unavailable":`${(we.summary.memoryMinimumAvailableBytes/1073741824).toFixed(2)} GB`,ps(we.summary.swapPeakPercent)]])}function yd(D){let t=D.targetType.prices?.find((m)=>m.location===D.location);return{serverId:D.serverId,serverName:D.serverName,location:D.location,from:D.currentType.name,to:D.targetType.name,capacityAvailable:D.capacityAvailable,alreadyComplete:D.alreadyComplete,upgradeDisk:D.upgradeDisk,target:{cores:D.targetType.cores,memoryGb:D.targetType.memory,diskGb:D.targetType.disk,architecture:D.targetType.architecture,cpuType:D.targetType.cpu_type},price:t?{hourlyNet:t.price_hourly.net,hourlyGross:t.price_hourly.gross,monthlyNet:t.price_monthly.net,monthlyGross:t.price_monthly.gross}:void 0}}function hp(D){let t=yd(D);c(["Server","Location","Resize","Capacity","CPU / RAM / disk","Monthly"],[[`${t.serverName} (${t.serverId})`,t.location,`${t.from} -> ${t.to}`,t.capacityAvailable?"available":"waiting",`${t.target.cores??0} / ${t.target.memoryGb??0} GB / ${t.target.diskGb??0} GB`,t.price?.monthlyGross??t.price?.monthlyNet??"not reported"]])}async function yp(D,t,m){let A=await Fe();if(A.cloud?.provider!=="hetzner")throw Error("server:resize currently requires the Hetzner provider.");let _=m.env??process.env.CLOUD_ENV??"production",z=T(A,_),U=await pe(z),Y=lr(A),X=new We({apiToken:dr(Y.apiToken,A)}),re=U&&(D===U.serverName||D===String(U.serverId)||D===U.stackName)?await X.getServer(U.serverId):(await X.listServers()).find((je)=>je.name===D||String(je.id)===D);if(!re)throw Error(`Hetzner server ${D} was not found.`);if(U?.serverId&&U.serverId!==re.id)throw Error(`Refusing to resize ${D}: project state is pinned to server ${U.serverId}.`);let se=dt(t),he=m.upgradeDisk!==!1,we=await ci(X,re.id,se,he);if(m.json)console.log(JSON.stringify({schemaVersion:1,plan:yd(we)},null,2));else hp(we);if(!m.apply){if(!m.json)e(`Preview only. Re-run with --apply --confirm ${re.name} to execute.`);return}if(m.confirm!==re.name)throw Error(`Resize requires --confirm ${re.name}.`);let Pe=U?.publicIp??re.public_net.ipv4?.ip;if(!Pe)throw Error(`Server ${re.name} has no public IPv4 address for verification.`);let ye={host:Pe,user:U?.sshUser??Y.sshUser,identityFile:Y.sshPrivateKeyPath,connectTimeoutSec:15,sshTimeoutMs:600000,routeTimeoutMs:20000},Ie=await ls(z),Le=new Date().toISOString(),Re={schemaVersion:1,operationId:Ie?.targetType===se&&Ie.serverId===re.id?Ie.operationId:crypto.randomUUID(),stackName:z,serverId:re.id,serverName:re.name,sourceType:we.currentType.name,targetType:se,upgradeDisk:he,phase:"planning",status:"running",attempts:Ie?.targetType===se?Ie.attempts:0,startedAt:Ie?.targetType===se?Ie.startedAt:Le,updatedAt:Le,manifest:Ie?.targetType===se?Ie.manifest:void 0};await pt(Re);let De=Za(m.pollSeconds,900)*1000,Ee=Za(m.maxWaitMinutes,0)*60000,Ke=Date.now();for(;;){let je=await ci(X,re.id,se,he);if(!je.capacityAvailable&&!je.alreadyComplete){if(Re={...Re,phase:"waiting-capacity",status:"waiting-capacity",updatedAt:new Date().toISOString()},await pt(Re),!m.wait){if(!m.json)e(`${se} has no capacity in ${je.location}. The server remains running and unchanged. Use --wait to poll safely.`);return}if(Ee>0&&Date.now()-Ke>=Ee)throw Error(`Timed out waiting for ${se} capacity in ${je.location}.`);if(!m.json)e(`Waiting ${Math.round(De/1000)} seconds for ${se} capacity...`);await Bun.sleep(De);continue}let _e=await ds(z);try{let et=await(async()=>{try{return Re={...Re,status:"running",attempts:Re.attempts+1,updatedAt:new Date().toISOString()},await pt(Re),await ns({client:X,serverId:re.id,targetType:se,upgradeDisk:he,resumeFromOff:Re.phase==="shutting-down"||Re.phase==="resizing"||Re.phase==="powering-on"||Re.phase==="verifying",manifest:Re.manifest,hooks:{preflight:async()=>{let Rt=await is(ye);return Re={...Re,manifest:Rt,updatedAt:new Date().toISOString()},await pt(Re),Rt},afterBoot:async(Rt,Et,wt,Pt)=>os(ye,Et,wt,Pt),onPhase:async(Rt,Et)=>{if(Re={...Re,phase:Rt,status:Rt==="waiting-capacity"?"waiting-capacity":Rt==="complete"?"completed":"running",updatedAt:new Date().toISOString()},await pt(Re),!m.json)e(`${Rt}${Et?` ${JSON.stringify(Et)}`:""}`)}}})}finally{await _e()}})();if(Re={...Re,status:et.status==="completed"||et.status==="already-complete"?"completed":et.status==="recovered"?"recovered":"waiting-capacity",lastError:et.status==="recovered"?et.error:void 0,updatedAt:new Date().toISOString()},await pt(Re),m.json)console.log(JSON.stringify({schemaVersion:1,result:et},null,2));else if(et.status==="completed"||et.status==="already-complete")n(`${re.name} is on ${se} and passed full post-boot verification.`);else if(et.status==="recovered"&&et.retryable&&m.wait){e(`Capacity race recovered safely. Waiting ${Math.round(De/1000)} seconds before retry.`),await Bun.sleep(De);continue}else if(et.status==="recovered")throw Error(`Resize did not complete and the original server was recovered: ${et.error}`);return}catch(et){throw Re={...Re,status:"failed",lastError:et instanceof Error?et.message:String(et),updatedAt:new Date().toISOString()},await pt(Re),et}}}async function vp(D,t){let m=await Fe();if(m.cloud?.provider!=="hetzner")throw Error("server:optimize currently requires the Hetzner provider.");let A=t.env??process.env.CLOUD_ENV??"production",_=T(m,A),z=await pe(_);if(!z?.serverId)throw Error(`No pinned Hetzner server was found for ${_}.`);if(![z.serverName,String(z.serverId),z.stackName].includes(D))throw Error(`Refusing to optimize ${D}: project state is pinned to ${z.serverName}.`);let U=lr(m),Y=new We({apiToken:dr(U.apiToken,m)}),X=await Y.getServer(z.serverId),ne=z.publicIp??X.public_net.ipv4?.ip;if(!ne)throw Error(`Server ${X.name} has no public IPv4 address.`);let re=to(m);if(t.json)console.log(JSON.stringify({schemaVersion:1,server:X.name,plan:re},null,2));else c(["Server","Firewall ports","Monitoring","Auto updates","Swap","SSH passwords"],[[X.name,re.firewallPorts.join(","),re.monitoring?"on":"off",re.autoUpdates?"on":"off",`${re.swapGb} GB`,"disabled"]]);if(!t.apply){if(!t.json)e(`Preview only. Re-run with --apply --confirm ${X.name} to execute.`);return}if(t.confirm!==X.name)throw Error(`Optimization requires --confirm ${X.name}.`);let se={host:ne,user:z.sshUser??U.sshUser,identityFile:U.sshPrivateKeyPath,connectTimeoutSec:15},he=await en(se),we=he.routeProbes.filter((wt)=>!wt.ok),Pe=m.infrastructure?.compute,ye=Z(Pe)?new Set(["nginx.service","bun-gateway.service","ts-cloud-nginx.service"]):new Set,Ie=he.failedUnits.filter((wt)=>!ye.has(wt));if(Ie.length>0)throw Error(`Preflight found failed services: ${Ie.join(", ")}`);if(we.length>0)throw Error(`Preflight found unhealthy routes: ${we.map((wt)=>wt.domain).join(", ")}`);let Le=Z(Pe)?[]:Object.values(m.sites??{}).flatMap((wt)=>typeof wt?.port==="number"&&![80,443].includes(wt.port)?[wt.port]:[]),Re=Yr({allowSsh:Pe?.allowSsh!==!1,sitePorts:Le,allowedPorts:Pe?.firewall?.allowedPorts}),De=(z.firewallId?(await Y.listFirewalls()).find((wt)=>wt.id===z.firewallId):void 0)??(await Y.listFirewalls()).find((wt)=>wt.name===`${m.project.slug}-${A}-app-fw`);if(!De)throw Error(`Managed firewall for ${X.name} was not found.`);let Ee=await Y.setFirewallRules(De.id,Re);await Promise.all(Ee.map((wt)=>Y.waitForAction(wt.id))),await ss(m,se);let[Ke,je]=await Promise.all([en(se),as(se)]),_e=cs(re,je),et=pc(he,Ke),Rt=Ke.routeProbes.filter((wt)=>!wt.ok);if(et.stoppedServices.length>0)_e.push(`services stopped: ${et.stoppedServices.join(", ")}`);if(et.changedRouteFragments.length>0)_e.push(`route fragments changed: ${et.changedRouteFragments.join(", ")}`);if(et.missingRouteIds.length>0)_e.push(`routes disappeared: ${et.missingRouteIds.join(", ")}`);if(et.changedReleaseLinks.length>0)_e.push(`release links changed: ${et.changedReleaseLinks.join(", ")}`);if(et.missingData.length>0)_e.push(`databases or volumes disappeared: ${et.missingData.join(", ")}`);if(Rt.length>0)_e.push(`unhealthy routes: ${Rt.map((wt)=>wt.domain).join(", ")}`);if(_e.length>0)throw Error(`Host optimization verification failed: ${_e.join("; ")}`);let Et={serverId:X.id,serverName:X.name,services:Ke.runningServices.length,routes:Ke.routeIds.length,healthyDomains:Ke.routeProbes.length,releaseLinks:Ke.releaseLinks.length,databasesAndVolumes:Ke.dataCatalog.length,report:je};if(t.json)console.log(JSON.stringify({schemaVersion:1,result:Et},null,2));else n(`${X.name} passed full host, rpx route, service, release, and data verification.`)}async function bp(D,t,m){let A=await Fe(),_=m.env??"production",z=T(A,_);await bi(async(U)=>{let Y=Mi(U,D),X=U.store.list(U.controlPlane.project.id,!0),ne=Y.provider==="hetzner"&&Y.providerId?(()=>{try{let De=lr(A);return new We({apiToken:dr(De.apiToken,A)})}catch{return null}})():null,re=Y.providerId?Number(Y.providerId):Number.NaN,se=await pe(z),he=se!=null&&se.serverId===re,we=new Br,Pe=Y.trustState==="pinned",ye={takenNames:async()=>{let De=X.map((Ee)=>Ee.name);if(ne)De.push(...(await ne.listServers()).map((Ee)=>Ee.name));return De},inventoryName:()=>U.store.get(Y.id)?.name??Y.name,renameInventory:(De)=>{U.store.update(Y.id,{name:De})},...ne&&Number.isFinite(re)?{providerName:async()=>(await ne.getServer(re)).name,renameProvider:async(De)=>{await ne.renameServer(re,De)}}:{},...he?{stateName:async()=>(await pe(z))?.serverName,writeStateName:async(De)=>{let Ee=await pe(z);if(Ee)await ut(z,{...Ee,serverName:De})}}:{},...Pe?{remoteHostname:async()=>{let De=await we.exec(Y,"hostname");return De.code===0?De.stdout.trim():void 0},setRemoteHostname:async(De)=>{let Ee=await we.exec(Y,hd(De));if(Ee.code!==0)throw Error(Ee.stderr.trim()||`hostname exited ${Ee.code}`)}}:{}},Ie=await gd(Y.name,t,ye),Le=await Ws(Ie);if(!m.apply){let De=[ne?"":"provider record (no provider id or no API token)",he?"":"local state pin (none points at this server)",Pe?"":"box hostname (host key is not pinned — run server:validate first)"].filter(Boolean);if(m.json){console.log(JSON.stringify({schemaVersion:1,operation:Ie.operation,target:Ie.target,rename:{from:Y.name,to:t},notCovered:De,steps:Le.map((Ee)=>({id:Ee.step.id,title:Ee.step.title,state:Ee.state,change:Ee.step.change,reason:Ee.reason}))},null,2));return}for(let Ee of Vs(Ie,Le))console.log(Ee);for(let Ee of De)console.log(` not covered: ${Ee}`);console.log(" Re-run with --apply to perform it.");return}let Re=await Gs(Ie,Le,{log:(De)=>e(De),audit:(De)=>U.controlPlane.store.appendEvent({projectId:U.controlPlane.project.id,resourceId:Y.resourceId,type:`${De.operation}.${De.step}.${De.state}`,level:De.state==="failed"?"error":"info",payload:{target:De.target,renameTo:t,...De.error?{error:De.error}:{}}})});if(m.json)console.log(JSON.stringify({schemaVersion:1,outcome:Re},null,2));if(!Re.success){let De=Re.steps.find((Ee)=>Ee.state==="failed");throw Error(`${Ie.operation} stopped at '${De?.title}': ${De?.error}. Earlier steps stayed applied; re-run the same command to continue from here.`)}if(!m.json)n(`Renamed ${Y.name} to ${t}.`)})}function Qa(D){D.command("capabilities [server]","Show provider and target operation support").option("--json","Print structured JSON").action(async(t,m)=>{try{await bi(async(A)=>{let _=t?[Mi(A,t)]:A.store.list(A.controlPlane.project.id),z=_.flatMap((U)=>Object.entries(U.capabilities).map(([Y,X])=>({server:U.name,provider:U.provider,action:Y,...X})));if(m.json)console.log(JSON.stringify({schemaVersion:1,targets:z},null,2));else c(["Server","Provider","Action","Support","Explanation"],z.map((U)=>[U.server,U.provider,U.action,U.supported?"supported":"unsupported",U.reason??"—"]))})}catch(A){yn(A)}}),D.command("server:resize <name> <type>","Safely resize Hetzner compute with full host verification").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Execute the reviewed resize plan").option("--confirm <name>","Exact server name required for mutation").option("--upgrade-disk","Permanently expand the root disk to the target size",{default:!0}).option("--wait","Poll capacity without taking the server offline until placement is available").option("--poll-seconds <seconds>","Capacity retry interval",{default:"900"}).option("--max-wait-minutes <minutes>","Stop waiting after this many minutes; 0 waits indefinitely",{default:"0"}).option("--json","Print structured JSON").action(async(t,m,A)=>{try{await yp(t,m,A)}catch(_){yn(_)}}),D.command("server:optimize <name>","Reconcile and verify a production Hetzner host").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Apply the reviewed host optimization plan").option("--confirm <name>","Exact server name required for mutation").option("--json","Print structured JSON").action(async(t,m)=>{try{await vp(t,m)}catch(A){yn(A)}}),D.command("server:monitoring <name>","Read historical Hetzner CPU, RAM, swap, disk, and network metrics").option("--env <environment>","Deployment environment",{default:"production"}).option("--range <duration>","Historical window from 5m to 24h",{default:"3h"}).option("--step <seconds>","Hetzner provider sample interval",{default:"60"}).option("--json","Print structured JSON").action(async(t,m)=>{try{await gp(t,m)}catch(A){yn(A)}}),D.command("server:list","List fleet servers").option("--json","Print structured JSON").action(async(t)=>{try{await bi(async(m)=>{let A=m.store.list(m.controlPlane.project.id);if(t.json)console.log(JSON.stringify(A,null,2));else c(["Name","Provider ID","Provider / region","Status / trust","Roles","CPU / memory","Heartbeat"],A.map((_)=>[_.name,_.providerId??"—",`${_.provider} / ${_.region??"external"}`,`${_.status} / ${_.trustState}`,_.roles.join(","),`${_.capacity.cpu??0} / ${_.capacity.memoryBytes??0}`,_.heartbeatAt??"never"]))})}catch(m){yn(m)}}),D.command("server:create <name>","Enroll a provisioned or existing server").option("--provider <provider>","aws, hetzner, or ssh",{default:"ssh"}).option("--provider-id <id>","Stable provider identity").option("--endpoint <host>","SSH hostname or IP").option("--user <user>","Non-root SSH user",{default:"deploy"}).option("--credential-ref <ref>","Secret reference",{default:"secret://fleet/agent"}).option("--region <region>","Provider region").option("--roles <roles>","Comma-separated roles",{default:"application"}).option("--labels <labels>","Comma-separated key=value labels").action(async(t,m)=>{try{await bi(async(A)=>{let _=m.provider;if(!["aws","hetzner","ssh"].includes(_)||!m.endpoint)throw Error("A supported --provider and --endpoint are required; provisioning and enrollment are separate operations.");let z=A.service.enroll({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,provider:_,providerId:m.providerId,endpoint:m.endpoint,sshUser:m.user??"deploy",credentialRef:m.credentialRef??"secret://fleet/agent",region:m.region,roles:String(m.roles??"application").split(","),labels:Object.fromEntries(String(m.labels??"").split(",").map((U)=>U.split("=")).filter((U)=>U[0]&&U[1]))});n(`Enrolled ${z.name} as ${z.id}; no remote mutation was performed.`)})}catch(A){yn(A)}}),D.command("server:validate <name>","Pin trust and validate a server").option("--accept-host-key <fingerprint>","Accept reviewed rotation").option("--json","Print JSON").action(async(t,m)=>{try{await bi(async(A)=>{let _=Mi(A,t);if(_=await A.service.test(_.id),_.trustState==="rotation_pending"){if(!m.acceptHostKey)throw Error(`Host key changed to ${_.pendingHostKey}; review it first.`);_=A.service.reviewHostKey(_.id,m.acceptHostKey)}let z=await A.service.validate(_.id);if(m.json)console.log(JSON.stringify(z.validation,null,2));else c(["Severity","Code","Finding","Remediation"],(z.validation?.findings??[]).map((U)=>[U.severity,U.code,U.message,U.remediation??"—"]));if(!z.validation?.valid)process.exitCode=1})}catch(A){yn(A)}}),D.command("server:bootstrap <name>","Preview or queue bootstrap").option("--apply","Apply reviewed plan").action(async(t,m)=>{try{await bi(async(A)=>{let _=A.service.bootstrap(Mi(A,t).id,!!m.apply);if(_.preview)c(["Step"],_.steps.map((z)=>[z]));else n(`Bootstrap queued: ${_.operation?.id}`)})}catch(A){yn(A)}}),D.command("server:rename <name> <new-name>","Rename a server in place, without recreating it").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Perform the reviewed rename plan").option("--json","Print structured JSON").action(async(t,m,A)=>{try{await bp(t,m,A)}catch(_){yn(_)}}),D.command("server:drain <name>","Drain without terminating").option("--complete","Mark movement complete").action(async(t,m)=>{try{await bi(async(A)=>n(A.service.drain(Mi(A,t).id,!!m.complete).status))}catch(A){yn(A)}}),D.command("server:uncordon <name>","Return a server to scheduling").action(async(t)=>{try{await bi(async(m)=>{m.service.uncordon(Mi(m,t).id),n(`Uncordoned ${t}.`)})}catch(m){yn(m)}}),D.command("server:archive <name>","Archive inventory without termination").option("--confirm <name>","Exact name").action(async(t,m)=>{try{await bi(async(A)=>{A.service.archive(Mi(A,t).id,m.confirm??""),n(`Archived ${t}; provider infrastructure was not terminated.`)})}catch(A){yn(A)}}),D.command("server:ssh <name>","Open strict SSH using the enrolled endpoint").action(async(t)=>{try{await bi(async(m)=>{let A=Mi(m,t);if(A.trustState!=="pinned")throw Error("Validate and pin the server host key first.");let _=Bun.spawn(["ssh","-p",String(A.sshPort),"-o","StrictHostKeyChecking=yes",`${A.sshUser}@${A.endpoint}`],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}),z=await _.exited;if(z)process.exitCode=z})}catch(m){yn(m)}});for(let[t,m]of[["server:logs <name>","Use the runtime log service"],["server:deploy <name>","Use immutable release deployment"],["server:reboot <name>","Reboot provider compute"],["server:destroy <name>","Terminate provider compute"],["server:recipe <name> <recipe>","Apply a server recipe"],["server:firewall:add <name> <rule>","Add firewall rule"],["server:firewall:list <name>","List firewall rules"],["server:firewall:remove <name> <rule>","Remove firewall rule"],["server:ssl:install <domain>","Install TLS"],["server:ssl:renew <domain>","Renew TLS"],["server:snapshot <name>","Create provider snapshot"],["server:snapshot:restore <name> <snapshot-id>","Restore provider snapshot"],["server:update <name>","Update OS packages"],["server:secure <name>","Apply OS hardening"]])D.command(t,m).action(async(...A)=>En(t.split(" ")[0],{target:String(A[0]??""),nextAction:"Use the dashboard capability view or the corresponding runtime, release, firewall, backup, or maintenance service."}))}import{existsSync as vd,mkdirSync as wp,readFileSync as Sp,writeFileSync as Cp}from"node:fs";import{join as bd,resolve as $p}from"node:path";var Xa=(D)=>{r(D instanceof Error?D.message:String(D)),process.exitCode=1};async function kp(){try{return await Fe()}catch{return}}function Ap(D){if(D===void 0)return;let t=Number(D);if(!Number.isInteger(t)||t<1||t>65535)throw Error(`--port must be a port number, not '${D}'.`);return t}function wd(D,t,m){let A=D?.ssh?.hosts?.find((_)=>_.host===t);return new fr({hosts:t?[A??{host:t}]:D?.ssh?.hosts,user:m.user,port:Ap(m.port),privateKeyPath:m.key,hostKey:D?.ssh?.hostKey,sudo:D?.ssh?.sudo,profile:m.profile??D?.ssh?.profile,publicIp:D?.ssh?.publicIp,lan:D?.ssh?.lan})}var Sd=1073741824;function ec(D){D.command("ssh:preflight [host]","Check a bring-your-own Linux host before adopting it").option("--user <user>","SSH user (default: ssh.hosts[].user, then root)").option("--port <port>","SSH port (default: ssh.hosts[].port, then 22)").option("--key <path>","SSH private key (default: ssh.hosts[].privateKeyPath, then ~/.ssh/id_ed25519)").option("--profile <profile>","raspberry-pi or generic (default: ssh.profile, then generic)").option("--json","Print the facts and findings as JSON").action(async(t,m)=>{try{let A=await kp(),_=wd(A,t,m),z=t??_.targetHost(),U=await _.ensureHostKey(z),{facts:Y,findings:X}=await _.preflight(z);if(m.json)console.log(JSON.stringify({schemaVersion:1,host:z,hostKeyFingerprint:U.fingerprint,facts:Y,findings:X},null,2));else{if(i(`Preflight ${z}`),U.pinnedNow)e(`Pinned host key ${U.fingerprint}`);if(e(`${Y.os} on ${Y.arch}, ${Y.cpuCores} cores, ${(Y.memoryBytes/Sd).toFixed(1)} GiB memory, ${(Y.diskFreeBytes/Sd).toFixed(1)} GiB free`),Y.piModel)e(Y.piModel);if(Y.lanIp)e(`LAN address ${Y.lanIp}`);if(console.log(Zr(X)),gr(X))r("The host is not ready; fix the errors above and run the preflight again.");else n("The host is ready to adopt.")}if(gr(X))process.exitCode=1}catch(A){Xa(A)}}),D.command("ssh:bootstrap","Adopt the configured host: pin its key, run the preflight, then bootstrap it").option("--env <environment>","Deployment environment",{default:"production"}).option("--dry-run","Print the bootstrap script instead of running it").action(async(t)=>{try{let m=await Fe(),A=t.env??"production";if(t.dryRun){let U=mr(m),[Y]=U.hosts;console.log(pr({config:m,environment:A,profile:U.profile,sudoUser:Y.user!=="root"?Y.user:void 0,lan:U.lan}));return}let _=wd(m,void 0,{});i(`Adopt ${_.targetHost()}`);let z=await _.provisionComputeInfrastructure({config:m,environment:A});n(`${z.appPublicIp} is bootstrapped; deploy with: cloud deploy --env ${A}`)}catch(m){Xa(m)}}),D.command("ssh:first-boot","Write cloud-init first-boot files for a freshly flashed Raspberry Pi OS or Ubuntu image").option("--os <os>","raspberry-pi-os or ubuntu",{default:"raspberry-pi-os"}).option("--out <dir>","Directory to write user-data, network-config and meta-data into (never a mounted card; copy them yourself)").option("--hostname <name>","Hostname of the new host").option("--user <user>","Deploy user created on first boot (default: ssh.hosts[].user, then pi)").option("--key <path>","Public key authorized for the user",{default:"~/.ssh/id_ed25519.pub"}).option("--wifi-ssid <ssid>","Join this Wi-Fi network on first boot").option("--wifi-passphrase-env <name>","Environment variable holding the Wi-Fi passphrase",{default:"WIFI_PASSWORD"}).option("--wifi-country <cc>","Wi-Fi regulatory domain, e.g. DE").option("--timezone <tz>","IANA timezone, e.g. Europe/Berlin").option("--locale <locale>","System locale, e.g. en_US.UTF-8").option("--env <environment>","Deployment environment the embedded bootstrap is built for",{default:"production"}).option("--force","Overwrite files already in --out").action(async(t)=>{try{if(!t.out)throw Error("--out <dir> is required.");if(!t.hostname)throw Error("--hostname <name> is required.");let m=t.os??"raspberry-pi-os";if(!(m in Zi))throw Error(`--os must be raspberry-pi-os or ubuntu, not '${m}'.`);let A=await Fe(),_=t.env??"production",z=mr(A,{hosts:A.ssh?.hosts?.length?void 0:[{host:t.hostname}],profile:m==="raspberry-pi-os"?"raspberry-pi":void 0}),U=t.user??(A.ssh?.hosts?.[0]?.user??"pi"),Y=Qr(t.key??"~/.ssh/id_ed25519.pub");if(!vd(Y))throw Error(`Public key not found at ${Y}; pass --key <path to a .pub file>.`);let X=Sp(Y,"utf8").trim(),ne;if(t.wifiSsid){let Pe=t.wifiPassphraseEnv??"WIFI_PASSWORD",ye=process.env[Pe];if(!ye)throw Error(`--wifi-ssid needs the passphrase in $${Pe} (set it in the environment, not on the command line).`);if(!t.wifiCountry)throw Error("--wifi-country <cc> is required with --wifi-ssid.");ne={ssid:t.wifiSsid,passphrase:ye,country:t.wifiCountry}}let re=pr({config:A,environment:_,profile:z.profile,sudoUser:U!=="root"?U:void 0,lan:z.lan}),se=eo({hostname:t.hostname,user:U,publicKey:X,timezone:t.timezone,locale:t.locale,wifi:ne},re,{os:m}),he=$p(t.out);wp(he,{recursive:!0});let we=Object.keys(se.files).filter((Pe)=>vd(bd(he,Pe)));if(we.length>0&&!t.force)throw Error(`${we.join(", ")} already exist in ${he}; pass --force to overwrite.`);for(let[Pe,ye]of Object.entries(se.files))Cp(bd(he,Pe),ye,{mode:384});i(`First boot for ${t.hostname}`),n(`Wrote ${Object.keys(se.files).join(", ")} to ${he}`),console.log(se.instructions)}catch(m){Xa(m)}})}async function Rp(){let D=await Fe(),t=y(process.cwd(),D),m=new xn(t.store),A=new _n(m,new ke(t.store));return{controlPlane:t,store:m,service:A}}async function gs(D){let t=await Rp();try{await D(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}var Ks=(D)=>Object.fromEntries(String(D??"").split(",").map((t)=>t.split("=")).filter((t)=>t[0]&&t[1])),Cd=(D)=>Object.fromEntries(Object.entries(Ks(D)).map(([t,m])=>[t,Number(m)]));function tc(D){D.command("pool:list","List capacity pools and reservations").option("--json","Print structured JSON").action(async(t)=>gs((m)=>{let A=m.store.listPools(m.controlPlane.project.id);if(t.json)console.log(JSON.stringify(A,null,2));else c(["Name","Purpose","Backend","Region / arch","Status","Capacity","Concurrency"],A.map((_)=>[_.name,_.purpose,_.backend,`${_.region??"any"} / ${_.architecture??"any"}`,_.status,JSON.stringify(_.capacity),String(_.concurrency)]))})),D.command("pool:create <name>","Create a capacity pool").option("--purpose <purpose>","application, build, worker, monitoring, or backup",{default:"application"}).option("--backend <backend>","server, ecs, or asg",{default:"server"}).option("--capacity <values>","cpu=4,memoryBytes=8589934592,diskBytes=...").option("--region <region>","Required region").option("--arch <architecture>","Required architecture").option("--labels <labels>","Pool key=value labels").option("--selector <labels>","Required server labels").option("--max <count>","Maximum workloads",{default:"10"}).option("--concurrency <count>","Build concurrency",{default:"1"}).action(async(t,m)=>gs((A)=>{let{purpose:_,backend:z}=m;if(!["application","build","worker","monitoring","backup"].includes(_)||!["server","ecs","asg"].includes(z))throw Error("Unsupported pool purpose or backend.");let U=A.store.createPool({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,purpose:_,backend:z,region:m.region,architecture:m.arch,labels:Ks(m.labels),requiredServerLabels:Ks(m.selector),toleratedTaints:[],capacity:{cpu:0,memoryBytes:0,diskBytes:0,gpu:0,...Cd(m.capacity)},maxWorkloads:Number(m.max),costWeight:1,concurrency:Number(m.concurrency),ephemeralWorkspaces:!0,allowProductionSecrets:!1,status:"active"});n(`Created ${U.name} (${U.id}).`)})),D.command("pool:add-server <pool> <server>","Attach an enrolled server to a pool").action(async(t,m)=>gs((A)=>{let _=A.store.listPools(A.controlPlane.project.id).find((U)=>U.id===t||U.name===t),z=new ke(A.controlPlane.store).list(A.controlPlane.project.id).find((U)=>U.id===m||U.name===m);if(!_||!z)throw Error("Pool or server was not found.");A.store.addMember(_.id,z.id),n(`Attached ${z.name} to ${_.name}.`)})),D.command("placement:explain","Explain eligible and rejected placement targets").option("--purpose <purpose>","Workload purpose",{default:"application"}).option("--resources <values>","Requested resources").option("--region <region>","Required region").option("--arch <architecture>","Required architecture").option("--labels <labels>","Required pool labels").option("--json","Print structured JSON").action(async(t)=>gs((m)=>{let A=m.service.explain(m.controlPlane.project.id,{purpose:t.purpose,resources:Cd(t.resources),region:t.region,architecture:t.arch,labels:Ks(t.labels)});if(t.json)console.log(JSON.stringify(A,null,2));else c(["Pool","Server","Eligible","Available","Fit / spread / cost","Reasons"],A.map((_)=>[_.poolName,_.serverId??"provider-managed",_.eligible?"yes":"no",JSON.stringify(_.available),`${_.score.fit.toFixed(2)} / ${_.score.spread} / ${_.score.cost}`,_.reasons.join("; ")||"eligible"]))})),D.command("pool:drain <pool>","Move stateless placements and block stateful placements").action(async(t)=>gs((m)=>{let A=m.store.listPools(m.controlPlane.project.id).find((z)=>z.id===t||z.name===t);if(!A)throw Error("Pool was not found.");let _=m.service.drainPool(A.id);n(`Draining ${A.name}: moved ${_.moved.length}, blocked ${_.blocked.length} stateful placement(s).`)}))}async function Ip(){let D=await Fe(),t=y(process.cwd(),D),m=new On(t.store),A=new $n(m);return{controlPlane:t,store:m,service:A}}async function po(D){let t=await Ip();try{await D(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}var hs=(D,t)=>{let m=D.store.list(D.controlPlane.project.id).find((A)=>A.id===t||A.name===t);if(!m)throw Error("Regional topology was not found.");return m},Pp=(D)=>D.split(",").map((t,m)=>{let[A,_="aws"]=t.split(":");return{region:String(A),provider:_,role:m===0?"primary":"secondary"}}),Dp=(D)=>{let t=new Set(["s3","dynamodb","secrets"]),m=D.split(",").map((_)=>_.trim()),A=m.find((_)=>!t.has(_));if(A)throw Error(`Unsupported replication kind: ${A}`);return m};function nc(D){D.command("region:list","List regional topology, traffic, and replication health").option("--json","Print structured JSON").action(async(t)=>po((m)=>{let A=m.store.list(m.controlPlane.project.id).map((_)=>({topology:_,targets:m.store.targets(_.id),channels:m.store.channels(_.id),route:m.store.route(_.id),executions:m.store.executions(_.id)}));if(t.json)console.log(JSON.stringify(A,null,2));else c(["Name","Hostname","Active","Status","Revision","Replication"],A.map((_)=>[_.topology.name,_.topology.hostname,_.topology.activeRegion,_.topology.status,_.topology.revision??"—",`${_.channels.filter((z)=>z.status==="in_sync").length}/${_.channels.length} in sync`]))})),D.command("region:create <name>","Create a persistent multi-region topology").option("--hostname <hostname>","Traffic hostname").option("--regions <regions>","Comma-separated region[:provider], primary first").option("--replicate <kinds>","s3,dynamodb,secrets",{default:"s3,dynamodb,secrets"}).option("--max-lag <seconds>","Maximum failover lag",{default:"30"}).option("--cdn","Enable CloudFront edge state").option("--waf","Enable WAF edge state").action(async(t,m)=>po((A)=>{if(!m.hostname||!m.regions)throw Error("--hostname and --regions are required.");let _=A.service.create({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,hostname:m.hostname,regions:Pp(m.regions),trafficPolicy:"active_passive",dataPolicy:{replicate:Dp(String(m.replicate)),maxLagSeconds:Number(m.maxLag),retainOnDestroy:!0},dnsProvider:"route53",cdnEnabled:!!m.cdn,wafEnabled:!!m.waf});n(`Created ${_.name} (${_.id}); run region:rollout with an immutable manifest.`)})),D.command("region:plan <name> <operation>","Preview exact regional execution steps").option("--target <region>","Failover or failback region").action(async(t,m,A)=>po((_)=>c(["Step"],_.service.plan(hs(_,t).id,m,A.target).map((z)=>[z])))),D.command("region:rollout <name> <revision>","Queue secondary-first regional stack rollout").option("--manifest <file>","Regional stack manifest JSON").action(async(t,m,A)=>po(async(_)=>{let z=A.manifest?await Bun.file(A.manifest).json():{},U=_.service.enqueue({topologyId:hs(_,t).id,kind:"rollout",revision:m,manifest:z});n(`Regional rollout queued: ${U.operation.id}`)})),D.command("region:failover <name> <region>","Queue health and RPO-gated failover").action(async(t,m)=>po((A)=>{let _=A.service.enqueue({topologyId:hs(A,t).id,kind:"failover",targetRegion:m});n(`Failover queued: ${_.operation.id}`)})),D.command("region:failback <name> <region>","Queue health and RPO-gated failback").action(async(t,m)=>po((A)=>{let _=A.service.enqueue({topologyId:hs(A,t).id,kind:"failback",targetRegion:m});n(`Failback queued: ${_.operation.id}`)})),D.command("region:destroy <name>","Drain traffic and delete regional stacks while retaining data").option("--confirm <name>","Exact topology name").action(async(t,m)=>po((A)=>{let _=A.service.enqueue({topologyId:hs(A,t).id,kind:"destroy",confirmation:m.confirm});n(`Regional teardown queued: ${_.operation.id}; replicated data remains retained.`)}))}var ys="0.16.47";async function Ep(){let D=await Fe(),t=y(process.cwd(),D),m=new Ln(t.store),A=new Mn(m),_=["releases","artifacts","operations","logs","previews","snapshots","orphaned_resources"].map((z)=>new Un(z,t.store,t.project.id,t.organization.id));return{controlPlane:t,store:m,service:A,cleanup:_}}async function Ii(D){let t=await Ep();try{await D(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}function rc(D){D.command("maintenance:list","List updates, campaigns, cleanup plans, and DR drills").option("--json","Print structured JSON").action(async(t)=>Ii((m)=>{let A={manifests:m.store.manifests(m.controlPlane.organization.id),windows:m.store.windows(m.controlPlane.project.id),campaigns:m.store.campaigns(m.controlPlane.project.id),cleanups:m.store.cleanups(m.controlPlane.project.id),drills:m.store.drills(m.controlPlane.project.id)},_=[...A.manifests.map((z)=>["update",z.version,z.compatibility.compatible?"compatible":"blocked",z.compatibility.reasons.join("; ")||z.channel]),...A.campaigns.map((z)=>["upgrade",z.id,z.status,z.currentStage??z.fromVersion]),...A.cleanups.map((z)=>["cleanup",z.kind,z.status,`${z.candidates.length} items / ${z.estimatedBytes} bytes`]),...A.drills.map((z)=>["DR drill",z.scenario,z.status,z.isolatedTarget])];if(t.json)console.log(JSON.stringify(A,null,2));else c(["Area","Name / version","Status","Detail"],_)})),D.command("maintenance:key:add <name> <pem>","Trust an Ed25519 update signing key").action(async(t,m)=>Ii(async(A)=>{let _=A.store.addTrustedKey({organizationId:A.controlPlane.organization.id,name:t,publicKeyPem:await Bun.file(m).text()});n(`Trusted ${_.name}: ${_.fingerprint}`)})),D.command("maintenance:manifest <document> <signature> <key>","Verify and import a signed update manifest").action(async(t,m,A)=>Ii(async(_)=>{let z=await Bun.file(t).json(),U=(await Bun.file(m).text()).trim(),Y=_.store.importManifest({organizationId:_.controlPlane.organization.id,keyId:A,document:z,signature:U,current:{schemaVersion:_.controlPlane.store.health().schemaVersion,currentVersion:ys,platform:process.platform,architecture:process.arch}});n(`Verified ${Y.version}; ${Y.compatibility.compatible?"compatible":"blocked: "+Y.compatibility.reasons.join("; ")}`)})),D.command("maintenance:window <name> <schedule>","Create an approved maintenance window").option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--minutes <minutes>","Duration",{default:"60"}).action(async(t,m,A)=>Ii((_)=>{let z=_.store.createWindow({projectId:_.controlPlane.project.id,name:t,schedule:m,timezone:A.timezone,durationMinutes:Number(A.minutes),allowedOperations:["platform.upgrade"],requireApproval:!0});n(`Created ${z.name} (${z.id}).`)})),D.command("platform:upgrade <manifest> <backup>","Plan and queue a signed staged platform update").option("--strategy <strategy>","canary or rolling",{default:"canary"}).option("--batch <count>","Rolling batch size",{default:"1"}).action(async(t,m,A)=>Ii((_)=>{let z=new ke(_.controlPlane.store).list(_.controlPlane.project.id).filter((X)=>X.status==="ready").map((X)=>({serverId:X.id,target:X.name,previousVersion:X.bootstrapVersion??ys}));if(!z.length)z.push({target:"control-plane",previousVersion:ys});let U=_.store.createCampaign({projectId:_.controlPlane.project.id,manifestId:t,fromVersion:ys,strategy:A.strategy==="rolling"?"rolling":"canary",batchSize:Number(A.batch),healthGate:{kind:"platform"},backupId:m,targets:z}),Y=_.service.enqueueCampaign(U.id);n(`Staged upgrade queued: ${Y.operation.id}`)})),D.command("cleanup:preview <kind>","Discover a bounded cleanup candidate set without deleting").option("--older-than <days>","Minimum age",{default:"30"}).action(async(t,m)=>Ii(async(A)=>{let _=await A.service.previewCleanup({projectId:A.controlPlane.project.id,kind:t,criteria:{olderThanDays:Number(m.olderThan)}},A.cleanup);c(["ID","Candidate","Bytes"],_.candidates.map((z)=>[z.id,z.label,String(z.bytes)])),e(`Apply with: cloud cleanup:apply ${_.id} --confirm "DELETE ${_.candidates.length} ${_.kind}"`)})),D.command("cleanup:apply <plan>","Apply an unchanged cleanup preview").option("--confirm <text>","Exact preview confirmation").action(async(t,m)=>Ii((A)=>{let _=A.service.enqueueCleanup(t,String(m.confirm??""));n(`Cleanup queued: ${_.operation.id}`)})),D.command("dr:plan <scenario> <target>","Plan an isolated disaster-recovery drill").option("--rpo <minutes>","RPO objective",{default:"60"}).option("--rto <minutes>","RTO objective",{default:"30"}).option("--recovery-point <id>","Recovery point").option("--topology <id>","Regional topology").action(async(t,m,A)=>Ii((_)=>{let z=_.store.createDrill({projectId:_.controlPlane.project.id,scenario:t,isolatedTarget:m,expectedRpoMinutes:Number(A.rpo),expectedRtoMinutes:Number(A.rto),recoveryPointId:A.recoveryPoint,topologyId:A.topology});n(`Planned isolated drill ${z.id}.`)})),D.command("dr:run <drill>","Queue an isolated disaster-recovery drill").action(async(t)=>Ii((m)=>{let A=m.service.enqueueDrill(t);n(`DR drill queued: ${A.operation.id}`)}))}async function ic(){try{return(await Fe()).project.region||process.env.AWS_REGION||"us-east-1"}catch{return process.env.AWS_REGION||"us-east-1"}}function oc(D){D.command("function:list","List all Lambda functions").option("--region <region>","AWS region").action(async(t)=>{i("Lambda Functions");let m=t?.region||await ic(),A=new I(m);try{let{Functions:_=[]}=await A.listFunctions({MaxItems:50});if(!_.length){e("No functions found");return}c(["Name","Runtime","Memory","Timeout","Last Modified"],_.map((z)=>[z.FunctionName??"-",z.Runtime??"-",z.MemorySize?`${z.MemorySize} MB`:"-",z.Timeout?`${z.Timeout}s`:"-",z.LastModified??"-"]))}catch(_){r(`Failed to list functions: ${_.message}`),process.exitCode=1}}),D.command("function:invoke <name>","Invoke a Lambda function").option("--payload <json>","Event payload as JSON",{default:"{}"}).option("--async","Fire-and-forget (Event invocation)").option("--region <region>","AWS region").action(async(t,m)=>{i(`Invoking ${t}`);let A=m?.region||await ic(),_=new I(A),z=new s("Invoking function...");z.start();try{let U=await _.invoke({FunctionName:t,InvocationType:m?.async?"Event":"RequestResponse",Payload:m?.payload||"{}",LogType:m?.async?"None":"Tail"});if(U.FunctionError){if(z.fail(`Function returned an error (${U.FunctionError})`),U.Payload)e(U.Payload);process.exitCode=1;return}if(z.succeed(`Invoked (status ${U.StatusCode})`),U.Payload)e(`
|
|
318
|
+
`)}async function pp(){let D=await Fe(),t=y(process.cwd(),D),m=new ke(t.store),A=new Nn(m,[new it("aws"),new it("hetzner"),new it("ssh")]);return{controlPlane:t,store:m,service:A}}async function bi(D){let t=await pp();try{return await D(t)}finally{t.controlPlane.store.close()}}var Mi=(D,t)=>{let m=D.store.list(D.controlPlane.project.id).find((A)=>A.id===t||A.name===t);if(!m)throw Error(`Server ${t} was not found.`);return m},yn=(D)=>{r(D instanceof Error?D.message:String(D)),process.exitCode=1};function Za(D,t){let m=Number.parseInt(D??"",10);return Number.isFinite(m)&&m>=0?m:t}function fp(D="3h"){let t=D.match(/^(\d+)(m|h)$/);if(!t)throw Error("--range must use minutes or hours, for example 30m or 3h.");let m=Number(t[1])*(t[2]==="h"?3600000:60000);if(m<300000||m>86400000)throw Error("--range must be between 5 minutes and 24 hours.");return m}function ps(D,t="%"){return D==null?"unavailable":`${D.toFixed(1)}${t}`}async function gp(D,t){let m=await Fe();if(m.cloud?.provider!=="hetzner")throw Error("server:monitoring currently requires the Hetzner provider.");let A=t.env??process.env.CLOUD_ENV??"production",_=T(m,A),z=await pe(_);if(!z?.serverId)throw Error(`No pinned Hetzner server was found for ${_}.`);if(![z.serverName,String(z.serverId),z.stackName].includes(D))throw Error(`Refusing to read ${D}: project state is pinned to ${z.serverName}.`);let U=lr(m),Y=new We({apiToken:dr(U.apiToken,m)}),X=await Y.getServer(z.serverId),ne=z.publicIp??X.public_net.ipv4?.ip;if(!ne)throw Error(`Server ${X.name} has no public IPv4 address.`);let re=new Date,se=new Date(re.getTime()-fp(t.range)),he=Math.min(3600,Math.max(60,Za(t.step,60))),we=await fd({client:Y,serverId:X.id,cores:Math.max(1,Number(X.server_type.cores)||1),from:se,to:re,step:he,remote:{host:ne,user:z.sshUser??U.sshUser,identityFile:U.sshPrivateKeyPath,connectTimeoutSec:15}});if(t.json){console.log(JSON.stringify({schemaVersion:1,server:X.name,result:we},null,2));return}c(["Server","Window","CPU average / peak","RAM average / peak","Minimum available","Swap peak"],[[X.name,`${we.from} to ${we.to}`,`${ps(we.summary.cpuAveragePercent)} / ${ps(we.summary.cpuPeakPercent)}`,`${ps(we.summary.memoryAveragePercent)} / ${ps(we.summary.memoryPeakPercent)}`,we.summary.memoryMinimumAvailableBytes==null?"unavailable":`${(we.summary.memoryMinimumAvailableBytes/1073741824).toFixed(2)} GB`,ps(we.summary.swapPeakPercent)]])}function yd(D){let t=D.targetType.prices?.find((m)=>m.location===D.location);return{serverId:D.serverId,serverName:D.serverName,location:D.location,from:D.currentType.name,to:D.targetType.name,capacityAvailable:D.capacityAvailable,alreadyComplete:D.alreadyComplete,upgradeDisk:D.upgradeDisk,target:{cores:D.targetType.cores,memoryGb:D.targetType.memory,diskGb:D.targetType.disk,architecture:D.targetType.architecture,cpuType:D.targetType.cpu_type},price:t?{hourlyNet:t.price_hourly.net,hourlyGross:t.price_hourly.gross,monthlyNet:t.price_monthly.net,monthlyGross:t.price_monthly.gross}:void 0}}function hp(D){let t=yd(D);c(["Server","Location","Resize","Capacity","CPU / RAM / disk","Monthly"],[[`${t.serverName} (${t.serverId})`,t.location,`${t.from} -> ${t.to}`,t.capacityAvailable?"available":"waiting",`${t.target.cores??0} / ${t.target.memoryGb??0} GB / ${t.target.diskGb??0} GB`,t.price?.monthlyGross??t.price?.monthlyNet??"not reported"]])}async function yp(D,t,m){let A=await Fe();if(A.cloud?.provider!=="hetzner")throw Error("server:resize currently requires the Hetzner provider.");let _=m.env??process.env.CLOUD_ENV??"production",z=T(A,_),U=await pe(z),Y=lr(A),X=new We({apiToken:dr(Y.apiToken,A)}),re=U&&(D===U.serverName||D===String(U.serverId)||D===U.stackName)?await X.getServer(U.serverId):(await X.listServers()).find((je)=>je.name===D||String(je.id)===D);if(!re)throw Error(`Hetzner server ${D} was not found.`);if(U?.serverId&&U.serverId!==re.id)throw Error(`Refusing to resize ${D}: project state is pinned to server ${U.serverId}.`);let se=dt(t),he=m.upgradeDisk!==!1,we=await ci(X,re.id,se,he);if(m.json)console.log(JSON.stringify({schemaVersion:1,plan:yd(we)},null,2));else hp(we);if(!m.apply){if(!m.json)e(`Preview only. Re-run with --apply --confirm ${re.name} to execute.`);return}if(m.confirm!==re.name)throw Error(`Resize requires --confirm ${re.name}.`);let Pe=U?.publicIp??re.public_net.ipv4?.ip;if(!Pe)throw Error(`Server ${re.name} has no public IPv4 address for verification.`);let ye={host:Pe,user:U?.sshUser??Y.sshUser,identityFile:Y.sshPrivateKeyPath,connectTimeoutSec:15,sshTimeoutMs:600000,routeTimeoutMs:20000},Ie=await ls(z),Le=new Date().toISOString(),Re={schemaVersion:1,operationId:Ie?.targetType===se&&Ie.serverId===re.id?Ie.operationId:crypto.randomUUID(),stackName:z,serverId:re.id,serverName:re.name,sourceType:we.currentType.name,targetType:se,upgradeDisk:he,phase:"planning",status:"running",attempts:Ie?.targetType===se?Ie.attempts:0,startedAt:Ie?.targetType===se?Ie.startedAt:Le,updatedAt:Le,manifest:Ie?.targetType===se?Ie.manifest:void 0};await pt(Re);let De=Za(m.pollSeconds,900)*1000,Ee=Za(m.maxWaitMinutes,0)*60000,Ke=Date.now();for(;;){let je=await ci(X,re.id,se,he);if(!je.capacityAvailable&&!je.alreadyComplete){if(Re={...Re,phase:"waiting-capacity",status:"waiting-capacity",updatedAt:new Date().toISOString()},await pt(Re),!m.wait){if(!m.json)e(`${se} has no capacity in ${je.location}. The server remains running and unchanged. Use --wait to poll safely.`);return}if(Ee>0&&Date.now()-Ke>=Ee)throw Error(`Timed out waiting for ${se} capacity in ${je.location}.`);if(!m.json)e(`Waiting ${Math.round(De/1000)} seconds for ${se} capacity...`);await Bun.sleep(De);continue}let _e=await ds(z);try{let et=await(async()=>{try{return Re={...Re,status:"running",attempts:Re.attempts+1,updatedAt:new Date().toISOString()},await pt(Re),await ns({client:X,serverId:re.id,targetType:se,upgradeDisk:he,resumeFromOff:Re.phase==="shutting-down"||Re.phase==="resizing"||Re.phase==="powering-on"||Re.phase==="verifying",manifest:Re.manifest,hooks:{preflight:async()=>{let Rt=await is(ye);return Re={...Re,manifest:Rt,updatedAt:new Date().toISOString()},await pt(Re),Rt},afterBoot:async(Rt,Et,wt,Pt)=>os(ye,Et,wt,Pt),onPhase:async(Rt,Et)=>{if(Re={...Re,phase:Rt,status:Rt==="waiting-capacity"?"waiting-capacity":Rt==="complete"?"completed":"running",updatedAt:new Date().toISOString()},await pt(Re),!m.json)e(`${Rt}${Et?` ${JSON.stringify(Et)}`:""}`)}}})}finally{await _e()}})();if(Re={...Re,status:et.status==="completed"||et.status==="already-complete"?"completed":et.status==="recovered"?"recovered":"waiting-capacity",lastError:et.status==="recovered"?et.error:void 0,updatedAt:new Date().toISOString()},await pt(Re),m.json)console.log(JSON.stringify({schemaVersion:1,result:et},null,2));else if(et.status==="completed"||et.status==="already-complete")n(`${re.name} is on ${se} and passed full post-boot verification.`);else if(et.status==="recovered"&&et.retryable&&m.wait){e(`Capacity race recovered safely. Waiting ${Math.round(De/1000)} seconds before retry.`),await Bun.sleep(De);continue}else if(et.status==="recovered")throw Error(`Resize did not complete and the original server was recovered: ${et.error}`);return}catch(et){throw Re={...Re,status:"failed",lastError:et instanceof Error?et.message:String(et),updatedAt:new Date().toISOString()},await pt(Re),et}}}async function vp(D,t){let m=await Fe();if(m.cloud?.provider!=="hetzner")throw Error("server:optimize currently requires the Hetzner provider.");let A=t.env??process.env.CLOUD_ENV??"production",_=T(m,A),z=await pe(_);if(!z?.serverId)throw Error(`No pinned Hetzner server was found for ${_}.`);if(![z.serverName,String(z.serverId),z.stackName].includes(D))throw Error(`Refusing to optimize ${D}: project state is pinned to ${z.serverName}.`);let U=lr(m),Y=new We({apiToken:dr(U.apiToken,m)}),X=await Y.getServer(z.serverId),ne=z.publicIp??X.public_net.ipv4?.ip;if(!ne)throw Error(`Server ${X.name} has no public IPv4 address.`);let re=to(m);if(t.json)console.log(JSON.stringify({schemaVersion:1,server:X.name,plan:re},null,2));else c(["Server","Firewall ports","Monitoring","Auto updates","Swap","SSH passwords"],[[X.name,re.firewallPorts.join(","),re.monitoring?"on":"off",re.autoUpdates?"on":"off",`${re.swapGb} GB`,"disabled"]]);if(!t.apply){if(!t.json)e(`Preview only. Re-run with --apply --confirm ${X.name} to execute.`);return}if(t.confirm!==X.name)throw Error(`Optimization requires --confirm ${X.name}.`);let se={host:ne,user:z.sshUser??U.sshUser,identityFile:U.sshPrivateKeyPath,connectTimeoutSec:15},he=await en(se),we=he.routeProbes.filter((wt)=>!wt.ok),Pe=m.infrastructure?.compute,ye=Z(Pe)?new Set(["nginx.service","bun-gateway.service","ts-cloud-nginx.service"]):new Set,Ie=he.failedUnits.filter((wt)=>!ye.has(wt));if(Ie.length>0)throw Error(`Preflight found failed services: ${Ie.join(", ")}`);if(we.length>0)throw Error(`Preflight found unhealthy routes: ${we.map((wt)=>wt.domain).join(", ")}`);let Le=Z(Pe)?[]:Object.values(m.sites??{}).flatMap((wt)=>typeof wt?.port==="number"&&![80,443].includes(wt.port)?[wt.port]:[]),Re=Yr({allowSsh:Pe?.allowSsh!==!1,sitePorts:Le,allowedPorts:Pe?.firewall?.allowedPorts}),De=(z.firewallId?(await Y.listFirewalls()).find((wt)=>wt.id===z.firewallId):void 0)??(await Y.listFirewalls()).find((wt)=>wt.name===`${m.project.slug}-${A}-app-fw`);if(!De)throw Error(`Managed firewall for ${X.name} was not found.`);let Ee=await Y.setFirewallRules(De.id,Re);await Promise.all(Ee.map((wt)=>Y.waitForAction(wt.id))),await ss(m,se);let[Ke,je]=await Promise.all([en(se),as(se)]),_e=cs(re,je),et=pc(he,Ke),Rt=Ke.routeProbes.filter((wt)=>!wt.ok);if(et.stoppedServices.length>0)_e.push(`services stopped: ${et.stoppedServices.join(", ")}`);if(et.changedRouteFragments.length>0)_e.push(`route fragments changed: ${et.changedRouteFragments.join(", ")}`);if(et.missingRouteIds.length>0)_e.push(`routes disappeared: ${et.missingRouteIds.join(", ")}`);if(et.changedReleaseLinks.length>0)_e.push(`release links changed: ${et.changedReleaseLinks.join(", ")}`);if(et.missingData.length>0)_e.push(`databases or volumes disappeared: ${et.missingData.join(", ")}`);if(Rt.length>0)_e.push(`unhealthy routes: ${Rt.map((wt)=>wt.domain).join(", ")}`);if(_e.length>0)throw Error(`Host optimization verification failed: ${_e.join("; ")}`);let Et={serverId:X.id,serverName:X.name,services:Ke.runningServices.length,routes:Ke.routeIds.length,healthyDomains:Ke.routeProbes.length,releaseLinks:Ke.releaseLinks.length,databasesAndVolumes:Ke.dataCatalog.length,report:je};if(t.json)console.log(JSON.stringify({schemaVersion:1,result:Et},null,2));else n(`${X.name} passed full host, rpx route, service, release, and data verification.`)}async function bp(D,t,m){let A=await Fe(),_=m.env??"production",z=T(A,_);await bi(async(U)=>{let Y=Mi(U,D),X=U.store.list(U.controlPlane.project.id,!0),ne=Y.provider==="hetzner"&&Y.providerId?(()=>{try{let De=lr(A);return new We({apiToken:dr(De.apiToken,A)})}catch{return null}})():null,re=Y.providerId?Number(Y.providerId):Number.NaN,se=await pe(z),he=se!=null&&se.serverId===re,we=new Br,Pe=Y.trustState==="pinned",ye={takenNames:async()=>{let De=X.map((Ee)=>Ee.name);if(ne)De.push(...(await ne.listServers()).map((Ee)=>Ee.name));return De},inventoryName:()=>U.store.get(Y.id)?.name??Y.name,renameInventory:(De)=>{U.store.update(Y.id,{name:De})},...ne&&Number.isFinite(re)?{providerName:async()=>(await ne.getServer(re)).name,renameProvider:async(De)=>{await ne.renameServer(re,De)}}:{},...he?{stateName:async()=>(await pe(z))?.serverName,writeStateName:async(De)=>{let Ee=await pe(z);if(Ee)await ut(z,{...Ee,serverName:De})}}:{},...Pe?{remoteHostname:async()=>{let De=await we.exec(Y,"hostname");return De.code===0?De.stdout.trim():void 0},setRemoteHostname:async(De)=>{let Ee=await we.exec(Y,hd(De));if(Ee.code!==0)throw Error(Ee.stderr.trim()||`hostname exited ${Ee.code}`)}}:{}},Ie=await gd(Y.name,t,ye),Le=await Ws(Ie);if(!m.apply){let De=[ne?"":"provider record (no provider id or no API token)",he?"":"local state pin (none points at this server)",Pe?"":"box hostname (host key is not pinned — run server:validate first)"].filter(Boolean);if(m.json){console.log(JSON.stringify({schemaVersion:1,operation:Ie.operation,target:Ie.target,rename:{from:Y.name,to:t},notCovered:De,steps:Le.map((Ee)=>({id:Ee.step.id,title:Ee.step.title,state:Ee.state,change:Ee.step.change,reason:Ee.reason}))},null,2));return}for(let Ee of Vs(Ie,Le))console.log(Ee);for(let Ee of De)console.log(` not covered: ${Ee}`);console.log(" Re-run with --apply to perform it.");return}let Re=await Gs(Ie,Le,{log:(De)=>e(De),audit:(De)=>U.controlPlane.store.appendEvent({projectId:U.controlPlane.project.id,resourceId:Y.resourceId,type:`${De.operation}.${De.step}.${De.state}`,level:De.state==="failed"?"error":"info",payload:{target:De.target,renameTo:t,...De.error?{error:De.error}:{}}})});if(m.json)console.log(JSON.stringify({schemaVersion:1,outcome:Re},null,2));if(!Re.success){let De=Re.steps.find((Ee)=>Ee.state==="failed");throw Error(`${Ie.operation} stopped at '${De?.title}': ${De?.error}. Earlier steps stayed applied; re-run the same command to continue from here.`)}if(!m.json)n(`Renamed ${Y.name} to ${t}.`)})}function Qa(D){D.command("capabilities [server]","Show provider and target operation support").option("--json","Print structured JSON").action(async(t,m)=>{try{await bi(async(A)=>{let _=t?[Mi(A,t)]:A.store.list(A.controlPlane.project.id),z=_.flatMap((U)=>Object.entries(U.capabilities).map(([Y,X])=>({server:U.name,provider:U.provider,action:Y,...X})));if(m.json)console.log(JSON.stringify({schemaVersion:1,targets:z},null,2));else c(["Server","Provider","Action","Support","Explanation"],z.map((U)=>[U.server,U.provider,U.action,U.supported?"supported":"unsupported",U.reason??"—"]))})}catch(A){yn(A)}}),D.command("server:resize <name> <type>","Safely resize Hetzner compute with full host verification").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Execute the reviewed resize plan").option("--confirm <name>","Exact server name required for mutation").option("--upgrade-disk","Permanently expand the root disk to the target size",{default:!0}).option("--wait","Poll capacity without taking the server offline until placement is available").option("--poll-seconds <seconds>","Capacity retry interval",{default:"900"}).option("--max-wait-minutes <minutes>","Stop waiting after this many minutes; 0 waits indefinitely",{default:"0"}).option("--json","Print structured JSON").action(async(t,m,A)=>{try{await yp(t,m,A)}catch(_){yn(_)}}),D.command("server:optimize <name>","Reconcile and verify a production Hetzner host").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Apply the reviewed host optimization plan").option("--confirm <name>","Exact server name required for mutation").option("--json","Print structured JSON").action(async(t,m)=>{try{await vp(t,m)}catch(A){yn(A)}}),D.command("server:monitoring <name>","Read historical Hetzner CPU, RAM, swap, disk, and network metrics").option("--env <environment>","Deployment environment",{default:"production"}).option("--range <duration>","Historical window from 5m to 24h",{default:"3h"}).option("--step <seconds>","Hetzner provider sample interval",{default:"60"}).option("--json","Print structured JSON").action(async(t,m)=>{try{await gp(t,m)}catch(A){yn(A)}}),D.command("server:list","List fleet servers").option("--json","Print structured JSON").action(async(t)=>{try{await bi(async(m)=>{let A=m.store.list(m.controlPlane.project.id);if(t.json)console.log(JSON.stringify(A,null,2));else c(["Name","Provider ID","Provider / region","Status / trust","Roles","CPU / memory","Heartbeat"],A.map((_)=>[_.name,_.providerId??"—",`${_.provider} / ${_.region??"external"}`,`${_.status} / ${_.trustState}`,_.roles.join(","),`${_.capacity.cpu??0} / ${_.capacity.memoryBytes??0}`,_.heartbeatAt??"never"]))})}catch(m){yn(m)}}),D.command("server:create <name>","Enroll a provisioned or existing server").option("--provider <provider>","aws, hetzner, or ssh",{default:"ssh"}).option("--provider-id <id>","Stable provider identity").option("--endpoint <host>","SSH hostname or IP").option("--user <user>","Non-root SSH user",{default:"deploy"}).option("--credential-ref <ref>","Secret reference",{default:"secret://fleet/agent"}).option("--region <region>","Provider region").option("--roles <roles>","Comma-separated roles",{default:"application"}).option("--labels <labels>","Comma-separated key=value labels").action(async(t,m)=>{try{await bi(async(A)=>{let _=m.provider;if(!["aws","hetzner","ssh"].includes(_)||!m.endpoint)throw Error("A supported --provider and --endpoint are required; provisioning and enrollment are separate operations.");let z=A.service.enroll({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,provider:_,providerId:m.providerId,endpoint:m.endpoint,sshUser:m.user??"deploy",credentialRef:m.credentialRef??"secret://fleet/agent",region:m.region,roles:String(m.roles??"application").split(","),labels:Object.fromEntries(String(m.labels??"").split(",").map((U)=>U.split("=")).filter((U)=>U[0]&&U[1]))});n(`Enrolled ${z.name} as ${z.id}; no remote mutation was performed.`)})}catch(A){yn(A)}}),D.command("server:validate <name>","Pin trust and validate a server").option("--accept-host-key <fingerprint>","Accept reviewed rotation").option("--json","Print JSON").action(async(t,m)=>{try{await bi(async(A)=>{let _=Mi(A,t);if(_=await A.service.test(_.id),_.trustState==="rotation_pending"){if(!m.acceptHostKey)throw Error(`Host key changed to ${_.pendingHostKey}; review it first.`);_=A.service.reviewHostKey(_.id,m.acceptHostKey)}let z=await A.service.validate(_.id);if(m.json)console.log(JSON.stringify(z.validation,null,2));else c(["Severity","Code","Finding","Remediation"],(z.validation?.findings??[]).map((U)=>[U.severity,U.code,U.message,U.remediation??"—"]));if(!z.validation?.valid)process.exitCode=1})}catch(A){yn(A)}}),D.command("server:bootstrap <name>","Preview or queue bootstrap").option("--apply","Apply reviewed plan").action(async(t,m)=>{try{await bi(async(A)=>{let _=A.service.bootstrap(Mi(A,t).id,!!m.apply);if(_.preview)c(["Step"],_.steps.map((z)=>[z]));else n(`Bootstrap queued: ${_.operation?.id}`)})}catch(A){yn(A)}}),D.command("server:rename <name> <new-name>","Rename a server in place, without recreating it").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Perform the reviewed rename plan").option("--json","Print structured JSON").action(async(t,m,A)=>{try{await bp(t,m,A)}catch(_){yn(_)}}),D.command("server:drain <name>","Drain without terminating").option("--complete","Mark movement complete").action(async(t,m)=>{try{await bi(async(A)=>n(A.service.drain(Mi(A,t).id,!!m.complete).status))}catch(A){yn(A)}}),D.command("server:uncordon <name>","Return a server to scheduling").action(async(t)=>{try{await bi(async(m)=>{m.service.uncordon(Mi(m,t).id),n(`Uncordoned ${t}.`)})}catch(m){yn(m)}}),D.command("server:archive <name>","Archive inventory without termination").option("--confirm <name>","Exact name").action(async(t,m)=>{try{await bi(async(A)=>{A.service.archive(Mi(A,t).id,m.confirm??""),n(`Archived ${t}; provider infrastructure was not terminated.`)})}catch(A){yn(A)}}),D.command("server:ssh <name>","Open strict SSH using the enrolled endpoint").action(async(t)=>{try{await bi(async(m)=>{let A=Mi(m,t);if(A.trustState!=="pinned")throw Error("Validate and pin the server host key first.");let _=Bun.spawn(["ssh","-p",String(A.sshPort),"-o","StrictHostKeyChecking=yes",`${A.sshUser}@${A.endpoint}`],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}),z=await _.exited;if(z)process.exitCode=z})}catch(m){yn(m)}});for(let[t,m]of[["server:logs <name>","Use the runtime log service"],["server:deploy <name>","Use immutable release deployment"],["server:reboot <name>","Reboot provider compute"],["server:destroy <name>","Terminate provider compute"],["server:recipe <name> <recipe>","Apply a server recipe"],["server:firewall:add <name> <rule>","Add firewall rule"],["server:firewall:list <name>","List firewall rules"],["server:firewall:remove <name> <rule>","Remove firewall rule"],["server:ssl:install <domain>","Install TLS"],["server:ssl:renew <domain>","Renew TLS"],["server:snapshot <name>","Create provider snapshot"],["server:snapshot:restore <name> <snapshot-id>","Restore provider snapshot"],["server:update <name>","Update OS packages"],["server:secure <name>","Apply OS hardening"]])D.command(t,m).action(async(...A)=>En(t.split(" ")[0],{target:String(A[0]??""),nextAction:"Use the dashboard capability view or the corresponding runtime, release, firewall, backup, or maintenance service."}))}import{existsSync as vd,mkdirSync as wp,readFileSync as Sp,writeFileSync as Cp}from"node:fs";import{join as bd,resolve as $p}from"node:path";var Xa=(D)=>{r(D instanceof Error?D.message:String(D)),process.exitCode=1};async function kp(){try{return await Fe()}catch{return}}function Ap(D){if(D===void 0)return;let t=Number(D);if(!Number.isInteger(t)||t<1||t>65535)throw Error(`--port must be a port number, not '${D}'.`);return t}function wd(D,t,m){let A=D?.ssh?.hosts?.find((_)=>_.host===t);return new fr({hosts:t?[A??{host:t}]:D?.ssh?.hosts,user:m.user,port:Ap(m.port),privateKeyPath:m.key,hostKey:D?.ssh?.hostKey,sudo:D?.ssh?.sudo,profile:m.profile??D?.ssh?.profile,publicIp:D?.ssh?.publicIp,lan:D?.ssh?.lan})}var Sd=1073741824;function ec(D){D.command("ssh:preflight [host]","Check a bring-your-own Linux host before adopting it").option("--user <user>","SSH user (default: ssh.hosts[].user, then root)").option("--port <port>","SSH port (default: ssh.hosts[].port, then 22)").option("--key <path>","SSH private key (default: ssh.hosts[].privateKeyPath, then ~/.ssh/id_ed25519)").option("--profile <profile>","raspberry-pi or generic (default: ssh.profile, then generic)").option("--json","Print the facts and findings as JSON").action(async(t,m)=>{try{let A=await kp(),_=wd(A,t,m),z=t??_.targetHost(),U=await _.ensureHostKey(z),{facts:Y,findings:X}=await _.preflight(z);if(m.json)console.log(JSON.stringify({schemaVersion:1,host:z,hostKeyFingerprint:U.fingerprint,facts:Y,findings:X},null,2));else{if(i(`Preflight ${z}`),U.pinnedNow)e(`Pinned host key ${U.fingerprint}`);if(e(`${Y.os} on ${Y.arch}, ${Y.cpuCores} cores, ${(Y.memoryBytes/Sd).toFixed(1)} GiB memory, ${(Y.diskFreeBytes/Sd).toFixed(1)} GiB free`),Y.piModel)e(Y.piModel);if(Y.lanIp)e(`LAN address ${Y.lanIp}`);if(console.log(Zr(X)),gr(X))r("The host is not ready; fix the errors above and run the preflight again.");else n("The host is ready to adopt.")}if(gr(X))process.exitCode=1}catch(A){Xa(A)}}),D.command("ssh:bootstrap","Adopt the configured host: pin its key, run the preflight, then bootstrap it").option("--env <environment>","Deployment environment",{default:"production"}).option("--dry-run","Print the bootstrap script instead of running it").action(async(t)=>{try{let m=await Fe(),A=t.env??"production";if(t.dryRun){let U=mr(m),[Y]=U.hosts;console.log(pr({config:m,environment:A,profile:U.profile,sudoUser:Y.user!=="root"?Y.user:void 0,lan:U.lan}));return}let _=wd(m,void 0,{});i(`Adopt ${_.targetHost()}`);let z=await _.provisionComputeInfrastructure({config:m,environment:A});n(`${z.appPublicIp} is bootstrapped; deploy with: cloud deploy --env ${A}`)}catch(m){Xa(m)}}),D.command("ssh:first-boot","Write cloud-init first-boot files for a freshly flashed Raspberry Pi OS or Ubuntu image").option("--os <os>","raspberry-pi-os or ubuntu",{default:"raspberry-pi-os"}).option("--out <dir>","Directory to write user-data, network-config and meta-data into (never a mounted card; copy them yourself)").option("--hostname <name>","Hostname of the new host").option("--user <user>","Deploy user created on first boot (default: ssh.hosts[].user, then pi)").option("--key <path>","Public key authorized for the user",{default:"~/.ssh/id_ed25519.pub"}).option("--wifi-ssid <ssid>","Join this Wi-Fi network on first boot").option("--wifi-passphrase-env <name>","Environment variable holding the Wi-Fi passphrase",{default:"WIFI_PASSWORD"}).option("--wifi-country <cc>","Wi-Fi regulatory domain, e.g. DE").option("--timezone <tz>","IANA timezone, e.g. Europe/Berlin").option("--locale <locale>","System locale, e.g. en_US.UTF-8").option("--env <environment>","Deployment environment the embedded bootstrap is built for",{default:"production"}).option("--force","Overwrite files already in --out").action(async(t)=>{try{if(!t.out)throw Error("--out <dir> is required.");if(!t.hostname)throw Error("--hostname <name> is required.");let m=t.os??"raspberry-pi-os";if(!(m in Zi))throw Error(`--os must be raspberry-pi-os or ubuntu, not '${m}'.`);let A=await Fe(),_=t.env??"production",z=mr(A,{hosts:A.ssh?.hosts?.length?void 0:[{host:t.hostname}],profile:m==="raspberry-pi-os"?"raspberry-pi":void 0}),U=t.user??(A.ssh?.hosts?.[0]?.user??"pi"),Y=Qr(t.key??"~/.ssh/id_ed25519.pub");if(!vd(Y))throw Error(`Public key not found at ${Y}; pass --key <path to a .pub file>.`);let X=Sp(Y,"utf8").trim(),ne;if(t.wifiSsid){let Pe=t.wifiPassphraseEnv??"WIFI_PASSWORD",ye=process.env[Pe];if(!ye)throw Error(`--wifi-ssid needs the passphrase in $${Pe} (set it in the environment, not on the command line).`);if(!t.wifiCountry)throw Error("--wifi-country <cc> is required with --wifi-ssid.");ne={ssid:t.wifiSsid,passphrase:ye,country:t.wifiCountry}}let re=pr({config:A,environment:_,profile:z.profile,sudoUser:U!=="root"?U:void 0,lan:z.lan}),se=eo({hostname:t.hostname,user:U,publicKey:X,timezone:t.timezone,locale:t.locale,wifi:ne},re,{os:m}),he=$p(t.out);wp(he,{recursive:!0});let we=Object.keys(se.files).filter((Pe)=>vd(bd(he,Pe)));if(we.length>0&&!t.force)throw Error(`${we.join(", ")} already exist in ${he}; pass --force to overwrite.`);for(let[Pe,ye]of Object.entries(se.files))Cp(bd(he,Pe),ye,{mode:384});i(`First boot for ${t.hostname}`),n(`Wrote ${Object.keys(se.files).join(", ")} to ${he}`),console.log(se.instructions)}catch(m){Xa(m)}})}async function Rp(){let D=await Fe(),t=y(process.cwd(),D),m=new xn(t.store),A=new _n(m,new ke(t.store));return{controlPlane:t,store:m,service:A}}async function gs(D){let t=await Rp();try{await D(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}var Ks=(D)=>Object.fromEntries(String(D??"").split(",").map((t)=>t.split("=")).filter((t)=>t[0]&&t[1])),Cd=(D)=>Object.fromEntries(Object.entries(Ks(D)).map(([t,m])=>[t,Number(m)]));function tc(D){D.command("pool:list","List capacity pools and reservations").option("--json","Print structured JSON").action(async(t)=>gs((m)=>{let A=m.store.listPools(m.controlPlane.project.id);if(t.json)console.log(JSON.stringify(A,null,2));else c(["Name","Purpose","Backend","Region / arch","Status","Capacity","Concurrency"],A.map((_)=>[_.name,_.purpose,_.backend,`${_.region??"any"} / ${_.architecture??"any"}`,_.status,JSON.stringify(_.capacity),String(_.concurrency)]))})),D.command("pool:create <name>","Create a capacity pool").option("--purpose <purpose>","application, build, worker, monitoring, or backup",{default:"application"}).option("--backend <backend>","server, ecs, or asg",{default:"server"}).option("--capacity <values>","cpu=4,memoryBytes=8589934592,diskBytes=...").option("--region <region>","Required region").option("--arch <architecture>","Required architecture").option("--labels <labels>","Pool key=value labels").option("--selector <labels>","Required server labels").option("--max <count>","Maximum workloads",{default:"10"}).option("--concurrency <count>","Build concurrency",{default:"1"}).action(async(t,m)=>gs((A)=>{let{purpose:_,backend:z}=m;if(!["application","build","worker","monitoring","backup"].includes(_)||!["server","ecs","asg"].includes(z))throw Error("Unsupported pool purpose or backend.");let U=A.store.createPool({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,purpose:_,backend:z,region:m.region,architecture:m.arch,labels:Ks(m.labels),requiredServerLabels:Ks(m.selector),toleratedTaints:[],capacity:{cpu:0,memoryBytes:0,diskBytes:0,gpu:0,...Cd(m.capacity)},maxWorkloads:Number(m.max),costWeight:1,concurrency:Number(m.concurrency),ephemeralWorkspaces:!0,allowProductionSecrets:!1,status:"active"});n(`Created ${U.name} (${U.id}).`)})),D.command("pool:add-server <pool> <server>","Attach an enrolled server to a pool").action(async(t,m)=>gs((A)=>{let _=A.store.listPools(A.controlPlane.project.id).find((U)=>U.id===t||U.name===t),z=new ke(A.controlPlane.store).list(A.controlPlane.project.id).find((U)=>U.id===m||U.name===m);if(!_||!z)throw Error("Pool or server was not found.");A.store.addMember(_.id,z.id),n(`Attached ${z.name} to ${_.name}.`)})),D.command("placement:explain","Explain eligible and rejected placement targets").option("--purpose <purpose>","Workload purpose",{default:"application"}).option("--resources <values>","Requested resources").option("--region <region>","Required region").option("--arch <architecture>","Required architecture").option("--labels <labels>","Required pool labels").option("--json","Print structured JSON").action(async(t)=>gs((m)=>{let A=m.service.explain(m.controlPlane.project.id,{purpose:t.purpose,resources:Cd(t.resources),region:t.region,architecture:t.arch,labels:Ks(t.labels)});if(t.json)console.log(JSON.stringify(A,null,2));else c(["Pool","Server","Eligible","Available","Fit / spread / cost","Reasons"],A.map((_)=>[_.poolName,_.serverId??"provider-managed",_.eligible?"yes":"no",JSON.stringify(_.available),`${_.score.fit.toFixed(2)} / ${_.score.spread} / ${_.score.cost}`,_.reasons.join("; ")||"eligible"]))})),D.command("pool:drain <pool>","Move stateless placements and block stateful placements").action(async(t)=>gs((m)=>{let A=m.store.listPools(m.controlPlane.project.id).find((z)=>z.id===t||z.name===t);if(!A)throw Error("Pool was not found.");let _=m.service.drainPool(A.id);n(`Draining ${A.name}: moved ${_.moved.length}, blocked ${_.blocked.length} stateful placement(s).`)}))}async function Ip(){let D=await Fe(),t=y(process.cwd(),D),m=new On(t.store),A=new $n(m);return{controlPlane:t,store:m,service:A}}async function po(D){let t=await Ip();try{await D(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}var hs=(D,t)=>{let m=D.store.list(D.controlPlane.project.id).find((A)=>A.id===t||A.name===t);if(!m)throw Error("Regional topology was not found.");return m},Pp=(D)=>D.split(",").map((t,m)=>{let[A,_="aws"]=t.split(":");return{region:String(A),provider:_,role:m===0?"primary":"secondary"}}),Dp=(D)=>{let t=new Set(["s3","dynamodb","secrets"]),m=D.split(",").map((_)=>_.trim()),A=m.find((_)=>!t.has(_));if(A)throw Error(`Unsupported replication kind: ${A}`);return m};function nc(D){D.command("region:list","List regional topology, traffic, and replication health").option("--json","Print structured JSON").action(async(t)=>po((m)=>{let A=m.store.list(m.controlPlane.project.id).map((_)=>({topology:_,targets:m.store.targets(_.id),channels:m.store.channels(_.id),route:m.store.route(_.id),executions:m.store.executions(_.id)}));if(t.json)console.log(JSON.stringify(A,null,2));else c(["Name","Hostname","Active","Status","Revision","Replication"],A.map((_)=>[_.topology.name,_.topology.hostname,_.topology.activeRegion,_.topology.status,_.topology.revision??"—",`${_.channels.filter((z)=>z.status==="in_sync").length}/${_.channels.length} in sync`]))})),D.command("region:create <name>","Create a persistent multi-region topology").option("--hostname <hostname>","Traffic hostname").option("--regions <regions>","Comma-separated region[:provider], primary first").option("--replicate <kinds>","s3,dynamodb,secrets",{default:"s3,dynamodb,secrets"}).option("--max-lag <seconds>","Maximum failover lag",{default:"30"}).option("--cdn","Enable CloudFront edge state").option("--waf","Enable WAF edge state").action(async(t,m)=>po((A)=>{if(!m.hostname||!m.regions)throw Error("--hostname and --regions are required.");let _=A.service.create({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,hostname:m.hostname,regions:Pp(m.regions),trafficPolicy:"active_passive",dataPolicy:{replicate:Dp(String(m.replicate)),maxLagSeconds:Number(m.maxLag),retainOnDestroy:!0},dnsProvider:"route53",cdnEnabled:!!m.cdn,wafEnabled:!!m.waf});n(`Created ${_.name} (${_.id}); run region:rollout with an immutable manifest.`)})),D.command("region:plan <name> <operation>","Preview exact regional execution steps").option("--target <region>","Failover or failback region").action(async(t,m,A)=>po((_)=>c(["Step"],_.service.plan(hs(_,t).id,m,A.target).map((z)=>[z])))),D.command("region:rollout <name> <revision>","Queue secondary-first regional stack rollout").option("--manifest <file>","Regional stack manifest JSON").action(async(t,m,A)=>po(async(_)=>{let z=A.manifest?await Bun.file(A.manifest).json():{},U=_.service.enqueue({topologyId:hs(_,t).id,kind:"rollout",revision:m,manifest:z});n(`Regional rollout queued: ${U.operation.id}`)})),D.command("region:failover <name> <region>","Queue health and RPO-gated failover").action(async(t,m)=>po((A)=>{let _=A.service.enqueue({topologyId:hs(A,t).id,kind:"failover",targetRegion:m});n(`Failover queued: ${_.operation.id}`)})),D.command("region:failback <name> <region>","Queue health and RPO-gated failback").action(async(t,m)=>po((A)=>{let _=A.service.enqueue({topologyId:hs(A,t).id,kind:"failback",targetRegion:m});n(`Failback queued: ${_.operation.id}`)})),D.command("region:destroy <name>","Drain traffic and delete regional stacks while retaining data").option("--confirm <name>","Exact topology name").action(async(t,m)=>po((A)=>{let _=A.service.enqueue({topologyId:hs(A,t).id,kind:"destroy",confirmation:m.confirm});n(`Regional teardown queued: ${_.operation.id}; replicated data remains retained.`)}))}var ys="0.16.48";async function Ep(){let D=await Fe(),t=y(process.cwd(),D),m=new Ln(t.store),A=new Mn(m),_=["releases","artifacts","operations","logs","previews","snapshots","orphaned_resources"].map((z)=>new Un(z,t.store,t.project.id,t.organization.id));return{controlPlane:t,store:m,service:A,cleanup:_}}async function Ii(D){let t=await Ep();try{await D(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}function rc(D){D.command("maintenance:list","List updates, campaigns, cleanup plans, and DR drills").option("--json","Print structured JSON").action(async(t)=>Ii((m)=>{let A={manifests:m.store.manifests(m.controlPlane.organization.id),windows:m.store.windows(m.controlPlane.project.id),campaigns:m.store.campaigns(m.controlPlane.project.id),cleanups:m.store.cleanups(m.controlPlane.project.id),drills:m.store.drills(m.controlPlane.project.id)},_=[...A.manifests.map((z)=>["update",z.version,z.compatibility.compatible?"compatible":"blocked",z.compatibility.reasons.join("; ")||z.channel]),...A.campaigns.map((z)=>["upgrade",z.id,z.status,z.currentStage??z.fromVersion]),...A.cleanups.map((z)=>["cleanup",z.kind,z.status,`${z.candidates.length} items / ${z.estimatedBytes} bytes`]),...A.drills.map((z)=>["DR drill",z.scenario,z.status,z.isolatedTarget])];if(t.json)console.log(JSON.stringify(A,null,2));else c(["Area","Name / version","Status","Detail"],_)})),D.command("maintenance:key:add <name> <pem>","Trust an Ed25519 update signing key").action(async(t,m)=>Ii(async(A)=>{let _=A.store.addTrustedKey({organizationId:A.controlPlane.organization.id,name:t,publicKeyPem:await Bun.file(m).text()});n(`Trusted ${_.name}: ${_.fingerprint}`)})),D.command("maintenance:manifest <document> <signature> <key>","Verify and import a signed update manifest").action(async(t,m,A)=>Ii(async(_)=>{let z=await Bun.file(t).json(),U=(await Bun.file(m).text()).trim(),Y=_.store.importManifest({organizationId:_.controlPlane.organization.id,keyId:A,document:z,signature:U,current:{schemaVersion:_.controlPlane.store.health().schemaVersion,currentVersion:ys,platform:process.platform,architecture:process.arch}});n(`Verified ${Y.version}; ${Y.compatibility.compatible?"compatible":"blocked: "+Y.compatibility.reasons.join("; ")}`)})),D.command("maintenance:window <name> <schedule>","Create an approved maintenance window").option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--minutes <minutes>","Duration",{default:"60"}).action(async(t,m,A)=>Ii((_)=>{let z=_.store.createWindow({projectId:_.controlPlane.project.id,name:t,schedule:m,timezone:A.timezone,durationMinutes:Number(A.minutes),allowedOperations:["platform.upgrade"],requireApproval:!0});n(`Created ${z.name} (${z.id}).`)})),D.command("platform:upgrade <manifest> <backup>","Plan and queue a signed staged platform update").option("--strategy <strategy>","canary or rolling",{default:"canary"}).option("--batch <count>","Rolling batch size",{default:"1"}).action(async(t,m,A)=>Ii((_)=>{let z=new ke(_.controlPlane.store).list(_.controlPlane.project.id).filter((X)=>X.status==="ready").map((X)=>({serverId:X.id,target:X.name,previousVersion:X.bootstrapVersion??ys}));if(!z.length)z.push({target:"control-plane",previousVersion:ys});let U=_.store.createCampaign({projectId:_.controlPlane.project.id,manifestId:t,fromVersion:ys,strategy:A.strategy==="rolling"?"rolling":"canary",batchSize:Number(A.batch),healthGate:{kind:"platform"},backupId:m,targets:z}),Y=_.service.enqueueCampaign(U.id);n(`Staged upgrade queued: ${Y.operation.id}`)})),D.command("cleanup:preview <kind>","Discover a bounded cleanup candidate set without deleting").option("--older-than <days>","Minimum age",{default:"30"}).action(async(t,m)=>Ii(async(A)=>{let _=await A.service.previewCleanup({projectId:A.controlPlane.project.id,kind:t,criteria:{olderThanDays:Number(m.olderThan)}},A.cleanup);c(["ID","Candidate","Bytes"],_.candidates.map((z)=>[z.id,z.label,String(z.bytes)])),e(`Apply with: cloud cleanup:apply ${_.id} --confirm "DELETE ${_.candidates.length} ${_.kind}"`)})),D.command("cleanup:apply <plan>","Apply an unchanged cleanup preview").option("--confirm <text>","Exact preview confirmation").action(async(t,m)=>Ii((A)=>{let _=A.service.enqueueCleanup(t,String(m.confirm??""));n(`Cleanup queued: ${_.operation.id}`)})),D.command("dr:plan <scenario> <target>","Plan an isolated disaster-recovery drill").option("--rpo <minutes>","RPO objective",{default:"60"}).option("--rto <minutes>","RTO objective",{default:"30"}).option("--recovery-point <id>","Recovery point").option("--topology <id>","Regional topology").action(async(t,m,A)=>Ii((_)=>{let z=_.store.createDrill({projectId:_.controlPlane.project.id,scenario:t,isolatedTarget:m,expectedRpoMinutes:Number(A.rpo),expectedRtoMinutes:Number(A.rto),recoveryPointId:A.recoveryPoint,topologyId:A.topology});n(`Planned isolated drill ${z.id}.`)})),D.command("dr:run <drill>","Queue an isolated disaster-recovery drill").action(async(t)=>Ii((m)=>{let A=m.service.enqueueDrill(t);n(`DR drill queued: ${A.operation.id}`)}))}async function ic(){try{return(await Fe()).project.region||process.env.AWS_REGION||"us-east-1"}catch{return process.env.AWS_REGION||"us-east-1"}}function oc(D){D.command("function:list","List all Lambda functions").option("--region <region>","AWS region").action(async(t)=>{i("Lambda Functions");let m=t?.region||await ic(),A=new P(m);try{let{Functions:_=[]}=await A.listFunctions({MaxItems:50});if(!_.length){e("No functions found");return}c(["Name","Runtime","Memory","Timeout","Last Modified"],_.map((z)=>[z.FunctionName??"-",z.Runtime??"-",z.MemorySize?`${z.MemorySize} MB`:"-",z.Timeout?`${z.Timeout}s`:"-",z.LastModified??"-"]))}catch(_){r(`Failed to list functions: ${_.message}`),process.exitCode=1}}),D.command("function:invoke <name>","Invoke a Lambda function").option("--payload <json>","Event payload as JSON",{default:"{}"}).option("--async","Fire-and-forget (Event invocation)").option("--region <region>","AWS region").action(async(t,m)=>{i(`Invoking ${t}`);let A=m?.region||await ic(),_=new P(A),z=new s("Invoking function...");z.start();try{let U=await _.invoke({FunctionName:t,InvocationType:m?.async?"Event":"RequestResponse",Payload:m?.payload||"{}",LogType:m?.async?"None":"Tail"});if(U.FunctionError){if(z.fail(`Function returned an error (${U.FunctionError})`),U.Payload)e(U.Payload);process.exitCode=1;return}if(z.succeed(`Invoked (status ${U.StatusCode})`),U.Payload)e(`
|
|
319
319
|
Response:`),e(U.Payload);if(U.LogResult)e(`
|
|
320
320
|
Logs:`),e(Buffer.from(U.LogResult,"base64").toString("utf-8"))}catch(U){z.fail(`Invocation failed: ${U.message}`),process.exitCode=1}}),D.command("function:logs <name>","View recent function logs").option("--tail","Continuously stream new log events").option("--filter <pattern>","CloudWatch filter pattern").option("--since <minutes>","Look back this many minutes",{default:"15"}).option("--region <region>","AWS region").action(async(t,m)=>{i(`Logs for ${t}`);let A=m?.region||await ic(),_=new oe(A),z=t.startsWith("/aws/lambda/")?t:`/aws/lambda/${t}`,U=Number(m?.since??15),Y=(X)=>{for(let ne of X){let re=ne.timestamp?new Date(ne.timestamp).toISOString():"";e(`${re} ${(ne.message??"").trimEnd()}`)}};try{let X=Date.now()-U*60000,{events:ne=[]}=await _.filterLogEvents({logGroupName:z,startTime:X,filterPattern:m?.filter,limit:200});if(Y(ne),ne.length)X=(ne[ne.length-1].timestamp??X)+1;if(m?.tail){e(`
|
|
321
321
|
(streaming — Ctrl+C to stop)`);for(;;){await new Promise((se)=>setTimeout(se,3000));let{events:re=[]}=await _.filterLogEvents({logGroupName:z,startTime:X,filterPattern:m?.filter,limit:200});if(re.length)Y(re),X=(re[re.length-1].timestamp??X)+1}}}catch(X){r(`Failed to read logs: ${X.message}`),process.exitCode=1}})}async function $d(D){let t=Bun.spawn(["docker",...D],{stdin:"inherit",stdout:"inherit",stderr:"pipe"}),[m,A]=await Promise.all([t.exited,new Response(t.stderr).text()]);return{ok:m===0,exitCode:m,command:["docker",...D].join(" "),stderr:A.trim()}}function sc(D){D.command("container:build","Build a Docker image").option("--tag <tag>","Image tag",{default:"latest"}).option("--file <dockerfile>","Dockerfile",{default:"Dockerfile"}).option("--dry-run","Print the exact command").option("--json","Print result JSON").action(async(t)=>{let m=["build","--file",t.file??"Dockerfile","--tag",t.tag??"latest","."];if(t.dryRun){console.log(`docker ${m.join(" ")}`);return}let A=await $d(m);if(t.json)console.log(JSON.stringify(A));if(!A.ok)r(A.stderr||`docker exited ${A.exitCode}`),process.exitCode=A.exitCode;else n(`Built ${t.tag??"latest"}.`)}),D.command("container:push","Push an already-tagged image").option("--image <image>","Full registry image reference").option("--dry-run","Print command").option("--json","Print JSON").action(async(t)=>{if(!t.image){r("--image with a full registry reference is required; credentials must come from docker credential helpers."),process.exitCode=2;return}if(t.dryRun){console.log(`docker push ${t.image}`);return}let m=await $d(["push",t.image]);if(t.json)console.log(JSON.stringify(m));if(!m.ok)r(m.stderr),process.exitCode=m.exitCode;else n(`Pushed ${t.image}.`)}),D.command("container:deploy","Deploy a container image").action(async()=>En("container:deploy",{message:"Direct ECS mutation is not registered as a shared release driver.",nextAction:"Import the image as an application and use the immutable release deployment workflow."}))}function mc(D){D.command("domain:list","List all domains").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(t)=>{i("Domains");try{let m=li(t?.provider),A=pn(t?.provider)?.provider||"unknown",_=new s(`Fetching domains from ${A}...`);_.start();let z=await m.listDomains();if(_.succeed(`Found ${z.length} domain(s)`),z.length===0){e("No domains found in this provider");return}let U=z.map((Y)=>[Y,"Active","-",A.charAt(0).toUpperCase()+A.slice(1)]);c(["Domain","Status","SSL","Provider"],U)}catch(m){r(`Failed to list domains: ${m instanceof Error?m.message:String(m)}`)}}),D.command("domain:add <domain>","Add a new domain").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(t,m)=>{i(`Adding Domain: ${t}`);try{let A=li(m?.provider),_=pn(m?.provider)?.provider||"unknown",z=new s(`Checking if ${_} can manage ${t}...`);if(z.start(),await A.canManageDomain(t))z.succeed(`Domain ${t} is already available in ${_}`),e(`
|
|
@@ -366,13 +366,13 @@ Spend worker stopped; the loop lease was released.`),process.exit(0)};process.on
|
|
|
366
366
|
`)){let A=m.trim();if(!A||A.startsWith("#"))continue;let _=A.indexOf("=");if(_===-1)continue;let z=A.slice(0,_).trim(),U=A.slice(_+1).trim();if(U.startsWith('"')&&U.endsWith('"')||U.startsWith("'")&&U.endsWith("'"))U=U.slice(1,-1);t[z]=U}return t}function sf(D){return Object.entries(D).sort(([m],[A])=>m.localeCompare(A)).map(([m,A])=>{if(!/[\s#"']/.test(A))return`${m}=${A}`;let _='"'+A.replace(/"/g,"\\\"")+'"';return`${m}=${_}`}).join(`
|
|
367
367
|
`)+`
|
|
368
368
|
`}function Fc(D){D.command("env:list","List configured environments and their deployed stack status").action(async()=>{i("Environments");try{let t=await Fe(),m=Object.keys(t.environments??{});if(!m.length){e("No environments configured. Add environments.<env> to your cloud.config.");return}let A=[];for(let _ of m){let z=t.environments[_],U=z?.region||t.project.region||"us-east-1",Y=z?.app?`serverless (${z.app.kind})`:"server/static",X="not deployed";if(z?.app)try{let ne=new v(U),{Stacks:re}=await ne.describeStacks({stackName:xe(t,_)});X=re?.[0]?.StackStatus??"not deployed"}catch{X="not deployed"}A.push([_,z?.type??_,Y,U,X])}c(["Environment","Type","Kind","Region","Stack status"],A)}catch(t){r(`Failed to list environments: ${t.message}`),process.exitCode=1}}),D.command("env:compare <env1> <env2>","Compare two environments' app configuration").action(async(t,m)=>{i(`Comparing ${t} ↔ ${m}`);try{let A=await Fe(),_=A.environments?.[t],z=A.environments?.[m];if(!_||!z){r(`Both environments must exist in config (have: ${Object.keys(A.environments??{}).join(", ")||"none"}).`),process.exitCode=1;return}let U=(se,he="")=>{let we={};for(let[Pe,ye]of Object.entries(se??{})){let Ie=he?`${he}.${Pe}`:Pe;if(ye&&typeof ye==="object"&&!Array.isArray(ye))Object.assign(we,U(ye,Ie));else we[Ie]=Array.isArray(ye)?JSON.stringify(ye):String(ye)}return we},Y=U(_),X=U(z),re=[...new Set([...Object.keys(Y),...Object.keys(X)])].sort().filter((se)=>Y[se]!==X[se]).map((se)=>[se,Y[se]??"—",X[se]??"—"]);if(!re.length){n("Identical — no configuration differences.");return}c(["Setting",t,m],re),e(`
|
|
369
|
-
${re.length} difference(s)`)}catch(A){r(`Comparison failed: ${A.message}`),process.exitCode=1}}),D.command("env:pull","Download a serverless function's environment to a .env file").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).option("--function <which>","Which function: http | queue | cli",{default:"http"}).option("--file <path>","Output file (default .env.<environment>)").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{region:_,functions:z}=bt(m,A),U=t?.function??"http",ne=(await new I(_).getFunction(z[U])).Configuration?.Environment?.Variables??{},re=t?.file??`.env.${A}`;rf(re,sf(ne)),n(`Wrote ${Object.keys(ne).length} variable(s) from ${z[U]} → ${re}`)}catch(m){r(`env:pull failed: ${m.message}`),process.exitCode=1}}),D.command("env:push","Upload a .env file to a serverless function's configuration").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).option("--function <which>","Function: http | queue | cli | all",{default:"all"}).option("--file <path>","Input file (default .env.<environment>)").option("--replace","Replace the entire env instead of merging over the live config").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{region:_,functions:z}=bt(m,A),U=t?.file??`.env.${A}`;if(!tf(U)){r(`File not found: ${U}`),process.exitCode=1;return}let Y=of(nf(U,"utf8")),X=(t?.function??"all").toLowerCase();if(X!=="all"&&!["http","queue","cli"].includes(X)){r("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let ne=X==="all"?["http","queue","cli"]:[X],re=(m.environments?.[A]?.app?.provisionedConcurrency??0)>0,se=new I(_);for(let he of ne){let we=z[he],Pe=Y;if(!t?.replace)Pe={...(await se.getFunction(we)).Configuration?.Environment?.Variables??{},...Y};if(Io(we,Pe),await se.updateFunctionConfiguration({FunctionName:we,Environment:{Variables:Pe}}),await se.waitForFunctionActive(we,120),re){let ye=await se.publishVersion({FunctionName:we});if(ye.Version)await se.updateAlias({FunctionName:we,Name:"live",FunctionVersion:ye.Version})}n(`Pushed ${Object.keys(Y).length} var(s) → ${we}${t?.replace?" (replaced)":" (merged)"}${re?" (published + flipped live alias)":""}`)}e("Note: a subsequent `cloud deploy:serverless` re-injects infra/secret env from config.")}catch(m){r(`env:push failed: ${m.message}`),process.exitCode=1}}),D.command("env:create <name>","Create new environment").action(async(t)=>{Lc(`env:create ${t}`,"Add an `environments.<name>` block to your cloud.config and run `cloud deploy:serverless --env <name>`.")}),D.command("env:switch <name>","Switch active environment").action(async(t)=>{Lc(`env:switch ${t}`,'ts-cloud has no persistent "active" environment — pass --env <name> to each command.')});for(let t of["clone","promote","sync"])D.command(`env:${t} <source> <target>`,`(${t}) environment configuration`).action(async(m,A)=>{Lc(`env:${t} ${m} ${A}`,`Copy the \`environments.${m}\` block to \`environments.${A}\` in cloud.config, then \`cloud deploy:serverless --env ${A}\`.`)});D.command("env:preview <branch>","Create, inspect, rebuild, extend, or destroy an immutable preview").option("--sha <commit>","Exact 40-64 character source commit").option("--pr <number>","Pull request number").option("--site <name>","Application site (defaults to the first site)").option("--env <environment>","Base environment",{default:"production"}).option("--domain <pattern>","HTTPS URL pattern containing {name}").option("--ttl <hours>","Preview lifetime in hours").option("--destroy","Queue teardown for this preview").option("--rebuild","Rebuild the currently recorded commit").option("--extend <hours>","Extend expiry from now/current expiry").option("--get-url","Print the stable preview URL without mutation").option("--yes","Skip destructive confirmation").action(async(t,m)=>{let A;try{let _=await Fe(),z=m.env||"production",U=m.site||Object.keys(_.sites??{})[0];if(!U)throw Error("No application site is configured; pass --site after adding one");A=y(process.cwd(),_);let Y=A.environments.get(z),X=Y?A.store.listResources(A.project.id,Y.id).find((ye)=>ye.kind==="application"&&ye.slug===U):void 0;if(!Y||!X)throw Error(`Application ${U} was not found in base environment ${z}`);let ne=new Xe(A.store),re=ne.previews.getDefinitionForResource(X.id);if(!re){let ye=_.sites?.[U]?.domain,Ie=m.domain||(ye?`https://{name}.preview.${ye}`:void 0);if(!Ie)throw Error("Pass --domain with an HTTPS pattern containing {name} to configure previews");re=ne.previews.createDefinition({projectId:A.project.id,resourceId:X.id,baseEnvironmentId:Y.id,domainPattern:Ie,ttlHours:Number(m.ttl)||void 0})}let se=Number(m.pr)||void 0,he=se?ne.previews.findForPullRequest(re.id,"local",se):ne.previews.findForBranch(re.id,"local",t);if(m.getUrl){if(!he)throw Error("Preview was not found");e(he.url??"URL unavailable");return}if(m.extend){if(!he)throw Error("Preview was not found");let ye=ne.previews.extend(he.id,Number(m.extend));n(`Extended ${ye.name} until ${ye.expiresAt}`);return}if(m.destroy){if(!he)throw Error("Preview was not found");if(!m.yes&&!await u(`Destroy ${he.name} and only its tagged resources?`,!1))return;let ye=ne.enqueueDestroy(he,"manual");n(`Preview teardown queued: ${ye.id}`);return}if(m.rebuild){if(!he)throw Error("Preview was not found");let ye=ne.enqueueDeploy(he,{reason:"manual_rebuild"});n(`Preview rebuild queued: ${ye.id}`);return}if(!m.sha)throw Error("Pass --sha with the exact immutable commit to deploy");let we=ne.previews.upsert({definitionId:re.id,repository:"local",branch:t,pullRequestNumber:se,commitSha:m.sha}),Pe=ne.enqueueDeploy(we.preview,{created:we.created});n(`${we.created?"Preview creation":"Preview update"} queued: ${Pe.id}`),e(`${we.preview.url} · expires ${we.preview.expiresAt}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A?.store.close()}}),D.command("env:cleanup","Remove stale preview environments").option("--dry-run","List cleanup candidates without queuing teardown").option("--max-age <hours>","Also remove previews older than this age").option("--keep <count>","Keep this many newest previews per application").action(async(t)=>{let m;try{let A=await Fe();m=y(process.cwd(),A);let z=new Xe(m.store).cleanup({dryRun:t.dryRun,maxAgeHours:Number(t.maxAge)||void 0,keepCount:Number(t.keep)||void 0});c(["Preview","Status","Expires","Reason"],z.candidates.map((U)=>[U.preview.name,U.preview.status,U.preview.expiresAt,U.reasons.join(", ")])),n(t.dryRun?`${z.candidates.length} cleanup candidate(s)`:`${z.operations.length} teardown job(s) queued`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m?.store.close()}}),D.command("env:previews","List persistent preview environments").option("--site <name>","Filter application site").action(async(t)=>{let m;try{let A=await Fe();m=y(process.cwd(),A);let _=new Xe(m.store),z=t.site?new Set(m.store.listResources(m.project.id).filter((Y)=>Y.slug===t.site).map((Y)=>Y.id)):void 0,U=_.previews.listInstances({projectId:m.project.id}).filter((Y)=>!z||z.has(Y.resourceId));c(["ID","Name","Status","Branch / PR","Commit","URL","Expires","Cost"],U.map((Y)=>[Y.id,Y.name,Y.status,Y.pullRequestNumber?`PR #${Y.pullRequestNumber}`:Y.branch,Y.commitSha.slice(0,12),Y.url??"—",Y.expiresAt,Y.costEstimate==null?"—":`$${Y.costEstimate.toFixed(2)}`]))}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m?.store.close()}})}import{readFileSync as na}from"node:fs";async function Lr(){let D=await Fe(),t=y(process.cwd(),D);return{config:D,controlPlane:t,applications:new gt(t.store)}}async function jc(D,t,m){let A=await Lr();try{let _=A.applications.get(D);if(!_)throw Error("Compose application was not found");let z=t??A.controlPlane.store.listEnvironments(_.projectId).find((Y)=>Y.id===_.environmentId)?.slug;if(!z)throw Error("Compose environment was not found");let U=await or(A.config,z,m(_));if(!U.ok)throw Error(U.stderr||U.error||"Remote Compose command failed");if(U.stdout)e(U.stdout)}finally{A.controlPlane.store.close()}}function Bc(D){D.command("compose:preview <file>","Parse Compose without persistence or provider mutation").option("--name <name>","Application name",{default:"Compose application"}).option("--env <environment>","Target environment",{default:"production"}).action(async(t,m)=>{let A=await Lr();try{let _=A.controlPlane.environments.get(m.env??"production");if(!_)throw Error("Environment was not found");let z=A.applications.preview(na(t,"utf8"),{name:m.name??"Compose application",projectId:A.controlPlane.project.id,environmentId:_.id});if(c(["Severity","Path","Code","Message"],z.diagnostics.map((U)=>[U.severity,U.path,U.code,U.message])),!z.valid)process.exitCode=1;else n(`${Object.keys(z.manifest.spec.services).length} services are valid`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:import <file>","Import and persist a validated Compose application").option("--name <name>","Application name").option("--env <environment>","Target environment",{default:"production"}).action(async(t,m)=>{let A=await Lr();try{let _=A.controlPlane.environments.get(m.env??"production");if(!_)throw Error("Environment was not found");let z=m.name??t.split("/").at(-2)??"Compose application",U=A.applications.import(na(t,"utf8"),{name:z,projectId:A.controlPlane.project.id,environmentId:_.id});n(`Imported ${U.application.name} (${U.application.id})`),e(`${Object.keys(U.application.manifest.spec.services).length} services · ${U.parsed.diagnostics.length} diagnostic(s)`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:templates","List pinned verified Compose templates").action(()=>c(["ID","Version","Category","Architecture","Verified"],hr().map((t)=>[t.id,t.version,t.category,t.architecture,t.lastVerifiedAt]))),D.command("compose:catalog <file>","Validate and inspect a local/custom template catalog").option("--env <environment>","Target environment",{default:"production"}).action(async(t,m)=>{let A=await Lr();try{let _=A.controlPlane.environments.get(m.env??"production");if(!_)throw Error("Environment was not found");let z=yc(na(t,"utf8"),{projectId:A.controlPlane.project.id,environmentId:_.id});if(c(["ID","Version","Category","Services","Checksum"],z.templates.map((U)=>[U.template.id,U.template.version,U.template.category,String(Object.keys(U.parsed.manifest.spec.services).length),U.template.checksum.slice(0,12)])),z.diagnostics.length)c(["Severity","Path","Message"],z.diagnostics.map((U)=>[U.severity,U.path,U.message]));if(!z.valid)process.exitCode=1}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:template <template>","Create an editable application from a template").option("--name <name>","Application name").option("--domain <domain>","Primary domain").option("--env <environment>","Target environment",{default:"production"}).option("--version <version>","Pinned template version").action(async(t,m)=>{let A=await Lr();try{let _=A.controlPlane.environments.get(m.env??"production");if(!_)throw Error("Environment was not found");let z=A.applications.fromTemplate(t,{domain:m.domain??""},{name:m.name??t,projectId:A.controlPlane.project.id,environmentId:_.id,version:m.version});n(`Created ${z.application.name} from ${t}@${z.application.templateVersion}`),e(`Export with cloud compose:export ${z.application.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:list","List Compose applications").action(async()=>{let t=await Lr();try{c(["ID","Name","Status","Services","Source","Updated"],t.applications.list({projectId:t.controlPlane.project.id}).map((m)=>[m.id,m.name,m.status,String(Object.keys(m.manifest.spec.services).length),m.templateId?`${m.templateId}@${m.templateVersion}`:"compose",m.updatedAt]))}finally{t.controlPlane.store.close()}}),D.command("compose:export <application>","Print a normalized editable Compose file").action(async(t)=>{let m=await Lr();try{let A=m.applications.get(t);if(!A)throw Error("Compose application was not found");e(pi(A.manifest))}finally{m.controlPlane.store.close()}}),D.command("compose:diff <application> <file>","Compare persisted and proposed Compose manifests").action(async(t,m)=>{let A=await Lr();try{let _=A.applications.get(t);if(!_)throw Error("Compose application was not found");let z=Ot(na(m,"utf8"),_.manifest.metadata);c(["Path","Before","After"],fs(_.manifest,z.manifest).map((U)=>[U.path,JSON.stringify(U.before),JSON.stringify(U.after)]))}finally{A.controlPlane.store.close()}});for(let t of["deploy","redeploy","start","stop"])D.command(`compose:${t} <application>`,`${t} a Compose application through the durable queue`).action(async(m)=>{let A=await Lr();try{let _=A.applications.get(m);if(!_)throw Error("Compose application was not found");let z=new Je(A.controlPlane.store).enqueue(_,t);n(`${t} queued: ${z.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}});D.command("compose:scale <application> <service> <replicas>","Scale one capable Compose service").action(async(t,m,A)=>{let _=await Lr();try{let z=_.applications.get(t);if(!z)throw Error("Compose application was not found");let U=new Je(_.controlPlane.store).enqueue(z,"scale",{service:m,replicas:Number(A)});n(`Scale queued: ${U.id}`)}catch(z){r(z instanceof Error?z.message:String(z)),process.exitCode=1}finally{_.controlPlane.store.close()}}),D.command("compose:delete <application>","Delete a Compose stack; named volumes are preserved by default").option("--remove-volumes","Also permanently remove named volumes").option("--confirm <text>","Exact application confirmation").action(async(t,m)=>{let A=await Lr();try{let _=A.applications.get(t);if(!_)throw Error("Compose application was not found");let z=new Je(A.controlPlane.store).enqueue(_,"delete",{removeVolumes:m.removeVolumes,confirmation:m.confirm});n(`Deletion queued: ${z.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:logs <application> <service>","Read ordered service logs").option("--lines <count>","Tail line count",{default:"200"}).option("--env <environment>","Target environment").action((t,m,A)=>jc(t,A.env,(_)=>mi(_.manifest,m,Number(A.lines)||200))),D.command("compose:shell <application> <service> [command...]","Run a command in one service").option("--env <environment>","Target environment").action((t,m,A,_)=>jc(t,_.env,(z)=>gi(z.manifest,m,A?.length?A:["sh"]))),D.command("compose:apply <application>","Internal durable Compose runtime action").option("--action <action>","Runtime action").option("--env <environment>","Target environment").option("--service <service>","Service name").option("--replicas <count>","Replica count").option("--remove-volumes","Remove named volumes").action((t,m)=>jc(t,m.env,(A)=>{if(m.action==="scale")return Sc(A.manifest,m.service??"",Number(m.replicas));return bc(A.manifest,m.action??"deploy",{service:m.service,removeVolumes:m.removeVolumes})}))}import{readFileSync as af}from"node:fs";async function Fr(){let D=await Fe(),t=y(process.cwd(),D);return{controlPlane:t,releases:new st(t.store)}}function _c(D){if(!D)return{};return JSON.parse(af(D,"utf8"))}function Mc(D){D.command("release:artifact <uri>","Register a verified content-addressed artifact").option("--digest <sha256>","Required sha256: digest").option("--kind <kind>","Deployable kind").option("--size <bytes>","Artifact size",{default:"0"}).option("--media-type <type>","Artifact media type").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.releases.registerArtifact({organizationId:A.controlPlane.organization.id,digest:String(m.digest??""),kind:m.kind??"compute",uri:t,size:Number(m.size),mediaType:m.mediaType});n(`Artifact registered: ${_.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("release:create <resource> <artifact>","Create an immutable release record").option("--env <environment>","Target environment",{default:"production"}).option("--kind <kind>","Deployable kind").option("--source-sha <sha>","Exact source commit").option("--strategy <strategy>","Activation strategy",{default:"atomic"}).option("--manifest <file>","Immutable manifest JSON").option("--config <file>","Resolved non-secret config JSON").option("--health-path <path>","HTTP health path").option("--approval","Require an environment approval").option("--trigger <trigger>","Build trigger",{default:"manual"}).action(async(t,m,A)=>{let _=await Fr();try{let z=_.controlPlane.environments.get(A.env??"production"),U=z?_.controlPlane.store.listResources(_.controlPlane.project.id,z.id).find((X)=>X.slug===t):void 0;if(!z||!U)throw Error("Release resource was not found");let Y=_.releases.releases.create({organizationId:_.controlPlane.organization.id,projectId:_.controlPlane.project.id,environmentId:z.id,resourceId:U.id,artifactId:m,kind:A.kind??"compute",sourceSha:A.sourceSha,config:_c(A.config),manifest:_c(A.manifest),strategy:A.strategy??"atomic",healthGate:A.healthPath?{protocol:"http",path:A.healthPath,timeoutSeconds:5,intervalSeconds:5,healthyThreshold:2,unhealthyThreshold:2}:void 0,approvalRequired:A.approval,trigger:A.trigger});n(`Release created: ${Y.id} (${Y.status})`)}catch(z){r(z instanceof Error?z.message:String(z)),process.exitCode=1}finally{_.controlPlane.store.close()}}),D.command("release:list","List unified release history").option("--env <environment>","Filter environment").action(async(t)=>{let m=await Fr();try{let A=t.env?m.controlPlane.environments.get(t.env):void 0;c(["ID","Resource","Status","Kind","Artifact","Strategy","Source","Created"],m.releases.releases.list({projectId:m.controlPlane.project.id,environmentId:A?.id}).map((_)=>[_.id,m.controlPlane.store.getResource(_.resourceId)?.slug??_.resourceId,_.status,_.kind,_.artifactId.slice(0,12),_.strategy,_.sourceSha?.slice(0,12)??"—",_.createdAt]))}finally{m.controlPlane.store.close()}}),D.command("release:show <release>","Show provenance, health, approvals, and transitions").action(async(t)=>{let m=await Fr();try{let A=m.releases.releases.get(t);if(!A)throw Error("Release was not found");e(JSON.stringify({release:A,artifact:m.releases.releases.getArtifact(A.artifactId),approvals:m.releases.releases.approvals(t),transitions:m.releases.releases.transitions(t)},null,2))}finally{m.controlPlane.store.close()}}),D.command("release:compare <left> <right>","Compare artifact, source, config, manifest, and data caveats").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.releases.compare(t,m);e(JSON.stringify(_,null,2))}finally{A.controlPlane.store.close()}}),D.command("release:capabilities <kind>","Explain supported deployment strategies").option("--health","Readiness gate is configured").option("--replicas <count>","Replica count",{default:"1"}).action((t,m)=>c(["Strategy","Supported","Capacity","Cost","Explanation","Rollback"],sr({kind:t,hasHealthGate:!!m.health,replicas:Number(m.replicas)}).map((A)=>[A.strategy,A.supported?"yes":"no",`${A.capacityMultiplier}x`,A.costImpact,A.explanation,A.rollback]))),D.command("release:promote <release> <resource>","Promote the same artifact without rebuilding").option("--env <environment>","Target environment").option("--config <file>","Target non-secret config JSON").option("--approval","Require approval").action(async(t,m,A)=>{let _=await Fr();try{let z=_.controlPlane.environments.get(String(A.env??"")),U=z?_.controlPlane.store.listResources(_.controlPlane.project.id,z.id).find((X)=>X.slug===m):void 0;if(!z||!U)throw Error("Promotion target was not found");let Y=_.releases.releases.promote(t,{targetEnvironmentId:z.id,targetResourceId:U.id,config:_c(A.config),approvalRequired:A.approval});n(`Promoted without rebuild: ${Y.id} (${Y.status})`)}catch(z){r(z instanceof Error?z.message:String(z)),process.exitCode=1}finally{_.controlPlane.store.close()}}),D.command("release:approve <release>","Approve or reject an environment gate").option("--actor <id>","Approving actor ID").option("--reject","Reject instead").option("--comment <text>","Approval comment").action(async(t,m)=>{let A=await Fr();try{if(!m.actor)throw Error("--actor is required");let _=A.releases.releases.approve(t,{actorId:m.actor,decision:m.reject?"rejected":"approved",comment:m.comment});n(`Release is ${_.status}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("release:activate <release>","Queue immutable artifact activation").action(async(t)=>{let m=await Fr();try{let A=m.releases.releases.get(t);if(!A)throw Error("Release was not found");let _=m.releases.enqueueActivation(A);n(`Activation queued: ${_.id}`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m.controlPlane.store.close()}}),D.command("release:rollback <release>","Restore the preserved prior immutable release").option("--to <release>","Explicit preserved release ID").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.releases.get(t);if(!_)throw Error("Release was not found");let z=A.releases.enqueueRollback(_,{targetReleaseId:m.to});n(`Rollback queued: ${z.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("release:health <release>","Record activation health and trigger bounded rollback").option("--healthy","Health gate passed").option("--operation <id>","Activation operation ID").option("--message <text>","Health summary").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.completeHealthGate(t,{healthy:!!m.healthy,operationId:m.operation,message:m.message});n(_.rollbackOperation?`Health failed; rollback queued: ${_.rollbackOperation.id}`:`Release is ${_.release.status}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("release:pin <release>","Pin or unpin a release against retention").option("--remove","Remove pin").option("--reason <text>","Pin reason").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.releases.pin(t,!m.remove,m.reason);n(_.pinned?"Release pinned.":"Release unpinned.")}finally{A.controlPlane.store.close()}})}import{existsSync as cf}from"node:fs";function Uc(D){D.command("assets:build","Run the configured build hooks for an environment").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).action(async(t)=>{i("Building Assets");try{let m=await Fe(),A=t?.env||"production",z=m.environments?.[A]?.app?.build??[];if(!z.length){r(`No build hooks defined for '${A}'. Set environments.${A}.app.build = ['bun run build', …] in cloud.config.`),process.exitCode=1;return}let{execSync:U}=await import("node:child_process");for(let Y of z)d(`$ ${Y}`),U(Y,{stdio:"inherit"});n("Build hooks complete")}catch(m){r(`Build failed: ${m.message}`),process.exitCode=1}});for(let t of["assets:optimize:images","images:optimize"])D.command(t,"Optimize images (not implemented)").option("--dir <directory>","Directory to optimize").action(async()=>{r(`'${t}' is not implemented — ts-cloud has no built-in image optimizer.`),e("Optimize images in your own build step (e.g. sharp/squoosh), then `cloud assets:deploy`."),process.exitCode=1});D.command("assets:deploy","Deploy static assets to S3").option("--source <path>","Source directory",{default:"dist"}).option("--bucket <name>","S3 bucket name").option("--prefix <prefix>","S3 prefix/folder").option("--delete","Delete files not in source").option("--cache-control <value>","Cache-Control header",{default:"public, max-age=31536000"}).action(async(t)=>{i("Deploying Assets to S3");try{let A=(await Fe()).project.region||"us-east-1",_=t?.source||"dist",z=t?.bucket,U=t?.prefix,Y=t?.delete||!1,X=t?.cacheControl||"public, max-age=31536000";if(!z){r("--bucket is required");return}if(!cf(_)){r(`Source directory not found: ${_}`);return}if(e(`Source: ${_}`),e(`Bucket: s3://${z}${U?`/${U}`:""}`),e(`Cache-Control: ${X}`),Y)a("Delete mode enabled - files not in source will be removed");if(!await u(`
|
|
369
|
+
${re.length} difference(s)`)}catch(A){r(`Comparison failed: ${A.message}`),process.exitCode=1}}),D.command("env:pull","Download a serverless function's environment to a .env file").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).option("--function <which>","Which function: http | queue | cli",{default:"http"}).option("--file <path>","Output file (default .env.<environment>)").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{region:_,functions:z}=bt(m,A),U=t?.function??"http",ne=(await new P(_).getFunction(z[U])).Configuration?.Environment?.Variables??{},re=t?.file??`.env.${A}`;rf(re,sf(ne)),n(`Wrote ${Object.keys(ne).length} variable(s) from ${z[U]} → ${re}`)}catch(m){r(`env:pull failed: ${m.message}`),process.exitCode=1}}),D.command("env:push","Upload a .env file to a serverless function's configuration").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).option("--function <which>","Function: http | queue | cli | all",{default:"all"}).option("--file <path>","Input file (default .env.<environment>)").option("--replace","Replace the entire env instead of merging over the live config").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{region:_,functions:z}=bt(m,A),U=t?.file??`.env.${A}`;if(!tf(U)){r(`File not found: ${U}`),process.exitCode=1;return}let Y=of(nf(U,"utf8")),X=(t?.function??"all").toLowerCase();if(X!=="all"&&!["http","queue","cli"].includes(X)){r("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let ne=X==="all"?["http","queue","cli"]:[X],re=(m.environments?.[A]?.app?.provisionedConcurrency??0)>0,se=new P(_);for(let he of ne){let we=z[he],Pe=Y;if(!t?.replace)Pe={...(await se.getFunction(we)).Configuration?.Environment?.Variables??{},...Y};if(Io(we,Pe),await se.updateFunctionConfiguration({FunctionName:we,Environment:{Variables:Pe}}),await se.waitForFunctionActive(we,120),re){let ye=await se.publishVersion({FunctionName:we});if(ye.Version)await se.updateAlias({FunctionName:we,Name:"live",FunctionVersion:ye.Version})}n(`Pushed ${Object.keys(Y).length} var(s) → ${we}${t?.replace?" (replaced)":" (merged)"}${re?" (published + flipped live alias)":""}`)}e("Note: a subsequent `cloud deploy:serverless` re-injects infra/secret env from config.")}catch(m){r(`env:push failed: ${m.message}`),process.exitCode=1}}),D.command("env:create <name>","Create new environment").action(async(t)=>{Lc(`env:create ${t}`,"Add an `environments.<name>` block to your cloud.config and run `cloud deploy:serverless --env <name>`.")}),D.command("env:switch <name>","Switch active environment").action(async(t)=>{Lc(`env:switch ${t}`,'ts-cloud has no persistent "active" environment — pass --env <name> to each command.')});for(let t of["clone","promote","sync"])D.command(`env:${t} <source> <target>`,`(${t}) environment configuration`).action(async(m,A)=>{Lc(`env:${t} ${m} ${A}`,`Copy the \`environments.${m}\` block to \`environments.${A}\` in cloud.config, then \`cloud deploy:serverless --env ${A}\`.`)});D.command("env:preview <branch>","Create, inspect, rebuild, extend, or destroy an immutable preview").option("--sha <commit>","Exact 40-64 character source commit").option("--pr <number>","Pull request number").option("--site <name>","Application site (defaults to the first site)").option("--env <environment>","Base environment",{default:"production"}).option("--domain <pattern>","HTTPS URL pattern containing {name}").option("--ttl <hours>","Preview lifetime in hours").option("--destroy","Queue teardown for this preview").option("--rebuild","Rebuild the currently recorded commit").option("--extend <hours>","Extend expiry from now/current expiry").option("--get-url","Print the stable preview URL without mutation").option("--yes","Skip destructive confirmation").action(async(t,m)=>{let A;try{let _=await Fe(),z=m.env||"production",U=m.site||Object.keys(_.sites??{})[0];if(!U)throw Error("No application site is configured; pass --site after adding one");A=y(process.cwd(),_);let Y=A.environments.get(z),X=Y?A.store.listResources(A.project.id,Y.id).find((ye)=>ye.kind==="application"&&ye.slug===U):void 0;if(!Y||!X)throw Error(`Application ${U} was not found in base environment ${z}`);let ne=new Xe(A.store),re=ne.previews.getDefinitionForResource(X.id);if(!re){let ye=_.sites?.[U]?.domain,Ie=m.domain||(ye?`https://{name}.preview.${ye}`:void 0);if(!Ie)throw Error("Pass --domain with an HTTPS pattern containing {name} to configure previews");re=ne.previews.createDefinition({projectId:A.project.id,resourceId:X.id,baseEnvironmentId:Y.id,domainPattern:Ie,ttlHours:Number(m.ttl)||void 0})}let se=Number(m.pr)||void 0,he=se?ne.previews.findForPullRequest(re.id,"local",se):ne.previews.findForBranch(re.id,"local",t);if(m.getUrl){if(!he)throw Error("Preview was not found");e(he.url??"URL unavailable");return}if(m.extend){if(!he)throw Error("Preview was not found");let ye=ne.previews.extend(he.id,Number(m.extend));n(`Extended ${ye.name} until ${ye.expiresAt}`);return}if(m.destroy){if(!he)throw Error("Preview was not found");if(!m.yes&&!await u(`Destroy ${he.name} and only its tagged resources?`,!1))return;let ye=ne.enqueueDestroy(he,"manual");n(`Preview teardown queued: ${ye.id}`);return}if(m.rebuild){if(!he)throw Error("Preview was not found");let ye=ne.enqueueDeploy(he,{reason:"manual_rebuild"});n(`Preview rebuild queued: ${ye.id}`);return}if(!m.sha)throw Error("Pass --sha with the exact immutable commit to deploy");let we=ne.previews.upsert({definitionId:re.id,repository:"local",branch:t,pullRequestNumber:se,commitSha:m.sha}),Pe=ne.enqueueDeploy(we.preview,{created:we.created});n(`${we.created?"Preview creation":"Preview update"} queued: ${Pe.id}`),e(`${we.preview.url} · expires ${we.preview.expiresAt}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A?.store.close()}}),D.command("env:cleanup","Remove stale preview environments").option("--dry-run","List cleanup candidates without queuing teardown").option("--max-age <hours>","Also remove previews older than this age").option("--keep <count>","Keep this many newest previews per application").action(async(t)=>{let m;try{let A=await Fe();m=y(process.cwd(),A);let z=new Xe(m.store).cleanup({dryRun:t.dryRun,maxAgeHours:Number(t.maxAge)||void 0,keepCount:Number(t.keep)||void 0});c(["Preview","Status","Expires","Reason"],z.candidates.map((U)=>[U.preview.name,U.preview.status,U.preview.expiresAt,U.reasons.join(", ")])),n(t.dryRun?`${z.candidates.length} cleanup candidate(s)`:`${z.operations.length} teardown job(s) queued`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m?.store.close()}}),D.command("env:previews","List persistent preview environments").option("--site <name>","Filter application site").action(async(t)=>{let m;try{let A=await Fe();m=y(process.cwd(),A);let _=new Xe(m.store),z=t.site?new Set(m.store.listResources(m.project.id).filter((Y)=>Y.slug===t.site).map((Y)=>Y.id)):void 0,U=_.previews.listInstances({projectId:m.project.id}).filter((Y)=>!z||z.has(Y.resourceId));c(["ID","Name","Status","Branch / PR","Commit","URL","Expires","Cost"],U.map((Y)=>[Y.id,Y.name,Y.status,Y.pullRequestNumber?`PR #${Y.pullRequestNumber}`:Y.branch,Y.commitSha.slice(0,12),Y.url??"—",Y.expiresAt,Y.costEstimate==null?"—":`$${Y.costEstimate.toFixed(2)}`]))}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m?.store.close()}})}import{readFileSync as na}from"node:fs";async function Lr(){let D=await Fe(),t=y(process.cwd(),D);return{config:D,controlPlane:t,applications:new gt(t.store)}}async function jc(D,t,m){let A=await Lr();try{let _=A.applications.get(D);if(!_)throw Error("Compose application was not found");let z=t??A.controlPlane.store.listEnvironments(_.projectId).find((Y)=>Y.id===_.environmentId)?.slug;if(!z)throw Error("Compose environment was not found");let U=await or(A.config,z,m(_));if(!U.ok)throw Error(U.stderr||U.error||"Remote Compose command failed");if(U.stdout)e(U.stdout)}finally{A.controlPlane.store.close()}}function Bc(D){D.command("compose:preview <file>","Parse Compose without persistence or provider mutation").option("--name <name>","Application name",{default:"Compose application"}).option("--env <environment>","Target environment",{default:"production"}).action(async(t,m)=>{let A=await Lr();try{let _=A.controlPlane.environments.get(m.env??"production");if(!_)throw Error("Environment was not found");let z=A.applications.preview(na(t,"utf8"),{name:m.name??"Compose application",projectId:A.controlPlane.project.id,environmentId:_.id});if(c(["Severity","Path","Code","Message"],z.diagnostics.map((U)=>[U.severity,U.path,U.code,U.message])),!z.valid)process.exitCode=1;else n(`${Object.keys(z.manifest.spec.services).length} services are valid`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:import <file>","Import and persist a validated Compose application").option("--name <name>","Application name").option("--env <environment>","Target environment",{default:"production"}).action(async(t,m)=>{let A=await Lr();try{let _=A.controlPlane.environments.get(m.env??"production");if(!_)throw Error("Environment was not found");let z=m.name??t.split("/").at(-2)??"Compose application",U=A.applications.import(na(t,"utf8"),{name:z,projectId:A.controlPlane.project.id,environmentId:_.id});n(`Imported ${U.application.name} (${U.application.id})`),e(`${Object.keys(U.application.manifest.spec.services).length} services · ${U.parsed.diagnostics.length} diagnostic(s)`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:templates","List pinned verified Compose templates").action(()=>c(["ID","Version","Category","Architecture","Verified"],hr().map((t)=>[t.id,t.version,t.category,t.architecture,t.lastVerifiedAt]))),D.command("compose:catalog <file>","Validate and inspect a local/custom template catalog").option("--env <environment>","Target environment",{default:"production"}).action(async(t,m)=>{let A=await Lr();try{let _=A.controlPlane.environments.get(m.env??"production");if(!_)throw Error("Environment was not found");let z=yc(na(t,"utf8"),{projectId:A.controlPlane.project.id,environmentId:_.id});if(c(["ID","Version","Category","Services","Checksum"],z.templates.map((U)=>[U.template.id,U.template.version,U.template.category,String(Object.keys(U.parsed.manifest.spec.services).length),U.template.checksum.slice(0,12)])),z.diagnostics.length)c(["Severity","Path","Message"],z.diagnostics.map((U)=>[U.severity,U.path,U.message]));if(!z.valid)process.exitCode=1}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:template <template>","Create an editable application from a template").option("--name <name>","Application name").option("--domain <domain>","Primary domain").option("--env <environment>","Target environment",{default:"production"}).option("--version <version>","Pinned template version").action(async(t,m)=>{let A=await Lr();try{let _=A.controlPlane.environments.get(m.env??"production");if(!_)throw Error("Environment was not found");let z=A.applications.fromTemplate(t,{domain:m.domain??""},{name:m.name??t,projectId:A.controlPlane.project.id,environmentId:_.id,version:m.version});n(`Created ${z.application.name} from ${t}@${z.application.templateVersion}`),e(`Export with cloud compose:export ${z.application.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:list","List Compose applications").action(async()=>{let t=await Lr();try{c(["ID","Name","Status","Services","Source","Updated"],t.applications.list({projectId:t.controlPlane.project.id}).map((m)=>[m.id,m.name,m.status,String(Object.keys(m.manifest.spec.services).length),m.templateId?`${m.templateId}@${m.templateVersion}`:"compose",m.updatedAt]))}finally{t.controlPlane.store.close()}}),D.command("compose:export <application>","Print a normalized editable Compose file").action(async(t)=>{let m=await Lr();try{let A=m.applications.get(t);if(!A)throw Error("Compose application was not found");e(pi(A.manifest))}finally{m.controlPlane.store.close()}}),D.command("compose:diff <application> <file>","Compare persisted and proposed Compose manifests").action(async(t,m)=>{let A=await Lr();try{let _=A.applications.get(t);if(!_)throw Error("Compose application was not found");let z=Ot(na(m,"utf8"),_.manifest.metadata);c(["Path","Before","After"],fs(_.manifest,z.manifest).map((U)=>[U.path,JSON.stringify(U.before),JSON.stringify(U.after)]))}finally{A.controlPlane.store.close()}});for(let t of["deploy","redeploy","start","stop"])D.command(`compose:${t} <application>`,`${t} a Compose application through the durable queue`).action(async(m)=>{let A=await Lr();try{let _=A.applications.get(m);if(!_)throw Error("Compose application was not found");let z=new Je(A.controlPlane.store).enqueue(_,t);n(`${t} queued: ${z.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}});D.command("compose:scale <application> <service> <replicas>","Scale one capable Compose service").action(async(t,m,A)=>{let _=await Lr();try{let z=_.applications.get(t);if(!z)throw Error("Compose application was not found");let U=new Je(_.controlPlane.store).enqueue(z,"scale",{service:m,replicas:Number(A)});n(`Scale queued: ${U.id}`)}catch(z){r(z instanceof Error?z.message:String(z)),process.exitCode=1}finally{_.controlPlane.store.close()}}),D.command("compose:delete <application>","Delete a Compose stack; named volumes are preserved by default").option("--remove-volumes","Also permanently remove named volumes").option("--confirm <text>","Exact application confirmation").action(async(t,m)=>{let A=await Lr();try{let _=A.applications.get(t);if(!_)throw Error("Compose application was not found");let z=new Je(A.controlPlane.store).enqueue(_,"delete",{removeVolumes:m.removeVolumes,confirmation:m.confirm});n(`Deletion queued: ${z.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("compose:logs <application> <service>","Read ordered service logs").option("--lines <count>","Tail line count",{default:"200"}).option("--env <environment>","Target environment").action((t,m,A)=>jc(t,A.env,(_)=>mi(_.manifest,m,Number(A.lines)||200))),D.command("compose:shell <application> <service> [command...]","Run a command in one service").option("--env <environment>","Target environment").action((t,m,A,_)=>jc(t,_.env,(z)=>gi(z.manifest,m,A?.length?A:["sh"]))),D.command("compose:apply <application>","Internal durable Compose runtime action").option("--action <action>","Runtime action").option("--env <environment>","Target environment").option("--service <service>","Service name").option("--replicas <count>","Replica count").option("--remove-volumes","Remove named volumes").action((t,m)=>jc(t,m.env,(A)=>{if(m.action==="scale")return Sc(A.manifest,m.service??"",Number(m.replicas));return bc(A.manifest,m.action??"deploy",{service:m.service,removeVolumes:m.removeVolumes})}))}import{readFileSync as af}from"node:fs";async function Fr(){let D=await Fe(),t=y(process.cwd(),D);return{controlPlane:t,releases:new st(t.store)}}function _c(D){if(!D)return{};return JSON.parse(af(D,"utf8"))}function Mc(D){D.command("release:artifact <uri>","Register a verified content-addressed artifact").option("--digest <sha256>","Required sha256: digest").option("--kind <kind>","Deployable kind").option("--size <bytes>","Artifact size",{default:"0"}).option("--media-type <type>","Artifact media type").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.releases.registerArtifact({organizationId:A.controlPlane.organization.id,digest:String(m.digest??""),kind:m.kind??"compute",uri:t,size:Number(m.size),mediaType:m.mediaType});n(`Artifact registered: ${_.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("release:create <resource> <artifact>","Create an immutable release record").option("--env <environment>","Target environment",{default:"production"}).option("--kind <kind>","Deployable kind").option("--source-sha <sha>","Exact source commit").option("--strategy <strategy>","Activation strategy",{default:"atomic"}).option("--manifest <file>","Immutable manifest JSON").option("--config <file>","Resolved non-secret config JSON").option("--health-path <path>","HTTP health path").option("--approval","Require an environment approval").option("--trigger <trigger>","Build trigger",{default:"manual"}).action(async(t,m,A)=>{let _=await Fr();try{let z=_.controlPlane.environments.get(A.env??"production"),U=z?_.controlPlane.store.listResources(_.controlPlane.project.id,z.id).find((X)=>X.slug===t):void 0;if(!z||!U)throw Error("Release resource was not found");let Y=_.releases.releases.create({organizationId:_.controlPlane.organization.id,projectId:_.controlPlane.project.id,environmentId:z.id,resourceId:U.id,artifactId:m,kind:A.kind??"compute",sourceSha:A.sourceSha,config:_c(A.config),manifest:_c(A.manifest),strategy:A.strategy??"atomic",healthGate:A.healthPath?{protocol:"http",path:A.healthPath,timeoutSeconds:5,intervalSeconds:5,healthyThreshold:2,unhealthyThreshold:2}:void 0,approvalRequired:A.approval,trigger:A.trigger});n(`Release created: ${Y.id} (${Y.status})`)}catch(z){r(z instanceof Error?z.message:String(z)),process.exitCode=1}finally{_.controlPlane.store.close()}}),D.command("release:list","List unified release history").option("--env <environment>","Filter environment").action(async(t)=>{let m=await Fr();try{let A=t.env?m.controlPlane.environments.get(t.env):void 0;c(["ID","Resource","Status","Kind","Artifact","Strategy","Source","Created"],m.releases.releases.list({projectId:m.controlPlane.project.id,environmentId:A?.id}).map((_)=>[_.id,m.controlPlane.store.getResource(_.resourceId)?.slug??_.resourceId,_.status,_.kind,_.artifactId.slice(0,12),_.strategy,_.sourceSha?.slice(0,12)??"—",_.createdAt]))}finally{m.controlPlane.store.close()}}),D.command("release:show <release>","Show provenance, health, approvals, and transitions").action(async(t)=>{let m=await Fr();try{let A=m.releases.releases.get(t);if(!A)throw Error("Release was not found");e(JSON.stringify({release:A,artifact:m.releases.releases.getArtifact(A.artifactId),approvals:m.releases.releases.approvals(t),transitions:m.releases.releases.transitions(t)},null,2))}finally{m.controlPlane.store.close()}}),D.command("release:compare <left> <right>","Compare artifact, source, config, manifest, and data caveats").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.releases.compare(t,m);e(JSON.stringify(_,null,2))}finally{A.controlPlane.store.close()}}),D.command("release:capabilities <kind>","Explain supported deployment strategies").option("--health","Readiness gate is configured").option("--replicas <count>","Replica count",{default:"1"}).action((t,m)=>c(["Strategy","Supported","Capacity","Cost","Explanation","Rollback"],sr({kind:t,hasHealthGate:!!m.health,replicas:Number(m.replicas)}).map((A)=>[A.strategy,A.supported?"yes":"no",`${A.capacityMultiplier}x`,A.costImpact,A.explanation,A.rollback]))),D.command("release:promote <release> <resource>","Promote the same artifact without rebuilding").option("--env <environment>","Target environment").option("--config <file>","Target non-secret config JSON").option("--approval","Require approval").action(async(t,m,A)=>{let _=await Fr();try{let z=_.controlPlane.environments.get(String(A.env??"")),U=z?_.controlPlane.store.listResources(_.controlPlane.project.id,z.id).find((X)=>X.slug===m):void 0;if(!z||!U)throw Error("Promotion target was not found");let Y=_.releases.releases.promote(t,{targetEnvironmentId:z.id,targetResourceId:U.id,config:_c(A.config),approvalRequired:A.approval});n(`Promoted without rebuild: ${Y.id} (${Y.status})`)}catch(z){r(z instanceof Error?z.message:String(z)),process.exitCode=1}finally{_.controlPlane.store.close()}}),D.command("release:approve <release>","Approve or reject an environment gate").option("--actor <id>","Approving actor ID").option("--reject","Reject instead").option("--comment <text>","Approval comment").action(async(t,m)=>{let A=await Fr();try{if(!m.actor)throw Error("--actor is required");let _=A.releases.releases.approve(t,{actorId:m.actor,decision:m.reject?"rejected":"approved",comment:m.comment});n(`Release is ${_.status}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("release:activate <release>","Queue immutable artifact activation").action(async(t)=>{let m=await Fr();try{let A=m.releases.releases.get(t);if(!A)throw Error("Release was not found");let _=m.releases.enqueueActivation(A);n(`Activation queued: ${_.id}`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m.controlPlane.store.close()}}),D.command("release:rollback <release>","Restore the preserved prior immutable release").option("--to <release>","Explicit preserved release ID").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.releases.get(t);if(!_)throw Error("Release was not found");let z=A.releases.enqueueRollback(_,{targetReleaseId:m.to});n(`Rollback queued: ${z.id}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("release:health <release>","Record activation health and trigger bounded rollback").option("--healthy","Health gate passed").option("--operation <id>","Activation operation ID").option("--message <text>","Health summary").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.completeHealthGate(t,{healthy:!!m.healthy,operationId:m.operation,message:m.message});n(_.rollbackOperation?`Health failed; rollback queued: ${_.rollbackOperation.id}`:`Release is ${_.release.status}`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}finally{A.controlPlane.store.close()}}),D.command("release:pin <release>","Pin or unpin a release against retention").option("--remove","Remove pin").option("--reason <text>","Pin reason").action(async(t,m)=>{let A=await Fr();try{let _=A.releases.releases.pin(t,!m.remove,m.reason);n(_.pinned?"Release pinned.":"Release unpinned.")}finally{A.controlPlane.store.close()}})}import{existsSync as cf}from"node:fs";function Uc(D){D.command("assets:build","Run the configured build hooks for an environment").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).action(async(t)=>{i("Building Assets");try{let m=await Fe(),A=t?.env||"production",z=m.environments?.[A]?.app?.build??[];if(!z.length){r(`No build hooks defined for '${A}'. Set environments.${A}.app.build = ['bun run build', …] in cloud.config.`),process.exitCode=1;return}let{execSync:U}=await import("node:child_process");for(let Y of z)d(`$ ${Y}`),U(Y,{stdio:"inherit"});n("Build hooks complete")}catch(m){r(`Build failed: ${m.message}`),process.exitCode=1}});for(let t of["assets:optimize:images","images:optimize"])D.command(t,"Optimize images (not implemented)").option("--dir <directory>","Directory to optimize").action(async()=>{r(`'${t}' is not implemented — ts-cloud has no built-in image optimizer.`),e("Optimize images in your own build step (e.g. sharp/squoosh), then `cloud assets:deploy`."),process.exitCode=1});D.command("assets:deploy","Deploy static assets to S3").option("--source <path>","Source directory",{default:"dist"}).option("--bucket <name>","S3 bucket name").option("--prefix <prefix>","S3 prefix/folder").option("--delete","Delete files not in source").option("--cache-control <value>","Cache-Control header",{default:"public, max-age=31536000"}).action(async(t)=>{i("Deploying Assets to S3");try{let A=(await Fe()).project.region||"us-east-1",_=t?.source||"dist",z=t?.bucket,U=t?.prefix,Y=t?.delete||!1,X=t?.cacheControl||"public, max-age=31536000";if(!z){r("--bucket is required");return}if(!cf(_)){r(`Source directory not found: ${_}`);return}if(e(`Source: ${_}`),e(`Bucket: s3://${z}${U?`/${U}`:""}`),e(`Cache-Control: ${X}`),Y)a("Delete mode enabled - files not in source will be removed");if(!await u(`
|
|
370
370
|
Deploy assets now?`,!0)){e("Deployment cancelled");return}let re=new g(A),se=new s("Uploading assets to S3...");se.start(),await re.sync({source:_,bucket:z,prefix:U,delete:Y,cacheControl:X,acl:"public-read"}),se.succeed("Assets deployed successfully!");let we=(await re.getBucketSize(z,U)/1024/1024).toFixed(2);n(`
|
|
371
371
|
Deployment complete!`),e(`Total size: ${we} MB`),e(`
|
|
372
372
|
Assets URL: https://${z}.s3.${A}.amazonaws.com${U?`/${U}`:""}`)}catch(m){r(`Deployment failed: ${m.message}`)}}),D.command("assets:invalidate","Invalidate CloudFront cache").option("--distribution <id>","CloudFront distribution ID").option("--paths <paths>","Paths to invalidate (comma-separated)",{default:"/*"}).option("--wait","Wait for invalidation to complete").action(async(t)=>{i("Invalidating CloudFront Cache");try{let m=t?.distribution;if(!m){r("--distribution is required");return}let _=(t?.paths||"/*").split(",").map((re)=>re.trim()),z=t?.wait||!1;if(e(`Distribution: ${m}`),e(`Paths: ${_.join(", ")}`),!await u(`
|
|
373
373
|
Invalidate cache now?`,!0)){e("Invalidation cancelled");return}let Y=new E,X=new s("Creating invalidation...");X.start();let ne=await Y.invalidatePaths(m,_);if(X.succeed("Invalidation created"),n(`
|
|
374
374
|
Invalidation ID: ${ne.Id}`),e(`Status: ${ne.Status}`),e(`Created: ${new Date(ne.CreateTime).toLocaleString()}`),z){let re=new s("Waiting for invalidation to complete...");re.start(),await Y.waitForInvalidation(m,ne.Id),re.succeed("Invalidation completed!")}else e(`
|
|
375
|
-
Invalidation is in progress. Use --wait to wait for completion.`)}catch(m){r(`Invalidation failed: ${m.message}`)}})}function Wc(D){for(let[t,m]of[["team:add <email> <role>","Invite an organization member"],["team:list","List organization members"],["team:remove <email>","Revoke organization membership"]])D.command(t,m).action(async(...A)=>En(t.split(" ")[0],{target:String(A[0]??""),message:"Legacy IAM-backed team commands are deprecated and cannot safely represent organization-scoped access.",nextAction:"Use the Team dashboard or organization invitation API; this command exits without changing IAM."}))}import{execFileSync as Gf,execSync as cl}from"node:child_process";import{copyFileSync as xs,existsSync as vn,mkdirSync as iu,readFileSync as al,writeFileSync as Hf}from"node:fs";import{tmpdir as ou}from"node:os";import{dirname as Kf,join as su,resolve as Jf}from"node:path";import{existsSync as qc,readdirSync as zd,statSync as Gc}from"node:fs";import{join as ra}from"node:path";var lf=new Set([".DS_Store","Thumbs.db",".gitkeep",".keep"]);function Ud(D){return lf.has(D)}function Wd(D,t=0){if(t>8)return!0;let m;try{m=zd(D)}catch{return!1}for(let A of m){if(Ud(A))continue;let _=ra(D,A),z;try{z=Gc(_)}catch{continue}if(z.isFile())return!0;if(z.isDirectory()&&Wd(_,t+1))return!0}return!1}function df(D){let t;try{t=zd(D)}catch{return[]}let m=[];for(let A of t){if(Ud(A))continue;let _=ra(D,A);try{if(Gc(_).isDirectory()&&qc(ra(_,"index.html")))m.push(A)}catch{}}return m.sort((A,_)=>A.localeCompare(_))}function Vd(D){let{root:t,siteName:m,expectIndex:A}=D,_=[];if(!qc(t))return _.push({level:"error",message:`Build output not found at ${t} for site '${m}'`}),_;if(!Gc(t).isDirectory())return _.push({level:"error",message:`Site '${m}' has root ${t}, which is a file, not a directory`}),_;if(!Wd(t))return _.push({level:"error",message:`Site '${m}' would ship an empty release: ${t} contains no files. Check that the build ran and wrote where 'root' points.`}),_;if(!A)return _;if(qc(ra(t,"index.html")))return _;let z=df(t);if(z.length===1){let U=`${t.replace(/\/+$/,"")}/${z[0]}`;return _.push({level:"error",message:`Site '${m}' has no index.html at the top of ${t}, but ${U} has one. The generator wrote one level down, so this release would serve 404s at the domain root. Set root: '${U}'.`}),_}if(z.length>1)return _.push({level:"warning",message:`Site '${m}' has no index.html at the top of ${t}. These subdirectories have one: ${z.join(", ")}. The domain root will 404.`}),_;return _.push({level:"warning",message:`Site '${m}' has no index.html anywhere under ${t}. The domain root will 404.`}),_}function uf(D){if(!D)return[];if(Array.isArray(D))return D.map(String);let t=D,m=t.Items??t.Item;if(!m)return[];if(Array.isArray(m))return m.map(String);if(typeof m==="object"&&m!==null){let A=m;if("Item"in A){let _=A.Item;if(!_)return[];return Array.isArray(_)?_.map(String):[String(_)]}if("CNAME"in A){let _=A.CNAME;if(!_)return[];return Array.isArray(_)?_.map(String):[String(_)]}return Object.values(A).flatMap((_)=>{if(Array.isArray(_))return _.map(String);return typeof _==="string"?[_]:[]})}return[String(m)]}async function ia(D){if(D.length===0)return;let t=new E,m=await t.listDistributions(),A=[...new Set(D.map((_)=>_.toLowerCase()))];for(let _ of A){let z=m.find((Y)=>uf(Y.Aliases).some((ne)=>ne.toLowerCase()===_));if(!z?.Id){a(`No CloudFront distribution found for ${_} — skipping dynamic HTTP method sync`);continue}if(await t.ensureDynamicHttpMethods(z.Id))n(`CloudFront ${z.Id} (${_}): enabled POST/PUT/PATCH/DELETE on dynamic paths`);else e(`CloudFront ${z.Id} (${_}): already allows dynamic HTTP methods`)}}import{execSync as mf}from"node:child_process";async function pf(D,t,m,A={},_=process.cwd()){if(!D)return;if(A.step?.(`Running ${m} hook`),typeof D==="string"){mf(D,{stdio:"inherit",cwd:_});return}await D(t)}async function Cs(D,t,m={},A){try{return await pf(D.hooks?.[t],D,t,m,A),!0}catch(_){return m.error?.(`${t} hook failed: ${_?.message||_}`),!1}}var wi=(D)=>D===void 0?void 0:D?"on":"off";function Hc(D){let t={ssl:D.ssl,always_use_https:wi(D.alwaysUseHttps),automatic_https_rewrites:wi(D.automaticHttpsRewrites),min_tls_version:D.minTlsVersion,tls_1_3:wi(D.tls13),brotli:wi(D.brotli),http3:wi(D.http3),"0rtt":wi(D.zeroRtt),early_hints:wi(D.earlyHints),browser_cache_ttl:D.browserCacheTtl,always_online:wi(D.alwaysOnline),websockets:wi(D.websockets),email_obfuscation:wi(D.emailObfuscation)};if(D.hsts)t.security_header={strict_transport_security:{enabled:D.hsts.enabled,max_age:D.hsts.maxAge??31536000,include_subdomains:D.hsts.includeSubdomains??!1,preload:D.hsts.preload??!1,nosniff:D.hsts.noSniff??!0}};for(let m of Object.keys(t))if(t[m]===void 0)delete t[m];return{...t,...D.raw??{}}}var $s={ssl:"strict",alwaysUseHttps:!0,automaticHttpsRewrites:!0,minTlsVersion:"1.2",tls13:!0,brotli:!0,http3:!0,earlyHints:!0,alwaysOnline:!0,emailObfuscation:!1,browserCacheTtl:0};import{connect as ff}from"node:tls";async function Kc(D){let{address:t,serverName:m,port:A=443,timeoutMs:_=5000}=D;return new Promise((z)=>{let U=!1,Y=(ne)=>{if(U)return;U=!0;try{X.destroy()}catch{}z(ne)},X=ff({host:t,port:A,servername:m,rejectUnauthorized:!1,timeout:_});X.once("secureConnect",()=>{if(X.authorized){Y({reachable:!0,trusted:!0});return}Y({reachable:!0,trusted:!1,reason:X.authorizationError?String(X.authorizationError):"origin certificate is not trusted for this hostname"})}),X.once("timeout",()=>{Y({reachable:!1,trusted:!1,reason:`no TLS handshake within ${_}ms`})}),X.once("error",(ne)=>{Y({reachable:!1,trusted:!1,reason:ne.message})})})}function gf(D,t){let m=t.replace(/\.$/,"").toLowerCase();return D.some((A)=>{let _=A.replace(/\.$/,"").toLowerCase();if(_===m)return!0;if(!_.startsWith("*."))return!1;let z=_.slice(2);if(!m.endsWith(`.${z}`))return!1;return!m.slice(0,m.length-z.length-1).includes(".")})}async function Jc(D){let{provider:t,zone:m,hosts:A,ipv4:_,ipv6:z,proxied:U=!0,settings:Y=$s,cache:X,originGuard:ne,purge:re=!0,ttl:se=300,skipOriginProbe:he=!1,skipEdgeCertificateCheck:we=!1}=D,Pe={records:[],settingsChanged:[],cacheRules:0,originGuard:!1,purged:!1,deferredProxy:[],warnings:[]};if(A.length===0)return Pe;let ye=[],Ie=U&&!we?await t.listEdgeCertificateHosts(m):null;for(let Re of A){let De=U;if(De&&Ie&&!gf(Ie,Re)){De=!1;let Ke=Ie.length===0?"the zone has no active edge certificate yet":`no active edge certificate covers it (${Ie.join(", ")})`;Pe.deferredProxy.push({host:Re,reason:Ke}),Pe.warnings.push(`${Re}: publishing DNS-only — ${Ke}. Proxying it would make Cloudflare terminate TLS with no certificate for the name, which fails the handshake outright rather than degrading. Universal SSL covers the apex and ONE level of subdomain, so a deeper name needs its own certificate; otherwise re-run the deploy once the certificate is active.`)}if(De&&!he){let Ke=await Kc({address:_,serverName:Re});if(!Ke.trusted){De=!1;let je=Ke.reason||"origin is not serving a trusted certificate";Pe.deferredProxy.push({host:Re,reason:je}),Pe.warnings.push(`${Re}: publishing DNS-only for now — the origin does not yet serve a certificate this proxy can verify (${je}). The origin issues certificates over HTTP-01, which needs the name to reach it directly; re-run the deploy once it has one and the record will be proxied.`)}}if(De)ye.push(Re);let Ee=[{type:"A",content:_}];if(z&&Wi(Re))Ee.push({type:"AAAA",content:z});for(let{type:Ke,content:je}of Ee){let _e=await t.upsertRecord(m,{name:Re,type:Ke,content:je,ttl:se,proxied:De});if(_e.success)Pe.records.push({host:Re,type:Ke,content:je,proxied:De});else Pe.warnings.push(`${Re} ${Ke} → ${je} failed: ${_e.message||"unknown provider error"}`)}}if(ye.length===0){if(U)Pe.warnings.push("No host is proxied yet, so zone settings, cache rules and purge were skipped — they only take effect through the proxy.");return Pe}try{let Re=Hc(Y),De=await t.applyZoneSettings(m,Re);Pe.settingsChanged=De.changed;for(let Ee of De.failed)Pe.warnings.push(`zone setting '${Ee.id}' could not be applied: ${Ee.error}`)}catch(Re){Pe.warnings.push(`zone settings could not be read: ${Re instanceof Error?Re.message:String(Re)}`)}let Le=ks(ye,X);if(Le.length>0){let Re=await t.putManagedPhaseRules(m,"http_request_cache_settings",Le);if(Re.success)Pe.cacheRules=Le.length;else Pe.warnings.push(`cache rules could not be written: ${Re.message}`)}if(ne){let Re=Ns(ye,ne.header,ne.value);if(Re.length>0){let De=await t.putManagedPhaseRules(m,"http_request_late_transform",Re);if(De.success)Pe.originGuard=!0;else Pe.warnings.push(`origin guard header could not be written: ${De.message}. `+"The gateway will reject requests that lack it — disable the guard or grant the token Transform Rules: Edit.")}}if(re){if(Pe.purged=await t.purgeCache(m,{hosts:ye}),!Pe.purged)Pe.warnings.push("edge cache purge failed — new content may take until TTL expiry to appear.")}return Pe}var hf=(D)=>process.env[D];function qd(D,t){let m=D.replace(/\.$/,"").toLowerCase();if(t){let A=t.replace(/\.$/,"").toLowerCase();if(m===A||m.endsWith(`.${A}`))return A}return m.split(".").slice(-2).join(".")}function Yc(D,t={}){let m=t.env??hf,A=D.infrastructure?.compute?.proxy?.cdn,_=[];if(!A||A.provider!=="cloudflare")return{plan:null,errors:_};if(A.originDomain)_.push(`compute.proxy.cdn.originDomain (${A.originDomain}) is ignored for Cloudflare: the proxy forwards to the address in the record it fronts, `+"so no separate origin hostname is needed — and publishing one gives clients a documented way around the edge.");let z=m("CLOUDFLARE_API_TOKEN");if(!z)return _.push("CLOUDFLARE_API_TOKEN is not set — the Cloudflare CDN cannot be reconciled."),{plan:null,errors:_};let U=D.sites??{},Y=A.frontedHosts?.length?[...new Set(A.frontedHosts.map((he)=>he.toLowerCase()))]:Xi(U,{autoWww:D.infrastructure?.compute?.proxy?.autoWww!==!1});if(Y.length===0)return _.push("No hostnames to front — set compute.proxy.cdn.frontedHosts, or give a site a domain."),{plan:null,errors:_};let X=D.infrastructure?.dns?.domain,ne=new Set(Y.map((he)=>qd(he,X)));if(ne.size>1)return _.push(`Fronted hosts span more than one zone (${[...ne].join(", ")}). A Cloudflare zone is configured as a unit, so run one CDN config per zone.`),{plan:null,errors:_};let re=A.cloudflare??{},se=A.secret?{header:A.secretHeader??"X-Origin-Verify",value:A.secret}:void 0;return{plan:{zone:[...ne][0],hosts:Y,apiToken:z,zoneId:re.zoneId??m("CLOUDFLARE_ZONE_ID"),accountId:re.accountId??m("CLOUDFLARE_ACCOUNT_ID"),proxied:re.proxied!==!1,settings:re.settings??$s,cache:re.cache,originGuard:se,purge:re.purgeOnDeploy!==!1,skipOriginProbe:re.skipOriginProbe===!0},errors:_}}var yf="https://api.cloudflare.com/client/v4",Hd=10042,vf=10006,bf=10059;class ho extends Error{status;errors;constructor(D,t,m=[]){super(D);this.name="R2ApiError",this.status=t,this.errors=m}hasCode(D){return this.errors.some((t)=>t.code===D)}}class Yi extends ho{accountId;constructor(D,t,m=[]){super(`R2 is not enabled on Cloudflare account ${D}. Enable it once in the Cloudflare dashboard (R2 Object Storage, then follow the prompt to activate it), then re-run the deploy.`,t,m);this.name="R2NotEnabledError",this.accountId=D}}class jo{name="cloudflare-r2";apiToken;accountId;fetchImpl;constructor(D){if(!D.apiToken)throw Error("Cloudflare R2 needs an API token");if(!D.accountId)throw Error("Cloudflare R2 needs an account id");this.apiToken=D.apiToken,this.accountId=D.accountId,this.fetchImpl=D.fetch??((t,m)=>fetch(t,m))}async requestEnvelope(D,t,m,A={}){let _={Authorization:`Bearer ${this.apiToken}`};if(m!==void 0)_["Content-Type"]="application/json";if(A.jurisdiction&&A.jurisdiction!=="default")_["cf-r2-jurisdiction"]=A.jurisdiction;let z=await this.fetchImpl(`${yf}/accounts/${this.accountId}/r2${t}`,{method:D,headers:_,body:m===void 0?void 0:JSON.stringify(m)}),U=await z.text(),Y;try{Y=JSON.parse(U)}catch{throw new ho(`Cloudflare R2 API returned ${z.status} with a non-JSON body: ${U.slice(0,200)}`,z.status)}if(!Y.success){let X=Y.errors??[];if(X.some((re)=>re.code===Hd))throw new Yi(this.accountId,z.status,X);let ne=X.map((re)=>`${re.code}: ${re.message}`).join(", ");throw new ho(`Cloudflare R2 API error (${z.status}) on ${D} ${t}: ${ne||U.slice(0,200)}`,z.status,X)}return Y}async request(D,t,m,A){return(await this.requestEnvelope(D,t,m,A)).result}bucketPath(D){return`/buckets/${encodeURIComponent(D)}`}async listBuckets(D={}){let t=[],m;do{let A=new URLSearchParams({per_page:"1000"});if(m)A.set("cursor",m);let _=await this.requestEnvelope("GET",`/buckets?${A}`,void 0,D),z=_.result?.buckets??[];t.push(...z),m=z.length>0?_.result_info?.cursor||void 0:void 0}while(m);return t}async getBucket(D,t={}){try{return await this.request("GET",this.bucketPath(D),void 0,t)}catch(m){if(m instanceof Yi)throw m;if(m instanceof ho&&(m.hasCode(vf)||m.status===404))return null;throw m}}async ensureBucket(D,t={}){let m={jurisdiction:t.jurisdiction},A=await this.getBucket(D,m);if(A)return{bucket:A,created:!1};let _={name:D};if(t.locationHint)_.locationHint=t.locationHint;let z=await this.request("POST","/buckets",_,m);return{bucket:{...z,name:z?.name||D},created:!0}}async getCors(D,t={}){try{return(await this.request("GET",`${this.bucketPath(D)}/cors`,void 0,t))?.rules??[]}catch(m){if(m instanceof ho&&!(m instanceof Yi)&&(m.hasCode(bf)||m.status===404))return[];throw m}}async putCors(D,t,m={}){if(t.length===0){await this.request("DELETE",`${this.bucketPath(D)}/cors`,void 0,m);return}await this.request("PUT",`${this.bucketPath(D)}/cors`,{rules:t},m)}async ensureCors(D,t,m={}){let A=await this.getCors(D,m);if(oa(A,t))return!1;return await this.putCors(D,t,m),!0}async getLifecycle(D,t={}){return(await this.request("GET",`${this.bucketPath(D)}/lifecycle`,void 0,t))?.rules??[]}async putLifecycle(D,t,m={}){await this.request("PUT",`${this.bucketPath(D)}/lifecycle`,{rules:t},m)}async ensureLifecycle(D,t,m={}){let A=await this.getLifecycle(D,m);if(aa(A,t))return!1;return await this.putLifecycle(D,t,m),!0}async listCustomDomains(D,t={}){return(await this.request("GET",`${this.bucketPath(D)}/domains/custom`,void 0,t))?.domains??[]}async attachCustomDomain(D,t,m={}){return this.request("POST",`${this.bucketPath(D)}/domains/custom`,{domain:t.domain,zoneId:t.zoneId,enabled:t.enabled??!0,minTLS:t.minTLS??"1.2"},m)}async updateCustomDomain(D,t,m,A={}){return this.request("PUT",`${this.bucketPath(D)}/domains/custom/${encodeURIComponent(t)}`,m,A)}async ensureCustomDomain(D,t,m={},A){let _=t.minTLS??"1.2",U=(A??await this.listCustomDomains(D,m)).find((ne)=>ne.domain.toLowerCase()===t.domain.toLowerCase());if(!U){let ne=await this.attachCustomDomain(D,{...t,minTLS:_,enabled:!0},m);return{action:"attached",domain:{...ne,domain:ne?.domain||t.domain}}}let Y=U.minTLS??"1.0";if(U.enabled&&Y===_)return{action:"unchanged",domain:U};let X=await this.updateCustomDomain(D,U.domain,{enabled:!0,minTLS:_},m);return{action:"updated",domain:{...U,...X}}}async getManagedDomain(D,t={}){return this.request("GET",`${this.bucketPath(D)}/domains/managed`,void 0,t)}async setManagedDomain(D,t,m={}){let A=await this.getManagedDomain(D,m);if(A?.enabled===t)return{changed:!1,domain:A};return{changed:!0,domain:await this.request("PUT",`${this.bucketPath(D)}/domains/managed`,{enabled:t},m)}}}function As(D){if(Array.isArray(D))return`[${D.map(As).join(",")}]`;if(D&&typeof D==="object")return`{${Object.entries(D).filter(([,m])=>m!==void 0).sort(([m],[A])=>m.localeCompare(A)).map(([m,A])=>`${JSON.stringify(m)}:${As(A)}`).join(",")}}`;return JSON.stringify(D)}function Gd(D,t){let m=(A)=>A&&A.length>0?[...A].map(String).sort():void 0;return{id:t?D.id:void 0,allowed:{origins:m(D.allowed?.origins),methods:m(D.allowed?.methods?.map((A)=>A.toUpperCase())),headers:m(D.allowed?.headers?.map((A)=>A.toLowerCase()))},exposeHeaders:m(D.exposeHeaders?.map((A)=>A.toLowerCase())),maxAgeSeconds:D.maxAgeSeconds}}function oa(D,t){if(D.length!==t.length)return!1;return t.every((m,A)=>{let _=m.id!==void 0;return As(Gd(D[A],_))===As(Gd(m,_))})}function aa(D,t){if(D.length!==t.length)return!1;let m=(A)=>As([...A].map((_)=>({..._,storageClassTransitions:_.storageClassTransitions?.length?_.storageClassTransitions:void 0})).sort((_,z)=>_.id.localeCompare(z.id)));return m(D)===m(t)}var wf="r2 cache",Sf=/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;async function Xc(D,t={}){let m=t.log??(()=>{}),A={buckets:[],warnings:[]},_=D.infrastructure?.r2??D.r2,z=Object.entries(_?.buckets??{});if(z.length===0)return m("R2: no buckets declared, skipping."),A;if(!t.apiToken||!t.accountId){let re=[!t.apiToken&&"CLOUDFLARE_API_TOKEN",!t.accountId&&"CLOUDFLARE_ACCOUNT_ID"].filter(Boolean).join(" and "),se=`R2: ${z.length} bucket(s) declared but ${re} is not set, skipping.`;return m(se),A.warnings.push(se),A}let U=t.dryRun===!0,Y=new jo({apiToken:t.apiToken,accountId:t.accountId,fetch:t.fetch}),X=new ge(t.apiToken,{zoneId:t.zoneId,accountId:t.accountId}),ne=new Map;for(let[re,se]of z){let he=se?.name;if(!he||!Sf.test(he)){A.warnings.push(`R2: bucket '${re}' has an invalid name '${he??""}' (3-63 chars: lowercase letters, digits, hyphens), skipping.`);continue}m(`R2: ${U?"checking":"reconciling"} bucket ${he}...`);let we={name:he,changes:[],domains:[]};if(A.buckets.push(we),await Cf(se,{r2:Y,cloudflare:X,dryRun:U,summary:we,warnings:A.warnings,claimed:ne,log:m}),we.changes.length===0)m(`R2: ${he} is in sync.`);else for(let Pe of we.changes)m(`R2: ${he}: ${Pe}`)}return A}async function Cf(D,t){let{r2:m,dryRun:A,summary:_,warnings:z}=t,U=D.name,Y={jurisdiction:D.jurisdiction},X=(we,Pe)=>{let ye=A?Pe&&`would ${Pe}`:we;if(ye)_.changes.push(ye)},ne;if(A)ne=await m.getBucket(U,Y)!==null;else{let{created:we}=await m.ensureBucket(U,{locationHint:D.locationHint,jurisdiction:D.jurisdiction});if(ne=!0,we)X("created","")}if(!ne)X("","create bucket");let re=kf(D.customDomains);await Rs(t,`cors on ${U}`,async()=>{if(!D.cors)return;if(!ne){X("cors updated","set cors");return}let we=await m.getCors(U,Y);if(oa(we,D.cors))return;if(!A)await m.putCors(U,D.cors,Y);X(D.cors.length===0?"cors removed":"cors updated",D.cors.length===0?"remove cors":"update cors")}),await Rs(t,`lifecycle on ${U}`,async()=>{if(!D.lifecycle)return;let we=D.lifecycle.map(Yd);if(ne){let Pe=await m.getLifecycle(U,Y);if(aa(Pe,we))return}if(!A)await m.putLifecycle(U,we,Y);X("lifecycle updated","update lifecycle")}),await Rs(t,`r2.dev URL on ${U}`,async()=>{let we=D.publicDevUrl===!0;if(!ne){if(we)X("r2.dev URL enabled","enable r2.dev URL");return}if((await m.getManagedDomain(U,Y))?.enabled===we)return;if(!A)await m.setManagedDomain(U,we,Y);X(we?"r2.dev URL enabled":"r2.dev URL disabled",we?"enable r2.dev URL":"disable r2.dev URL")});let se=[];if(re.length>0&&ne)try{se=await m.listCustomDomains(U,Y)}catch(we){if(we instanceof Yi)throw we;z.push(`R2: could not list custom domains on ${U}: ${el(we)}`)}let he=new Map;for(let{domain:we,minTLS:Pe}of re){let ye=t.claimed.get(we);if(ye&&ye!==U){z.push(`R2: ${we} is declared on both ${ye} and ${U}; leaving it on ${ye}.`);continue}t.claimed.set(we,U);let Ie=se.find((Re)=>Re.domain.toLowerCase()===we),Le=Pe??"1.2";if(Ie&&Ie.enabled&&(Ie.minTLS??"1.0")===Le){if(_.domains.push({domain:we,status:Qc(Ie)}),Ie.zoneId)he.set(we,Ie.zoneId);continue}await Rs(t,`custom domain ${we} on ${U}`,async()=>{if(Ie){if(!A)await m.updateCustomDomain(U,Ie.domain,{enabled:!0,minTLS:Le},Y);if(X(`custom domain ${we} updated`,`update custom domain ${we}`),_.domains.push({domain:we,status:Qc({...Ie,enabled:!0})}),Ie.zoneId)he.set(we,Ie.zoneId);return}let Re=await t.cloudflare.zoneIdFor(we);if(he.set(we,Re),!A)await m.ensureCustomDomain(U,{domain:we,zoneId:Re,minTLS:Le},Y,se);X(`custom domain ${we} attached`,`attach custom domain ${we}`),_.domains.push({domain:we,status:A?"not attached":"pending"})},()=>_.domains.push({domain:we,status:"error"}))}if(D.cache&&re.length>0)await $f(D,re.map((we)=>we.domain).filter((we)=>t.claimed.get(we)===U),he,t,X)}async function $f(D,t,m,A,_){let z=Kd(D.cache);if(!z||t.length===0)return;let U=new Map;for(let Y of t){let X=m.get(Y);if(!X)try{X=await A.cloudflare.zoneIdFor(Y)}catch(ne){A.warnings.push(`R2: could not resolve the zone for ${Y}, cache rule skipped: ${el(ne)}`);continue}U.set(X,[...U.get(X)??[],Y])}for(let Y of U.values()){let X={action:"set_cache_settings",description:`${Er} ${wf}`,expression:`(${Tr(Y)})`,enabled:!0,action_parameters:z};await Rs(A,`cache rule for ${Y.join(", ")}`,async()=>{let ne=await A.cloudflare.getPhaseRules(Y[0],"http_request_cache_settings");if(Jd(ne,X))return;if(!A.dryRun){let re=await A.cloudflare.putManagedPhaseRules(Y[0],"http_request_cache_settings",[X]);if(!re.success)throw Error(re.message||"cache rule write failed")}_(`cache rule updated for ${Y.join(", ")}`,`update cache rule for ${Y.join(", ")}`)})}}function Kd(D){if(D.edgeTtl===void 0&&D.browserTtl===void 0)return null;let t={cache:!0};if(D.edgeTtl!==void 0)t.edge_ttl={mode:"override_origin",default:D.edgeTtl};if(D.browserTtl!==void 0)t.browser_ttl={mode:"override_origin",default:D.browserTtl};return t}function Jd(D,t){let m=wr(t.expression),A=Ci(t.expression),_=(Y)=>(Y.description||"").startsWith(Er),z=(Y)=>Y.action===t.action&&Y.description===t.description&&(Y.enabled??!0)===(t.enabled??!0)&&Ci(Y.expression)===A&&ca(Y.action_parameters)===ca(t.action_parameters),U=!1;for(let Y of D){if(!_(Y))continue;let X=wr(Y.expression);if(!m.some((re)=>X.includes(re)))continue;if(z(Y)&&m.every((re)=>X.includes(re))&&!U){U=!0;continue}return!1}return U}function Yd(D){let t={id:D.id,enabled:D.enabled??!0,conditions:{prefix:D.prefix??""}};if(D.expireAfterDays!==void 0)t.deleteObjectsTransition={condition:{type:"Age",maxAge:Zc(D.expireAfterDays)}};else if(D.expireOn!==void 0)t.deleteObjectsTransition={condition:{type:"Date",date:D.expireOn}};if(D.abortMultipartUploadsAfterDays!==void 0)t.abortMultipartUploadsTransition={condition:{type:"Age",maxAge:Zc(D.abortMultipartUploadsAfterDays)}};if(D.infrequentAccessAfterDays!==void 0)t.storageClassTransitions=[{storageClass:"InfrequentAccess",condition:{type:"Age",maxAge:Zc(D.infrequentAccessAfterDays)}}];return t}function Zc(D){return Math.round(D*86400)}function kf(D){let t=new Set,m=[];for(let A of D??[]){let _=typeof A==="string"?{domain:A}:A,z=_.domain?.trim().replace(/\.$/,"").toLowerCase();if(!z||t.has(z))continue;t.add(z),m.push({domain:z,minTLS:_.minTLS})}return m}function Qc(D){if(!D.enabled)return"disabled";let t=D.status?.ownership,m=D.status?.ssl;if(t==="active"&&m==="active")return"active";for(let A of[t,m])if(A==="error"||A==="blocked"||A==="deactivated")return A;return"pending"}async function Rs(D,t,m,A){try{await m()}catch(_){if(_ instanceof Yi)throw _;D.warnings.push(`R2: ${t} failed: ${el(_)}`),A?.()}}function el(D){return D instanceof Error?D.message:String(D)}function ca(D){if(Array.isArray(D))return`[${D.map(ca).join(",")}]`;if(D&&typeof D==="object")return`{${Object.entries(D).filter(([,m])=>m!==void 0).sort(([m],[A])=>m.localeCompare(A)).map(([m,A])=>`${JSON.stringify(m)}:${ca(A)}`).join(",")}}`;return JSON.stringify(D)}import{basename as Ef,extname as Tf}from"node:path";import{createHash as Af}from"node:crypto";var Rf="https://api.cloudflare.com/client/v4",io="TS_CLOUD_CONTENT_HASH",Ps="2025-09-01",nl="application/javascript+module",If=10007,Pf=[1e4,9109];class _o extends Error{status;errors;constructor(D,t,m=[]){super(D);this.name="WorkersApiError",this.status=t,this.errors=m}hasCode(D){return this.errors.some((t)=>t.code===D)}}class ma extends _o{permission;constructor(D,t,m,A=[]){super(`The Cloudflare API token is missing the "${D}" permission (${t}). Edit the token in the Cloudflare dashboard (My Profile, API Tokens), add it, then re-run the deploy.`,m,A);this.name="WorkersPermissionError",this.permission=D}}class pa{name="cloudflare-workers";apiToken;accountId;fetchImpl;constructor(D){if(!D.apiToken)throw Error("Cloudflare Workers needs an API token");if(!D.accountId)throw Error("Cloudflare Workers needs an account id");this.apiToken=D.apiToken,this.accountId=D.accountId,this.fetchImpl=D.fetch??((t,m)=>fetch(t,m))}async request(D,t,m){let A={Authorization:`Bearer ${this.apiToken}`},_;if(m instanceof FormData)_=m;else if(m!==void 0)A["Content-Type"]="application/json",_=JSON.stringify(m);let z=await this.fetchImpl(`${Rf}/accounts/${this.accountId}/workers${t}`,{method:D,headers:A,body:_}),U=await z.text(),Y;try{Y=JSON.parse(U)}catch{throw new _o(`Cloudflare Workers API returned ${z.status} with a non-JSON body: ${U.slice(0,200)}`,z.status)}if(!Y.success){let X=Y.errors??[],ne=X.map((re)=>`${re.code}: ${re.message}`).join(", ");if(Df(z.status,X))throw new ma(xf(t),`${D} ${t} -> ${z.status}${ne?`, ${ne}`:""}`,z.status,X);throw new _o(`Cloudflare Workers API error (${z.status}) on ${D} ${t}: ${ne||U.slice(0,200)}`,z.status,X)}return Y.result}scriptPath(D){return`/scripts/${encodeURIComponent(D)}`}async getSettings(D){try{return await this.request("GET",`${this.scriptPath(D)}/settings`)}catch(t){if(t instanceof ma)throw t;if(t instanceof _o&&(t.hasCode(If)||t.status===404))return null;throw t}}async getScript(D){return this.getSettings(D)}async uploadModuleScript(D,t){return this.request("PUT",this.scriptPath(D),Qd(t))}async ensureModuleScript(D,t){let m=Ds(t),A=await this.getSettings(D);if(A&&ga(A,t,m))return{action:"unchanged",hash:m};return await this.uploadModuleScript(D,Xd(t,m)),{action:A?"updated":"created",hash:m}}async listCustomDomains(D={}){let t=new URLSearchParams;if(D.service)t.set("service",D.service);if(D.hostname)t.set("hostname",D.hostname);if(D.zoneId)t.set("zone_id",D.zoneId);let m=t.toString(),A=m?`?${m}`:"";return await this.request("GET",`/domains${A}`)??[]}async attachCustomDomain(D,t){let m={hostname:D,service:t.service,environment:t.environment??"production"};if(t.zoneId)m.zone_id=t.zoneId;return this.request("PUT","/domains",m)}async detachCustomDomain(D){await this.request("DELETE",`/domains/${encodeURIComponent(D)}`)}async ensureCustomDomain(D,t,m){let _=(m??await this.listCustomDomains({hostname:D})).find((U)=>U.hostname.toLowerCase()===D.toLowerCase());if(_&&_.service===t.service)return{action:"unchanged",domain:_};if(_)throw Error(`${D} is attached to the Worker '${_.service}'. Detach it there first (Workers & Pages, ${_.service}, Settings, Domains & Routes); ts-cloud does not move it.`);let z=await this.attachCustomDomain(D,t);return{action:"attached",domain:{...z,hostname:z?.hostname||D}}}}function Df(D,t){if(t.some((m)=>Pf.includes(m.code)))return!0;return(D===401||D===403)&&t.length===0}function xf(D){if(D.startsWith("/domains"))return"Workers Routes: Edit (zone, for Workers custom domains) and Workers Scripts: Edit (account)";return"Workers Scripts: Edit (account)"}function Qd(D){if(!D.modules.some((A)=>A.name===D.mainModule))throw Error(`Worker main module '${D.mainModule}' is not one of the uploaded modules`);let t={main_module:D.mainModule,bindings:D.bindings??[],compatibility_date:D.compatibilityDate??Ps,compatibility_flags:D.compatibilityFlags??[],keep_bindings:["secret_text"]},m=new FormData;m.append("metadata",JSON.stringify(t));for(let A of D.modules)m.append(A.name,new Blob([A.content],{type:A.type}),A.name);return m}function Ds(D){let t={mainModule:D.mainModule,modules:[...D.modules].sort((m,A)=>m.name.localeCompare(A.name)).map((m)=>({name:m.name,type:m.type,sha256:Zd(m.content)})),bindings:[...D.bindings??[]].filter((m)=>m.name!==io).sort((m,A)=>m.name.localeCompare(A.name)),compatibilityDate:D.compatibilityDate??Ps,compatibilityFlags:[...D.compatibilityFlags??[]].sort()};return Zd(tl(t))}function Xd(D,t=Ds(D)){return{...D,bindings:[...(D.bindings??[]).filter((m)=>m.name!==io),{type:"plain_text",name:io,text:t}]}}function ga(D,t,m){let A=D.bindings??[],_=A.find((z)=>z.name===io&&z.type==="plain_text");if(!_||_.text!==m)return!1;return(t.bindings??[]).every((z)=>{let U=A.find((X)=>X.name===z.name);if(!U||U.type!==z.type)return!1;let Y=z.type==="r2_bucket"?"bucket_name":z.type==="plain_text"?"text":void 0;return!Y||U[Y]===z[Y]})}function Zd(D){return Af("sha256").update(D).digest("hex")}function tl(D){if(Array.isArray(D))return`[${D.map(tl).join(",")}]`;if(D&&typeof D==="object")return`{${Object.entries(D).filter(([,m])=>m!==void 0).sort(([m],[A])=>m.localeCompare(A)).map(([m,A])=>`${JSON.stringify(m)}:${tl(A)}`).join(",")}}`;return JSON.stringify(D)}async function rl(D){if(!await Bun.file(D).exists())throw Error(`Worker entry not found: ${D}`);let t;try{t=await Bun.build({entrypoints:[D],target:"browser",format:"esm",minify:!0,external:["cloudflare:*"]})}catch(_){throw Error(`Bundling Worker ${D} failed: ${Nf(_)}`)}if(!t.success)throw Error(`Bundling Worker ${D} failed: ${t.logs.map((_)=>String(_.message??_)).join("; ")}`);if(t.outputs.length!==1)throw Error(`Bundling Worker ${D} produced ${t.outputs.length} files (${t.outputs.map((_)=>_.path).join(", ")}); a Worker is uploaded as a single module, so remove the dynamic imports or asset imports that split it.`);let m=t.outputs[0];if(m.kind!=="entry-point")throw Error(`Bundling Worker ${D} produced a ${m.kind} instead of an entry module`);let A=await m.text();return{name:`${Ef(D,Tf(D))}.js`,content:A,type:nl,size:new TextEncoder().encode(A).byteLength}}function Nf(D){if(D instanceof AggregateError&&D.errors.length>0)return D.errors.map((t)=>t instanceof Error?t.message:String(t?.message??t)).join("; ");return D instanceof Error?D.message:String(D)}import{isAbsolute as Of,resolve as Lf}from"node:path";var Ff="https://api.cloudflare.com/client/v4",jf=/^[a-z0-9][a-z0-9_-]{0,62}$/;async function il(D,t={}){let m=t.log??(()=>{}),A={workers:[],warnings:[]},_=Object.entries(D.infrastructure?.workers??D.workers??{});if(_.length===0)return m("Workers: none declared, skipping."),A;if(!t.apiToken||!t.accountId){let Y=[!t.apiToken&&"CLOUDFLARE_API_TOKEN",!t.accountId&&"CLOUDFLARE_ACCOUNT_ID"].filter(Boolean).join(" and "),X=`Workers: ${_.length} Worker(s) declared but ${Y} is not set, skipping.`;return m(X),A.warnings.push(X),A}let z=D.infrastructure?.r2??D.r2,U={workers:new pa({apiToken:t.apiToken,accountId:t.accountId,fetch:t.fetch}),r2:new jo({apiToken:t.apiToken,accountId:t.accountId,fetch:t.fetch}),cloudflare:new ge(t.apiToken,{zoneId:t.zoneId,accountId:t.accountId}),apiToken:t.apiToken,fetch:t.fetch??((Y,X)=>fetch(Y,X)),projectRoot:t.projectRoot??process.cwd(),dryRun:t.dryRun===!0,warnings:A.warnings,log:m,r2ConfigDomains:Wf(z),r2Buckets:z,claimed:new Map};for(let[Y,X]of _){let ne=X?.name;if(!ne||!jf.test(ne)){A.warnings.push(`Workers: '${Y}' has an invalid name '${ne??""}' (1-63 chars: lowercase letters, digits, '-' and '_'), skipping.`);continue}if(!X.entry){A.warnings.push(`Workers: ${ne} has no entry, skipping.`);continue}m(`Workers: ${U.dryRun?"checking":"reconciling"} ${ne}...`);let re={name:ne,changes:[],domains:[]};A.workers.push(re),await Bf(X,re,U);for(let se of re.changes)m(`Workers: ${ne}: ${se}`)}return A}async function Bf(D,t,m){let A=D.name,_=Of(D.entry)?D.entry:Lf(m.projectRoot,D.entry),z;try{z=await rl(_)}catch(Y){throw Error(`Worker ${A}: ${Mo(Y)}`)}t.changes.push(`bundled ${qf(z.size)}`);let U={mainModule:z.name,modules:[{name:z.name,content:z.content,type:z.type}],bindings:eu(D,m),compatibilityDate:D.compatibilityDate??Ps,compatibilityFlags:D.compatibilityFlags??[]};if(m.dryRun){let Y=await m.workers.getSettings(A);t.changes.push(Y&&ga(Y,U,Ds(U))?"unchanged":"would upload")}else{let{action:Y}=await m.workers.ensureModuleScript(A,U);t.changes.push(Y==="unchanged"?"unchanged":"uploaded")}for(let Y of Vf(D.customDomains))await _f(A,Y,t,m)}async function _f(D,t,m,A){let _=(we)=>{m.domains.push({domain:t,status:we})},z=(we)=>{A.warnings.push(`Workers: ${we}`),_("conflict")},U=A.claimed.get(t);if(U&&U!==D){z(`${t} is declared on both ${U} and ${D}; leaving it on ${U}.`);return}A.claimed.set(t,D);let Y=A.r2ConfigDomains.get(t);if(Y){z(`${t} is also declared as a custom domain of the R2 bucket '${Y}' (r2.buckets). A hostname serves either a bucket or a Worker. Remove it from the bucket's customDomains and detach it in the dashboard (R2, ${Y}, Settings, Custom Domains) first; to serve the bucket through the Worker, bind it with bindings.r2Buckets instead.`);return}let X=await Mf(A);if(X===null){_("error");return}let ne=X.find((we)=>we.hostname.toLowerCase()===t);if(ne?.service===D){_("active");return}if(ne){z(`${t} is attached to the Worker '${ne.service}'. Detach it there first (Workers & Pages, ${ne.service}, Settings, Domains & Routes); it is not moved automatically.`);return}let re=(await zf(A))?.get(t);if(re){z(`${t} is attached to the R2 bucket '${re}' as a custom domain. Detach it first (R2, ${re}, Settings, Custom Domains, then re-run the deploy); it is not detached automatically. To serve the bucket through the Worker, bind it with bindings.r2Buckets.`);return}let se;try{se=await A.cloudflare.zoneIdFor(t)}catch(we){A.warnings.push(`Workers: could not resolve the zone for ${t}, not attached: ${Mo(we)}`),_("error");return}let he=await Uf(A,se,t);if(he.length>0){let we=he.map((Pe)=>`${Pe.type} ${Pe.content}`).join(", ");z(`${t} already has a DNS record (${we}). A Worker custom domain creates its own record, so Cloudflare will not attach over it. Delete the record first (DNS, Records), then re-run the deploy; it is not deleted automatically.`);return}if(A.dryRun){m.changes.push(`would attach custom domain ${t}`),_("not attached");return}try{await A.workers.ensureCustomDomain(t,{service:D,zoneId:se},X),m.changes.push(`custom domain ${t} attached`),X.push({id:"",hostname:t,service:D,zone_id:se}),_("pending")}catch(we){A.warnings.push(`Workers: custom domain ${t} on ${D} failed: ${Mo(we)}`),_("error")}}async function Mf(D){if(D.attached!==void 0)return D.attached;try{D.attached=await D.workers.listCustomDomains()}catch(t){D.warnings.push(`Workers: could not list Worker custom domains, none attached: ${Mo(t)}`),D.attached=null}return D.attached}async function zf(D){if(D.r2AccountDomains!==void 0)return D.r2AccountDomains;let t=new Map;try{let m=(await D.r2.listBuckets()).map((A)=>({name:A.name}));for(let A of Object.values(D.r2Buckets?.buckets??{}))if(A.jurisdiction&&A.jurisdiction!=="default")m.push({name:A.name,jurisdiction:A.jurisdiction});for(let A of m){let _=await D.r2.listCustomDomains(A.name,{jurisdiction:A.jurisdiction}).catch(()=>[]);for(let z of _)t.set(z.domain.toLowerCase(),A.name)}D.r2AccountDomains=t}catch(m){D.log(`Workers: could not check R2 custom domains (${Mo(m)}); relying on the DNS check.`),D.r2AccountDomains=null}return D.r2AccountDomains}async function Uf(D,t,m){try{let A=await D.fetch(`${Ff}/zones/${t}/dns_records?name=${encodeURIComponent(m)}&per_page=100`,{method:"GET",headers:{Authorization:`Bearer ${D.apiToken}`}}),_=await A.json();if(!_?.success)throw Error(`HTTP ${A.status}`);return(_.result??[]).filter((z)=>z.name?.toLowerCase()===m).map((z)=>({type:z.type??"?",content:z.content??""}))}catch(A){return D.log(`Workers: could not read DNS records for ${m} (${Mo(A)}); attaching anyway.`),[]}}function eu(D,t){let m=[],A=new Set,_=(U)=>{if(U===io)return t.warnings.push(`Workers: ${D.name}: binding name ${io} is reserved for ts-cloud, ignored.`),!1;if(A.has(U))return t.warnings.push(`Workers: ${D.name}: binding ${U} is declared twice; the first one wins.`),!1;return A.add(U),!0},z=new Map;for(let U of Object.values(t.r2Buckets?.buckets??{}))if(U.jurisdiction&&U.jurisdiction!=="default")z.set(U.name,U.jurisdiction);for(let[U,Y]of Object.entries(D.bindings?.r2Buckets??{})){if(!_(U))continue;let X=z.get(Y);m.push(X?{type:"r2_bucket",name:U,bucket_name:Y,jurisdiction:X}:{type:"r2_bucket",name:U,bucket_name:Y})}for(let[U,Y]of Object.entries(D.bindings?.vars??{}))if(_(U))m.push({type:"plain_text",name:U,text:String(Y)});return m}function Wf(D){let t=new Map;for(let m of Object.values(D?.buckets??{}))for(let A of m?.customDomains??[]){let _=tu(typeof A==="string"?A:A.domain);if(_)t.set(_,m.name)}return t}function Vf(D){return[...new Set((D??[]).map(tu).filter(Boolean))]}function tu(D){return(D??"").trim().replace(/\.$/,"").toLowerCase()}function qf(D){if(D<1024)return`${D} B`;if(D<1048576)return`${(D/1024).toFixed(1)} KB`;return`${(D/1024/1024).toFixed(1)} MB`}function Mo(D){return D instanceof Error?D.message:String(D)}async function Yf(D){let t=ta();if(t.emptyEnvKey||t.emptyEnvSecret)a("Empty AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY env var detected — likely a misconfigured .env file. Remove the empty lines or fill them in.");e(`AWS credentials: ${t.description}`);try{let A=await new lt(D).getCallerIdentity();if(A.Arn)e(`AWS identity: ${A.Arn}`)}catch(m){a(`Could not verify AWS identity: ${m.message}`)}}function Zf(){try{return cl("git rev-parse --short HEAD",{stdio:["ignore","pipe","ignore"]}).toString().trim()}catch{return Date.now().toString(36)}}async function ol(D,t,m,A){let{ensureManagementDashboard:_}=await import("./chunk-kqxk1pds.js");_(D,{logger:{info:e,warn:a}});let z=D.sites||{},U=Object.entries(z);if(U.length===0)return a("infrastructure.compute is set but no sites are configured — nothing to deploy. Add sites to `cloud.config.ts`."),!0;if(A&&!z[A])return r(`Site '${A}' was not found. Configured sites: ${U.map(([Le])=>Le).join(", ")}`),!1;let Y=U.filter(([Le,Re])=>{if(!Re)return!1;if(A&&Le!==A)return!1;if(P(Re)==="bucket")return a(`Site '${Le}' targets a bucket — skipping compute deploy (handled by the static-site path).`),!1;return qs(Re)});if(A)e(`Deploying only '${A}'. Other sites keep their current release; proxy routes are still regenerated in full.`);let X=D.project.slug,re=(D.infrastructure?.compute||{}).runtime||"bun",se=O({config:D}),he=Zf(),we=new Map,Pe=new Map,ye={step:(Le)=>d(Le),error:(Le)=>r(Le)};if(!await Cs(D,"beforeDeploy",ye))return!1;if(!await Cs(D,"beforeBuild",ye))return!1;for(let[Le,Re]of Y){let De=P(Re);if(i(`Preparing site: ${Le}`),e(`Domain: ${Re.domain||"(none)"}`),e(`Source: ${Re.root}`),e(`Kind: ${De==="server-static"?"static (shipped to /var/www)":"app (systemd service)"}`),De==="server-app")e(`Start: ${Re.start}`),e(`Port: ${Re.port??"(unset)"}`);if(Re.build){d(`Running build: ${Re.build}`);try{cl(Re.build,{stdio:"inherit",cwd:process.cwd()})}catch(et){return r(`Build failed for site '${Le}': ${et.message}`),!1}}let Ee=Vd({root:Re.root,siteName:Le,expectIndex:De==="server-static"});for(let et of Ee)if(et.level==="error")r(et.message);else a(et.message);if(Ee.some((et)=>et.level==="error"))return!1;let Ke=Re.build?void 0:JSON.stringify([Jf(Re.root),[...Re.exclude||[]].sort()]),je=Ke?Pe.get(Ke):void 0;if(je){d(`Reusing package for ${Re.root} → ${je}`),we.set(Le,je);continue}let _e=su(ou(),`${X}-${Le}-${he}.tar.gz`);d(`Packaging ${Re.root} → ${_e}`);try{let et=(Re.exclude||[]).map((Rt)=>`--exclude=${Rt}`);Gf("tar",["czf",_e,"-C",Re.root,...et,"."],{stdio:"inherit",env:{...process.env,COPYFILE_DISABLE:"1"}})}catch(et){return r(`Failed to package '${Le}': ${et.message}`),!1}if(we.set(Le,_e),Ke)Pe.set(Ke,_e)}if(!await Cs(D,"afterBuild",ye))return!1;let Ie=await ro({config:A?{...D,sites:Object.fromEntries(Y)}:D,rpxConfig:A?D:void 0,environment:t,driver:se,sha:he,runtime:re,tarballForSite:(Le)=>{let Re=we.get(Le);if(!Re)throw Error(`Missing tarball for site '${Le}'`);return Re},logger:{info:(Le)=>e(Le),warn:(Le)=>a(Le),error:(Le)=>r(Le),step:(Le)=>d(Le),success:(Le)=>n(Le)}});if(Ie&&!await Cs(D,"afterDeploy",ye))return!1;return Ie}async function wa(D){let t=new Gr;if(d("Running pre-deployment security scan..."),D.exclude?.length)e(`Excluding from the scan: ${D.exclude.join(", ")}`);let m=await t.scan({directory:D.sourceDir,failOnSeverity:D.failOnSeverity||"critical",skipPatterns:D.skipPatterns,exclude:D.exclude});return{passed:m.passed,result:m}}function Qf(){let D=su(ou(),`ts-cloud-empty-scan-${process.pid}`);return iu(D,{recursive:!0}),D}function Sa(D){let{summary:t,findings:m,scannedFiles:A,duration:_}=D;if(e(`
|
|
375
|
+
Invalidation is in progress. Use --wait to wait for completion.`)}catch(m){r(`Invalidation failed: ${m.message}`)}})}function Wc(D){for(let[t,m]of[["team:add <email> <role>","Invite an organization member"],["team:list","List organization members"],["team:remove <email>","Revoke organization membership"]])D.command(t,m).action(async(...A)=>En(t.split(" ")[0],{target:String(A[0]??""),message:"Legacy IAM-backed team commands are deprecated and cannot safely represent organization-scoped access.",nextAction:"Use the Team dashboard or organization invitation API; this command exits without changing IAM."}))}import{execFileSync as Gf,execSync as cl}from"node:child_process";import{copyFileSync as xs,existsSync as vn,mkdirSync as iu,readFileSync as al,writeFileSync as Hf}from"node:fs";import{tmpdir as ou}from"node:os";import{dirname as Kf,join as su,resolve as Jf}from"node:path";import{existsSync as qc,readdirSync as zd,statSync as Gc}from"node:fs";import{join as ra}from"node:path";var lf=new Set([".DS_Store","Thumbs.db",".gitkeep",".keep"]);function Ud(D){return lf.has(D)}function Wd(D,t=0){if(t>8)return!0;let m;try{m=zd(D)}catch{return!1}for(let A of m){if(Ud(A))continue;let _=ra(D,A),z;try{z=Gc(_)}catch{continue}if(z.isFile())return!0;if(z.isDirectory()&&Wd(_,t+1))return!0}return!1}function df(D){let t;try{t=zd(D)}catch{return[]}let m=[];for(let A of t){if(Ud(A))continue;let _=ra(D,A);try{if(Gc(_).isDirectory()&&qc(ra(_,"index.html")))m.push(A)}catch{}}return m.sort((A,_)=>A.localeCompare(_))}function Vd(D){let{root:t,siteName:m,expectIndex:A}=D,_=[];if(!qc(t))return _.push({level:"error",message:`Build output not found at ${t} for site '${m}'`}),_;if(!Gc(t).isDirectory())return _.push({level:"error",message:`Site '${m}' has root ${t}, which is a file, not a directory`}),_;if(!Wd(t))return _.push({level:"error",message:`Site '${m}' would ship an empty release: ${t} contains no files. Check that the build ran and wrote where 'root' points.`}),_;if(!A)return _;if(qc(ra(t,"index.html")))return _;let z=df(t);if(z.length===1){let U=`${t.replace(/\/+$/,"")}/${z[0]}`;return _.push({level:"error",message:`Site '${m}' has no index.html at the top of ${t}, but ${U} has one. The generator wrote one level down, so this release would serve 404s at the domain root. Set root: '${U}'.`}),_}if(z.length>1)return _.push({level:"warning",message:`Site '${m}' has no index.html at the top of ${t}. These subdirectories have one: ${z.join(", ")}. The domain root will 404.`}),_;return _.push({level:"warning",message:`Site '${m}' has no index.html anywhere under ${t}. The domain root will 404.`}),_}function uf(D){if(!D)return[];if(Array.isArray(D))return D.map(String);let t=D,m=t.Items??t.Item;if(!m)return[];if(Array.isArray(m))return m.map(String);if(typeof m==="object"&&m!==null){let A=m;if("Item"in A){let _=A.Item;if(!_)return[];return Array.isArray(_)?_.map(String):[String(_)]}if("CNAME"in A){let _=A.CNAME;if(!_)return[];return Array.isArray(_)?_.map(String):[String(_)]}return Object.values(A).flatMap((_)=>{if(Array.isArray(_))return _.map(String);return typeof _==="string"?[_]:[]})}return[String(m)]}async function ia(D){if(D.length===0)return;let t=new E,m=await t.listDistributions(),A=[...new Set(D.map((_)=>_.toLowerCase()))];for(let _ of A){let z=m.find((Y)=>uf(Y.Aliases).some((ne)=>ne.toLowerCase()===_));if(!z?.Id){a(`No CloudFront distribution found for ${_} — skipping dynamic HTTP method sync`);continue}if(await t.ensureDynamicHttpMethods(z.Id))n(`CloudFront ${z.Id} (${_}): enabled POST/PUT/PATCH/DELETE on dynamic paths`);else e(`CloudFront ${z.Id} (${_}): already allows dynamic HTTP methods`)}}import{execSync as mf}from"node:child_process";async function pf(D,t,m,A={},_=process.cwd()){if(!D)return;if(A.step?.(`Running ${m} hook`),typeof D==="string"){mf(D,{stdio:"inherit",cwd:_});return}await D(t)}async function Cs(D,t,m={},A){try{return await pf(D.hooks?.[t],D,t,m,A),!0}catch(_){return m.error?.(`${t} hook failed: ${_?.message||_}`),!1}}var wi=(D)=>D===void 0?void 0:D?"on":"off";function Hc(D){let t={ssl:D.ssl,always_use_https:wi(D.alwaysUseHttps),automatic_https_rewrites:wi(D.automaticHttpsRewrites),min_tls_version:D.minTlsVersion,tls_1_3:wi(D.tls13),brotli:wi(D.brotli),http3:wi(D.http3),"0rtt":wi(D.zeroRtt),early_hints:wi(D.earlyHints),browser_cache_ttl:D.browserCacheTtl,always_online:wi(D.alwaysOnline),websockets:wi(D.websockets),email_obfuscation:wi(D.emailObfuscation)};if(D.hsts)t.security_header={strict_transport_security:{enabled:D.hsts.enabled,max_age:D.hsts.maxAge??31536000,include_subdomains:D.hsts.includeSubdomains??!1,preload:D.hsts.preload??!1,nosniff:D.hsts.noSniff??!0}};for(let m of Object.keys(t))if(t[m]===void 0)delete t[m];return{...t,...D.raw??{}}}var $s={ssl:"strict",alwaysUseHttps:!0,automaticHttpsRewrites:!0,minTlsVersion:"1.2",tls13:!0,brotli:!0,http3:!0,earlyHints:!0,alwaysOnline:!0,emailObfuscation:!1,browserCacheTtl:0};import{connect as ff}from"node:tls";async function Kc(D){let{address:t,serverName:m,port:A=443,timeoutMs:_=5000}=D;return new Promise((z)=>{let U=!1,Y=(ne)=>{if(U)return;U=!0;try{X.destroy()}catch{}z(ne)},X=ff({host:t,port:A,servername:m,rejectUnauthorized:!1,timeout:_});X.once("secureConnect",()=>{if(X.authorized){Y({reachable:!0,trusted:!0});return}Y({reachable:!0,trusted:!1,reason:X.authorizationError?String(X.authorizationError):"origin certificate is not trusted for this hostname"})}),X.once("timeout",()=>{Y({reachable:!1,trusted:!1,reason:`no TLS handshake within ${_}ms`})}),X.once("error",(ne)=>{Y({reachable:!1,trusted:!1,reason:ne.message})})})}function gf(D,t){let m=t.replace(/\.$/,"").toLowerCase();return D.some((A)=>{let _=A.replace(/\.$/,"").toLowerCase();if(_===m)return!0;if(!_.startsWith("*."))return!1;let z=_.slice(2);if(!m.endsWith(`.${z}`))return!1;return!m.slice(0,m.length-z.length-1).includes(".")})}async function Jc(D){let{provider:t,zone:m,hosts:A,ipv4:_,ipv6:z,proxied:U=!0,settings:Y=$s,cache:X,originGuard:ne,purge:re=!0,ttl:se=300,skipOriginProbe:he=!1,skipEdgeCertificateCheck:we=!1}=D,Pe={records:[],settingsChanged:[],cacheRules:0,originGuard:!1,purged:!1,deferredProxy:[],warnings:[]};if(A.length===0)return Pe;let ye=[],Ie=U&&!we?await t.listEdgeCertificateHosts(m):null;for(let Re of A){let De=U;if(De&&Ie&&!gf(Ie,Re)){De=!1;let Ke=Ie.length===0?"the zone has no active edge certificate yet":`no active edge certificate covers it (${Ie.join(", ")})`;Pe.deferredProxy.push({host:Re,reason:Ke}),Pe.warnings.push(`${Re}: publishing DNS-only — ${Ke}. Proxying it would make Cloudflare terminate TLS with no certificate for the name, which fails the handshake outright rather than degrading. Universal SSL covers the apex and ONE level of subdomain, so a deeper name needs its own certificate; otherwise re-run the deploy once the certificate is active.`)}if(De&&!he){let Ke=await Kc({address:_,serverName:Re});if(!Ke.trusted){De=!1;let je=Ke.reason||"origin is not serving a trusted certificate";Pe.deferredProxy.push({host:Re,reason:je}),Pe.warnings.push(`${Re}: publishing DNS-only for now — the origin does not yet serve a certificate this proxy can verify (${je}). The origin issues certificates over HTTP-01, which needs the name to reach it directly; re-run the deploy once it has one and the record will be proxied.`)}}if(De)ye.push(Re);let Ee=[{type:"A",content:_}];if(z&&Wi(Re))Ee.push({type:"AAAA",content:z});for(let{type:Ke,content:je}of Ee){let _e=await t.upsertRecord(m,{name:Re,type:Ke,content:je,ttl:se,proxied:De});if(_e.success)Pe.records.push({host:Re,type:Ke,content:je,proxied:De});else Pe.warnings.push(`${Re} ${Ke} → ${je} failed: ${_e.message||"unknown provider error"}`)}}if(ye.length===0){if(U)Pe.warnings.push("No host is proxied yet, so zone settings, cache rules and purge were skipped — they only take effect through the proxy.");return Pe}try{let Re=Hc(Y),De=await t.applyZoneSettings(m,Re);Pe.settingsChanged=De.changed;for(let Ee of De.failed)Pe.warnings.push(`zone setting '${Ee.id}' could not be applied: ${Ee.error}`)}catch(Re){Pe.warnings.push(`zone settings could not be read: ${Re instanceof Error?Re.message:String(Re)}`)}let Le=ks(ye,X);if(Le.length>0){let Re=await t.putManagedPhaseRules(m,"http_request_cache_settings",Le);if(Re.success)Pe.cacheRules=Le.length;else Pe.warnings.push(`cache rules could not be written: ${Re.message}`)}if(ne){let Re=Ns(ye,ne.header,ne.value);if(Re.length>0){let De=await t.putManagedPhaseRules(m,"http_request_late_transform",Re);if(De.success)Pe.originGuard=!0;else Pe.warnings.push(`origin guard header could not be written: ${De.message}. `+"The gateway will reject requests that lack it — disable the guard or grant the token Transform Rules: Edit.")}}if(re){if(Pe.purged=await t.purgeCache(m,{hosts:ye}),!Pe.purged)Pe.warnings.push("edge cache purge failed — new content may take until TTL expiry to appear.")}return Pe}var hf=(D)=>process.env[D];function qd(D,t){let m=D.replace(/\.$/,"").toLowerCase();if(t){let A=t.replace(/\.$/,"").toLowerCase();if(m===A||m.endsWith(`.${A}`))return A}return m.split(".").slice(-2).join(".")}function Yc(D,t={}){let m=t.env??hf,A=D.infrastructure?.compute?.proxy?.cdn,_=[];if(!A||A.provider!=="cloudflare")return{plan:null,errors:_};if(A.originDomain)_.push(`compute.proxy.cdn.originDomain (${A.originDomain}) is ignored for Cloudflare: the proxy forwards to the address in the record it fronts, `+"so no separate origin hostname is needed — and publishing one gives clients a documented way around the edge.");let z=m("CLOUDFLARE_API_TOKEN");if(!z)return _.push("CLOUDFLARE_API_TOKEN is not set — the Cloudflare CDN cannot be reconciled."),{plan:null,errors:_};let U=D.sites??{},Y=A.frontedHosts?.length?[...new Set(A.frontedHosts.map((he)=>he.toLowerCase()))]:Xi(U,{autoWww:D.infrastructure?.compute?.proxy?.autoWww!==!1});if(Y.length===0)return _.push("No hostnames to front — set compute.proxy.cdn.frontedHosts, or give a site a domain."),{plan:null,errors:_};let X=D.infrastructure?.dns?.domain,ne=new Set(Y.map((he)=>qd(he,X)));if(ne.size>1)return _.push(`Fronted hosts span more than one zone (${[...ne].join(", ")}). A Cloudflare zone is configured as a unit, so run one CDN config per zone.`),{plan:null,errors:_};let re=A.cloudflare??{},se=A.secret?{header:A.secretHeader??"X-Origin-Verify",value:A.secret}:void 0;return{plan:{zone:[...ne][0],hosts:Y,apiToken:z,zoneId:re.zoneId??m("CLOUDFLARE_ZONE_ID"),accountId:re.accountId??m("CLOUDFLARE_ACCOUNT_ID"),proxied:re.proxied!==!1,settings:re.settings??$s,cache:re.cache,originGuard:se,purge:re.purgeOnDeploy!==!1,skipOriginProbe:re.skipOriginProbe===!0},errors:_}}var yf="https://api.cloudflare.com/client/v4",Hd=10042,vf=10006,bf=10059;class ho extends Error{status;errors;constructor(D,t,m=[]){super(D);this.name="R2ApiError",this.status=t,this.errors=m}hasCode(D){return this.errors.some((t)=>t.code===D)}}class Yi extends ho{accountId;constructor(D,t,m=[]){super(`R2 is not enabled on Cloudflare account ${D}. Enable it once in the Cloudflare dashboard (R2 Object Storage, then follow the prompt to activate it), then re-run the deploy.`,t,m);this.name="R2NotEnabledError",this.accountId=D}}class jo{name="cloudflare-r2";apiToken;accountId;fetchImpl;constructor(D){if(!D.apiToken)throw Error("Cloudflare R2 needs an API token");if(!D.accountId)throw Error("Cloudflare R2 needs an account id");this.apiToken=D.apiToken,this.accountId=D.accountId,this.fetchImpl=D.fetch??((t,m)=>fetch(t,m))}async requestEnvelope(D,t,m,A={}){let _={Authorization:`Bearer ${this.apiToken}`};if(m!==void 0)_["Content-Type"]="application/json";if(A.jurisdiction&&A.jurisdiction!=="default")_["cf-r2-jurisdiction"]=A.jurisdiction;let z=await this.fetchImpl(`${yf}/accounts/${this.accountId}/r2${t}`,{method:D,headers:_,body:m===void 0?void 0:JSON.stringify(m)}),U=await z.text(),Y;try{Y=JSON.parse(U)}catch{throw new ho(`Cloudflare R2 API returned ${z.status} with a non-JSON body: ${U.slice(0,200)}`,z.status)}if(!Y.success){let X=Y.errors??[];if(X.some((re)=>re.code===Hd))throw new Yi(this.accountId,z.status,X);let ne=X.map((re)=>`${re.code}: ${re.message}`).join(", ");throw new ho(`Cloudflare R2 API error (${z.status}) on ${D} ${t}: ${ne||U.slice(0,200)}`,z.status,X)}return Y}async request(D,t,m,A){return(await this.requestEnvelope(D,t,m,A)).result}bucketPath(D){return`/buckets/${encodeURIComponent(D)}`}async listBuckets(D={}){let t=[],m;do{let A=new URLSearchParams({per_page:"1000"});if(m)A.set("cursor",m);let _=await this.requestEnvelope("GET",`/buckets?${A}`,void 0,D),z=_.result?.buckets??[];t.push(...z),m=z.length>0?_.result_info?.cursor||void 0:void 0}while(m);return t}async getBucket(D,t={}){try{return await this.request("GET",this.bucketPath(D),void 0,t)}catch(m){if(m instanceof Yi)throw m;if(m instanceof ho&&(m.hasCode(vf)||m.status===404))return null;throw m}}async ensureBucket(D,t={}){let m={jurisdiction:t.jurisdiction},A=await this.getBucket(D,m);if(A)return{bucket:A,created:!1};let _={name:D};if(t.locationHint)_.locationHint=t.locationHint;let z=await this.request("POST","/buckets",_,m);return{bucket:{...z,name:z?.name||D},created:!0}}async getCors(D,t={}){try{return(await this.request("GET",`${this.bucketPath(D)}/cors`,void 0,t))?.rules??[]}catch(m){if(m instanceof ho&&!(m instanceof Yi)&&(m.hasCode(bf)||m.status===404))return[];throw m}}async putCors(D,t,m={}){if(t.length===0){await this.request("DELETE",`${this.bucketPath(D)}/cors`,void 0,m);return}await this.request("PUT",`${this.bucketPath(D)}/cors`,{rules:t},m)}async ensureCors(D,t,m={}){let A=await this.getCors(D,m);if(oa(A,t))return!1;return await this.putCors(D,t,m),!0}async getLifecycle(D,t={}){return(await this.request("GET",`${this.bucketPath(D)}/lifecycle`,void 0,t))?.rules??[]}async putLifecycle(D,t,m={}){await this.request("PUT",`${this.bucketPath(D)}/lifecycle`,{rules:t},m)}async ensureLifecycle(D,t,m={}){let A=await this.getLifecycle(D,m);if(aa(A,t))return!1;return await this.putLifecycle(D,t,m),!0}async listCustomDomains(D,t={}){return(await this.request("GET",`${this.bucketPath(D)}/domains/custom`,void 0,t))?.domains??[]}async attachCustomDomain(D,t,m={}){return this.request("POST",`${this.bucketPath(D)}/domains/custom`,{domain:t.domain,zoneId:t.zoneId,enabled:t.enabled??!0,minTLS:t.minTLS??"1.2"},m)}async updateCustomDomain(D,t,m,A={}){return this.request("PUT",`${this.bucketPath(D)}/domains/custom/${encodeURIComponent(t)}`,m,A)}async ensureCustomDomain(D,t,m={},A){let _=t.minTLS??"1.2",U=(A??await this.listCustomDomains(D,m)).find((ne)=>ne.domain.toLowerCase()===t.domain.toLowerCase());if(!U){let ne=await this.attachCustomDomain(D,{...t,minTLS:_,enabled:!0},m);return{action:"attached",domain:{...ne,domain:ne?.domain||t.domain}}}let Y=U.minTLS??"1.0";if(U.enabled&&Y===_)return{action:"unchanged",domain:U};let X=await this.updateCustomDomain(D,U.domain,{enabled:!0,minTLS:_},m);return{action:"updated",domain:{...U,...X}}}async getManagedDomain(D,t={}){return this.request("GET",`${this.bucketPath(D)}/domains/managed`,void 0,t)}async setManagedDomain(D,t,m={}){let A=await this.getManagedDomain(D,m);if(A?.enabled===t)return{changed:!1,domain:A};return{changed:!0,domain:await this.request("PUT",`${this.bucketPath(D)}/domains/managed`,{enabled:t},m)}}}function As(D){if(Array.isArray(D))return`[${D.map(As).join(",")}]`;if(D&&typeof D==="object")return`{${Object.entries(D).filter(([,m])=>m!==void 0).sort(([m],[A])=>m.localeCompare(A)).map(([m,A])=>`${JSON.stringify(m)}:${As(A)}`).join(",")}}`;return JSON.stringify(D)}function Gd(D,t){let m=(A)=>A&&A.length>0?[...A].map(String).sort():void 0;return{id:t?D.id:void 0,allowed:{origins:m(D.allowed?.origins),methods:m(D.allowed?.methods?.map((A)=>A.toUpperCase())),headers:m(D.allowed?.headers?.map((A)=>A.toLowerCase()))},exposeHeaders:m(D.exposeHeaders?.map((A)=>A.toLowerCase())),maxAgeSeconds:D.maxAgeSeconds}}function oa(D,t){if(D.length!==t.length)return!1;return t.every((m,A)=>{let _=m.id!==void 0;return As(Gd(D[A],_))===As(Gd(m,_))})}function aa(D,t){if(D.length!==t.length)return!1;let m=(A)=>As([...A].map((_)=>({..._,storageClassTransitions:_.storageClassTransitions?.length?_.storageClassTransitions:void 0})).sort((_,z)=>_.id.localeCompare(z.id)));return m(D)===m(t)}var wf="r2 cache",Sf=/^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$/;async function Xc(D,t={}){let m=t.log??(()=>{}),A={buckets:[],warnings:[]},_=D.infrastructure?.r2??D.r2,z=Object.entries(_?.buckets??{});if(z.length===0)return m("R2: no buckets declared, skipping."),A;if(!t.apiToken||!t.accountId){let re=[!t.apiToken&&"CLOUDFLARE_API_TOKEN",!t.accountId&&"CLOUDFLARE_ACCOUNT_ID"].filter(Boolean).join(" and "),se=`R2: ${z.length} bucket(s) declared but ${re} is not set, skipping.`;return m(se),A.warnings.push(se),A}let U=t.dryRun===!0,Y=new jo({apiToken:t.apiToken,accountId:t.accountId,fetch:t.fetch}),X=new ge(t.apiToken,{zoneId:t.zoneId,accountId:t.accountId}),ne=new Map;for(let[re,se]of z){let he=se?.name;if(!he||!Sf.test(he)){A.warnings.push(`R2: bucket '${re}' has an invalid name '${he??""}' (3-63 chars: lowercase letters, digits, hyphens), skipping.`);continue}m(`R2: ${U?"checking":"reconciling"} bucket ${he}...`);let we={name:he,changes:[],domains:[]};if(A.buckets.push(we),await Cf(se,{r2:Y,cloudflare:X,dryRun:U,summary:we,warnings:A.warnings,claimed:ne,log:m}),we.changes.length===0)m(`R2: ${he} is in sync.`);else for(let Pe of we.changes)m(`R2: ${he}: ${Pe}`)}return A}async function Cf(D,t){let{r2:m,dryRun:A,summary:_,warnings:z}=t,U=D.name,Y={jurisdiction:D.jurisdiction},X=(we,Pe)=>{let ye=A?Pe&&`would ${Pe}`:we;if(ye)_.changes.push(ye)},ne;if(A)ne=await m.getBucket(U,Y)!==null;else{let{created:we}=await m.ensureBucket(U,{locationHint:D.locationHint,jurisdiction:D.jurisdiction});if(ne=!0,we)X("created","")}if(!ne)X("","create bucket");let re=kf(D.customDomains);await Rs(t,`cors on ${U}`,async()=>{if(!D.cors)return;if(!ne){X("cors updated","set cors");return}let we=await m.getCors(U,Y);if(oa(we,D.cors))return;if(!A)await m.putCors(U,D.cors,Y);X(D.cors.length===0?"cors removed":"cors updated",D.cors.length===0?"remove cors":"update cors")}),await Rs(t,`lifecycle on ${U}`,async()=>{if(!D.lifecycle)return;let we=D.lifecycle.map(Yd);if(ne){let Pe=await m.getLifecycle(U,Y);if(aa(Pe,we))return}if(!A)await m.putLifecycle(U,we,Y);X("lifecycle updated","update lifecycle")}),await Rs(t,`r2.dev URL on ${U}`,async()=>{let we=D.publicDevUrl===!0;if(!ne){if(we)X("r2.dev URL enabled","enable r2.dev URL");return}if((await m.getManagedDomain(U,Y))?.enabled===we)return;if(!A)await m.setManagedDomain(U,we,Y);X(we?"r2.dev URL enabled":"r2.dev URL disabled",we?"enable r2.dev URL":"disable r2.dev URL")});let se=[];if(re.length>0&&ne)try{se=await m.listCustomDomains(U,Y)}catch(we){if(we instanceof Yi)throw we;z.push(`R2: could not list custom domains on ${U}: ${el(we)}`)}let he=new Map;for(let{domain:we,minTLS:Pe}of re){let ye=t.claimed.get(we);if(ye&&ye!==U){z.push(`R2: ${we} is declared on both ${ye} and ${U}; leaving it on ${ye}.`);continue}t.claimed.set(we,U);let Ie=se.find((Re)=>Re.domain.toLowerCase()===we),Le=Pe??"1.2";if(Ie&&Ie.enabled&&(Ie.minTLS??"1.0")===Le){if(_.domains.push({domain:we,status:Qc(Ie)}),Ie.zoneId)he.set(we,Ie.zoneId);continue}await Rs(t,`custom domain ${we} on ${U}`,async()=>{if(Ie){if(!A)await m.updateCustomDomain(U,Ie.domain,{enabled:!0,minTLS:Le},Y);if(X(`custom domain ${we} updated`,`update custom domain ${we}`),_.domains.push({domain:we,status:Qc({...Ie,enabled:!0})}),Ie.zoneId)he.set(we,Ie.zoneId);return}let Re=await t.cloudflare.zoneIdFor(we);if(he.set(we,Re),!A)await m.ensureCustomDomain(U,{domain:we,zoneId:Re,minTLS:Le},Y,se);X(`custom domain ${we} attached`,`attach custom domain ${we}`),_.domains.push({domain:we,status:A?"not attached":"pending"})},()=>_.domains.push({domain:we,status:"error"}))}if(D.cache&&re.length>0)await $f(D,re.map((we)=>we.domain).filter((we)=>t.claimed.get(we)===U),he,t,X)}async function $f(D,t,m,A,_){let z=Kd(D.cache);if(!z||t.length===0)return;let U=new Map;for(let Y of t){let X=m.get(Y);if(!X)try{X=await A.cloudflare.zoneIdFor(Y)}catch(ne){A.warnings.push(`R2: could not resolve the zone for ${Y}, cache rule skipped: ${el(ne)}`);continue}U.set(X,[...U.get(X)??[],Y])}for(let Y of U.values()){let X={action:"set_cache_settings",description:`${Er} ${wf}`,expression:`(${Tr(Y)})`,enabled:!0,action_parameters:z};await Rs(A,`cache rule for ${Y.join(", ")}`,async()=>{let ne=await A.cloudflare.getPhaseRules(Y[0],"http_request_cache_settings");if(Jd(ne,X))return;if(!A.dryRun){let re=await A.cloudflare.putManagedPhaseRules(Y[0],"http_request_cache_settings",[X]);if(!re.success)throw Error(re.message||"cache rule write failed")}_(`cache rule updated for ${Y.join(", ")}`,`update cache rule for ${Y.join(", ")}`)})}}function Kd(D){if(D.edgeTtl===void 0&&D.browserTtl===void 0)return null;let t={cache:!0};if(D.edgeTtl!==void 0)t.edge_ttl={mode:"override_origin",default:D.edgeTtl};if(D.browserTtl!==void 0)t.browser_ttl={mode:"override_origin",default:D.browserTtl};return t}function Jd(D,t){let m=wr(t.expression),A=Ci(t.expression),_=(Y)=>(Y.description||"").startsWith(Er),z=(Y)=>Y.action===t.action&&Y.description===t.description&&(Y.enabled??!0)===(t.enabled??!0)&&Ci(Y.expression)===A&&ca(Y.action_parameters)===ca(t.action_parameters),U=!1;for(let Y of D){if(!_(Y))continue;let X=wr(Y.expression);if(!m.some((re)=>X.includes(re)))continue;if(z(Y)&&m.every((re)=>X.includes(re))&&!U){U=!0;continue}return!1}return U}function Yd(D){let t={id:D.id,enabled:D.enabled??!0,conditions:{prefix:D.prefix??""}};if(D.expireAfterDays!==void 0)t.deleteObjectsTransition={condition:{type:"Age",maxAge:Zc(D.expireAfterDays)}};else if(D.expireOn!==void 0)t.deleteObjectsTransition={condition:{type:"Date",date:D.expireOn}};if(D.abortMultipartUploadsAfterDays!==void 0)t.abortMultipartUploadsTransition={condition:{type:"Age",maxAge:Zc(D.abortMultipartUploadsAfterDays)}};if(D.infrequentAccessAfterDays!==void 0)t.storageClassTransitions=[{storageClass:"InfrequentAccess",condition:{type:"Age",maxAge:Zc(D.infrequentAccessAfterDays)}}];return t}function Zc(D){return Math.round(D*86400)}function kf(D){let t=new Set,m=[];for(let A of D??[]){let _=typeof A==="string"?{domain:A}:A,z=_.domain?.trim().replace(/\.$/,"").toLowerCase();if(!z||t.has(z))continue;t.add(z),m.push({domain:z,minTLS:_.minTLS})}return m}function Qc(D){if(!D.enabled)return"disabled";let t=D.status?.ownership,m=D.status?.ssl;if(t==="active"&&m==="active")return"active";for(let A of[t,m])if(A==="error"||A==="blocked"||A==="deactivated")return A;return"pending"}async function Rs(D,t,m,A){try{await m()}catch(_){if(_ instanceof Yi)throw _;D.warnings.push(`R2: ${t} failed: ${el(_)}`),A?.()}}function el(D){return D instanceof Error?D.message:String(D)}function ca(D){if(Array.isArray(D))return`[${D.map(ca).join(",")}]`;if(D&&typeof D==="object")return`{${Object.entries(D).filter(([,m])=>m!==void 0).sort(([m],[A])=>m.localeCompare(A)).map(([m,A])=>`${JSON.stringify(m)}:${ca(A)}`).join(",")}}`;return JSON.stringify(D)}import{basename as Ef,extname as Tf}from"node:path";import{createHash as Af}from"node:crypto";var Rf="https://api.cloudflare.com/client/v4",io="TS_CLOUD_CONTENT_HASH",Ps="2025-09-01",nl="application/javascript+module",If=10007,Pf=[1e4,9109];class _o extends Error{status;errors;constructor(D,t,m=[]){super(D);this.name="WorkersApiError",this.status=t,this.errors=m}hasCode(D){return this.errors.some((t)=>t.code===D)}}class ma extends _o{permission;constructor(D,t,m,A=[]){super(`The Cloudflare API token is missing the "${D}" permission (${t}). Edit the token in the Cloudflare dashboard (My Profile, API Tokens), add it, then re-run the deploy.`,m,A);this.name="WorkersPermissionError",this.permission=D}}class pa{name="cloudflare-workers";apiToken;accountId;fetchImpl;constructor(D){if(!D.apiToken)throw Error("Cloudflare Workers needs an API token");if(!D.accountId)throw Error("Cloudflare Workers needs an account id");this.apiToken=D.apiToken,this.accountId=D.accountId,this.fetchImpl=D.fetch??((t,m)=>fetch(t,m))}async request(D,t,m){let A={Authorization:`Bearer ${this.apiToken}`},_;if(m instanceof FormData)_=m;else if(m!==void 0)A["Content-Type"]="application/json",_=JSON.stringify(m);let z=await this.fetchImpl(`${Rf}/accounts/${this.accountId}/workers${t}`,{method:D,headers:A,body:_}),U=await z.text(),Y;try{Y=JSON.parse(U)}catch{throw new _o(`Cloudflare Workers API returned ${z.status} with a non-JSON body: ${U.slice(0,200)}`,z.status)}if(!Y.success){let X=Y.errors??[],ne=X.map((re)=>`${re.code}: ${re.message}`).join(", ");if(Df(z.status,X))throw new ma(xf(t),`${D} ${t} -> ${z.status}${ne?`, ${ne}`:""}`,z.status,X);throw new _o(`Cloudflare Workers API error (${z.status}) on ${D} ${t}: ${ne||U.slice(0,200)}`,z.status,X)}return Y.result}scriptPath(D){return`/scripts/${encodeURIComponent(D)}`}async getSettings(D){try{return await this.request("GET",`${this.scriptPath(D)}/settings`)}catch(t){if(t instanceof ma)throw t;if(t instanceof _o&&(t.hasCode(If)||t.status===404))return null;throw t}}async getScript(D){return this.getSettings(D)}async uploadModuleScript(D,t){return this.request("PUT",this.scriptPath(D),Qd(t))}async ensureModuleScript(D,t){let m=Ds(t),A=await this.getSettings(D);if(A&&ga(A,t,m))return{action:"unchanged",hash:m};return await this.uploadModuleScript(D,Xd(t,m)),{action:A?"updated":"created",hash:m}}async listCustomDomains(D={}){let t=new URLSearchParams;if(D.service)t.set("service",D.service);if(D.hostname)t.set("hostname",D.hostname);if(D.zoneId)t.set("zone_id",D.zoneId);let m=t.toString(),A=m?`?${m}`:"";return await this.request("GET",`/domains${A}`)??[]}async attachCustomDomain(D,t){let m={hostname:D,service:t.service,environment:t.environment??"production"};if(t.zoneId)m.zone_id=t.zoneId;return this.request("PUT","/domains",m)}async detachCustomDomain(D){await this.request("DELETE",`/domains/${encodeURIComponent(D)}`)}async ensureCustomDomain(D,t,m){let _=(m??await this.listCustomDomains({hostname:D})).find((U)=>U.hostname.toLowerCase()===D.toLowerCase());if(_&&_.service===t.service)return{action:"unchanged",domain:_};if(_)throw Error(`${D} is attached to the Worker '${_.service}'. Detach it there first (Workers & Pages, ${_.service}, Settings, Domains & Routes); ts-cloud does not move it.`);let z=await this.attachCustomDomain(D,t);return{action:"attached",domain:{...z,hostname:z?.hostname||D}}}}function Df(D,t){if(t.some((m)=>Pf.includes(m.code)))return!0;return(D===401||D===403)&&t.length===0}function xf(D){if(D.startsWith("/domains"))return"Workers Routes: Edit (zone, for Workers custom domains) and Workers Scripts: Edit (account)";return"Workers Scripts: Edit (account)"}function Qd(D){if(!D.modules.some((A)=>A.name===D.mainModule))throw Error(`Worker main module '${D.mainModule}' is not one of the uploaded modules`);let t={main_module:D.mainModule,bindings:D.bindings??[],compatibility_date:D.compatibilityDate??Ps,compatibility_flags:D.compatibilityFlags??[],keep_bindings:["secret_text"]},m=new FormData;m.append("metadata",JSON.stringify(t));for(let A of D.modules)m.append(A.name,new Blob([A.content],{type:A.type}),A.name);return m}function Ds(D){let t={mainModule:D.mainModule,modules:[...D.modules].sort((m,A)=>m.name.localeCompare(A.name)).map((m)=>({name:m.name,type:m.type,sha256:Zd(m.content)})),bindings:[...D.bindings??[]].filter((m)=>m.name!==io).sort((m,A)=>m.name.localeCompare(A.name)),compatibilityDate:D.compatibilityDate??Ps,compatibilityFlags:[...D.compatibilityFlags??[]].sort()};return Zd(tl(t))}function Xd(D,t=Ds(D)){return{...D,bindings:[...(D.bindings??[]).filter((m)=>m.name!==io),{type:"plain_text",name:io,text:t}]}}function ga(D,t,m){let A=D.bindings??[],_=A.find((z)=>z.name===io&&z.type==="plain_text");if(!_||_.text!==m)return!1;return(t.bindings??[]).every((z)=>{let U=A.find((X)=>X.name===z.name);if(!U||U.type!==z.type)return!1;let Y=z.type==="r2_bucket"?"bucket_name":z.type==="plain_text"?"text":void 0;return!Y||U[Y]===z[Y]})}function Zd(D){return Af("sha256").update(D).digest("hex")}function tl(D){if(Array.isArray(D))return`[${D.map(tl).join(",")}]`;if(D&&typeof D==="object")return`{${Object.entries(D).filter(([,m])=>m!==void 0).sort(([m],[A])=>m.localeCompare(A)).map(([m,A])=>`${JSON.stringify(m)}:${tl(A)}`).join(",")}}`;return JSON.stringify(D)}async function rl(D){if(!await Bun.file(D).exists())throw Error(`Worker entry not found: ${D}`);let t;try{t=await Bun.build({entrypoints:[D],target:"browser",format:"esm",minify:!0,external:["cloudflare:*"]})}catch(_){throw Error(`Bundling Worker ${D} failed: ${Nf(_)}`)}if(!t.success)throw Error(`Bundling Worker ${D} failed: ${t.logs.map((_)=>String(_.message??_)).join("; ")}`);if(t.outputs.length!==1)throw Error(`Bundling Worker ${D} produced ${t.outputs.length} files (${t.outputs.map((_)=>_.path).join(", ")}); a Worker is uploaded as a single module, so remove the dynamic imports or asset imports that split it.`);let m=t.outputs[0];if(m.kind!=="entry-point")throw Error(`Bundling Worker ${D} produced a ${m.kind} instead of an entry module`);let A=await m.text();return{name:`${Ef(D,Tf(D))}.js`,content:A,type:nl,size:new TextEncoder().encode(A).byteLength}}function Nf(D){if(D instanceof AggregateError&&D.errors.length>0)return D.errors.map((t)=>t instanceof Error?t.message:String(t?.message??t)).join("; ");return D instanceof Error?D.message:String(D)}import{isAbsolute as Of,resolve as Lf}from"node:path";var Ff="https://api.cloudflare.com/client/v4",jf=/^[a-z0-9][a-z0-9_-]{0,62}$/;async function il(D,t={}){let m=t.log??(()=>{}),A={workers:[],warnings:[]},_=Object.entries(D.infrastructure?.workers??D.workers??{});if(_.length===0)return m("Workers: none declared, skipping."),A;if(!t.apiToken||!t.accountId){let Y=[!t.apiToken&&"CLOUDFLARE_API_TOKEN",!t.accountId&&"CLOUDFLARE_ACCOUNT_ID"].filter(Boolean).join(" and "),X=`Workers: ${_.length} Worker(s) declared but ${Y} is not set, skipping.`;return m(X),A.warnings.push(X),A}let z=D.infrastructure?.r2??D.r2,U={workers:new pa({apiToken:t.apiToken,accountId:t.accountId,fetch:t.fetch}),r2:new jo({apiToken:t.apiToken,accountId:t.accountId,fetch:t.fetch}),cloudflare:new ge(t.apiToken,{zoneId:t.zoneId,accountId:t.accountId}),apiToken:t.apiToken,fetch:t.fetch??((Y,X)=>fetch(Y,X)),projectRoot:t.projectRoot??process.cwd(),dryRun:t.dryRun===!0,warnings:A.warnings,log:m,r2ConfigDomains:Wf(z),r2Buckets:z,claimed:new Map};for(let[Y,X]of _){let ne=X?.name;if(!ne||!jf.test(ne)){A.warnings.push(`Workers: '${Y}' has an invalid name '${ne??""}' (1-63 chars: lowercase letters, digits, '-' and '_'), skipping.`);continue}if(!X.entry){A.warnings.push(`Workers: ${ne} has no entry, skipping.`);continue}m(`Workers: ${U.dryRun?"checking":"reconciling"} ${ne}...`);let re={name:ne,changes:[],domains:[]};A.workers.push(re),await Bf(X,re,U);for(let se of re.changes)m(`Workers: ${ne}: ${se}`)}return A}async function Bf(D,t,m){let A=D.name,_=Of(D.entry)?D.entry:Lf(m.projectRoot,D.entry),z;try{z=await rl(_)}catch(Y){throw Error(`Worker ${A}: ${Mo(Y)}`)}t.changes.push(`bundled ${qf(z.size)}`);let U={mainModule:z.name,modules:[{name:z.name,content:z.content,type:z.type}],bindings:eu(D,m),compatibilityDate:D.compatibilityDate??Ps,compatibilityFlags:D.compatibilityFlags??[]};if(m.dryRun){let Y=await m.workers.getSettings(A);t.changes.push(Y&&ga(Y,U,Ds(U))?"unchanged":"would upload")}else{let{action:Y}=await m.workers.ensureModuleScript(A,U);t.changes.push(Y==="unchanged"?"unchanged":"uploaded")}for(let Y of Vf(D.customDomains))await _f(A,Y,t,m)}async function _f(D,t,m,A){let _=(we)=>{m.domains.push({domain:t,status:we})},z=(we)=>{A.warnings.push(`Workers: ${we}`),_("conflict")},U=A.claimed.get(t);if(U&&U!==D){z(`${t} is declared on both ${U} and ${D}; leaving it on ${U}.`);return}A.claimed.set(t,D);let Y=A.r2ConfigDomains.get(t);if(Y){z(`${t} is also declared as a custom domain of the R2 bucket '${Y}' (r2.buckets). A hostname serves either a bucket or a Worker. Remove it from the bucket's customDomains and detach it in the dashboard (R2, ${Y}, Settings, Custom Domains) first; to serve the bucket through the Worker, bind it with bindings.r2Buckets instead.`);return}let X=await Mf(A);if(X===null){_("error");return}let ne=X.find((we)=>we.hostname.toLowerCase()===t);if(ne?.service===D){_("active");return}if(ne){z(`${t} is attached to the Worker '${ne.service}'. Detach it there first (Workers & Pages, ${ne.service}, Settings, Domains & Routes); it is not moved automatically.`);return}let re=(await zf(A))?.get(t);if(re){z(`${t} is attached to the R2 bucket '${re}' as a custom domain. Detach it first (R2, ${re}, Settings, Custom Domains, then re-run the deploy); it is not detached automatically. To serve the bucket through the Worker, bind it with bindings.r2Buckets.`);return}let se;try{se=await A.cloudflare.zoneIdFor(t)}catch(we){A.warnings.push(`Workers: could not resolve the zone for ${t}, not attached: ${Mo(we)}`),_("error");return}let he=await Uf(A,se,t);if(he.length>0){let we=he.map((Pe)=>`${Pe.type} ${Pe.content}`).join(", ");z(`${t} already has a DNS record (${we}). A Worker custom domain creates its own record, so Cloudflare will not attach over it. Delete the record first (DNS, Records), then re-run the deploy; it is not deleted automatically.`);return}if(A.dryRun){m.changes.push(`would attach custom domain ${t}`),_("not attached");return}try{await A.workers.ensureCustomDomain(t,{service:D,zoneId:se},X),m.changes.push(`custom domain ${t} attached`),X.push({id:"",hostname:t,service:D,zone_id:se}),_("pending")}catch(we){A.warnings.push(`Workers: custom domain ${t} on ${D} failed: ${Mo(we)}`),_("error")}}async function Mf(D){if(D.attached!==void 0)return D.attached;try{D.attached=await D.workers.listCustomDomains()}catch(t){D.warnings.push(`Workers: could not list Worker custom domains, none attached: ${Mo(t)}`),D.attached=null}return D.attached}async function zf(D){if(D.r2AccountDomains!==void 0)return D.r2AccountDomains;let t=new Map;try{let m=(await D.r2.listBuckets()).map((A)=>({name:A.name}));for(let A of Object.values(D.r2Buckets?.buckets??{}))if(A.jurisdiction&&A.jurisdiction!=="default")m.push({name:A.name,jurisdiction:A.jurisdiction});for(let A of m){let _=await D.r2.listCustomDomains(A.name,{jurisdiction:A.jurisdiction}).catch(()=>[]);for(let z of _)t.set(z.domain.toLowerCase(),A.name)}D.r2AccountDomains=t}catch(m){D.log(`Workers: could not check R2 custom domains (${Mo(m)}); relying on the DNS check.`),D.r2AccountDomains=null}return D.r2AccountDomains}async function Uf(D,t,m){try{let A=await D.fetch(`${Ff}/zones/${t}/dns_records?name=${encodeURIComponent(m)}&per_page=100`,{method:"GET",headers:{Authorization:`Bearer ${D.apiToken}`}}),_=await A.json();if(!_?.success)throw Error(`HTTP ${A.status}`);return(_.result??[]).filter((z)=>z.name?.toLowerCase()===m).map((z)=>({type:z.type??"?",content:z.content??""}))}catch(A){return D.log(`Workers: could not read DNS records for ${m} (${Mo(A)}); attaching anyway.`),[]}}function eu(D,t){let m=[],A=new Set,_=(U)=>{if(U===io)return t.warnings.push(`Workers: ${D.name}: binding name ${io} is reserved for ts-cloud, ignored.`),!1;if(A.has(U))return t.warnings.push(`Workers: ${D.name}: binding ${U} is declared twice; the first one wins.`),!1;return A.add(U),!0},z=new Map;for(let U of Object.values(t.r2Buckets?.buckets??{}))if(U.jurisdiction&&U.jurisdiction!=="default")z.set(U.name,U.jurisdiction);for(let[U,Y]of Object.entries(D.bindings?.r2Buckets??{})){if(!_(U))continue;let X=z.get(Y);m.push(X?{type:"r2_bucket",name:U,bucket_name:Y,jurisdiction:X}:{type:"r2_bucket",name:U,bucket_name:Y})}for(let[U,Y]of Object.entries(D.bindings?.vars??{}))if(_(U))m.push({type:"plain_text",name:U,text:String(Y)});return m}function Wf(D){let t=new Map;for(let m of Object.values(D?.buckets??{}))for(let A of m?.customDomains??[]){let _=tu(typeof A==="string"?A:A.domain);if(_)t.set(_,m.name)}return t}function Vf(D){return[...new Set((D??[]).map(tu).filter(Boolean))]}function tu(D){return(D??"").trim().replace(/\.$/,"").toLowerCase()}function qf(D){if(D<1024)return`${D} B`;if(D<1048576)return`${(D/1024).toFixed(1)} KB`;return`${(D/1024/1024).toFixed(1)} MB`}function Mo(D){return D instanceof Error?D.message:String(D)}async function Yf(D){let t=ta();if(t.emptyEnvKey||t.emptyEnvSecret)a("Empty AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY env var detected — likely a misconfigured .env file. Remove the empty lines or fill them in.");e(`AWS credentials: ${t.description}`);try{let A=await new lt(D).getCallerIdentity();if(A.Arn)e(`AWS identity: ${A.Arn}`)}catch(m){a(`Could not verify AWS identity: ${m.message}`)}}function Zf(){try{return cl("git rev-parse --short HEAD",{stdio:["ignore","pipe","ignore"]}).toString().trim()}catch{return Date.now().toString(36)}}async function ol(D,t,m,A){let{ensureManagementDashboard:_}=await import("./chunk-kqxk1pds.js");_(D,{logger:{info:e,warn:a}});let z=D.sites||{},U=Object.entries(z);if(U.length===0)return a("infrastructure.compute is set but no sites are configured — nothing to deploy. Add sites to `cloud.config.ts`."),!0;if(A&&!z[A])return r(`Site '${A}' was not found. Configured sites: ${U.map(([Le])=>Le).join(", ")}`),!1;let Y=U.filter(([Le,Re])=>{if(!Re)return!1;if(A&&Le!==A)return!1;if(I(Re)==="bucket")return a(`Site '${Le}' targets a bucket — skipping compute deploy (handled by the static-site path).`),!1;return qs(Re)});if(A)e(`Deploying only '${A}'. Other sites keep their current release; proxy routes are still regenerated in full.`);let X=D.project.slug,re=(D.infrastructure?.compute||{}).runtime||"bun",se=O({config:D}),he=Zf(),we=new Map,Pe=new Map,ye={step:(Le)=>d(Le),error:(Le)=>r(Le)};if(!await Cs(D,"beforeDeploy",ye))return!1;if(!await Cs(D,"beforeBuild",ye))return!1;for(let[Le,Re]of Y){let De=I(Re);if(i(`Preparing site: ${Le}`),e(`Domain: ${Re.domain||"(none)"}`),e(`Source: ${Re.root}`),e(`Kind: ${De==="server-static"?"static (shipped to /var/www)":"app (systemd service)"}`),De==="server-app")e(`Start: ${Re.start}`),e(`Port: ${Re.port??"(unset)"}`);if(Re.build){d(`Running build: ${Re.build}`);try{cl(Re.build,{stdio:"inherit",cwd:process.cwd()})}catch(et){return r(`Build failed for site '${Le}': ${et.message}`),!1}}let Ee=Vd({root:Re.root,siteName:Le,expectIndex:De==="server-static"});for(let et of Ee)if(et.level==="error")r(et.message);else a(et.message);if(Ee.some((et)=>et.level==="error"))return!1;let Ke=Re.build?void 0:JSON.stringify([Jf(Re.root),[...Re.exclude||[]].sort()]),je=Ke?Pe.get(Ke):void 0;if(je){d(`Reusing package for ${Re.root} → ${je}`),we.set(Le,je);continue}let _e=su(ou(),`${X}-${Le}-${he}.tar.gz`);d(`Packaging ${Re.root} → ${_e}`);try{let et=(Re.exclude||[]).map((Rt)=>`--exclude=${Rt}`);Gf("tar",["czf",_e,"-C",Re.root,...et,"."],{stdio:"inherit",env:{...process.env,COPYFILE_DISABLE:"1"}})}catch(et){return r(`Failed to package '${Le}': ${et.message}`),!1}if(we.set(Le,_e),Ke)Pe.set(Ke,_e)}if(!await Cs(D,"afterBuild",ye))return!1;let Ie=await ro({config:A?{...D,sites:Object.fromEntries(Y)}:D,rpxConfig:A?D:void 0,environment:t,driver:se,sha:he,runtime:re,tarballForSite:(Le)=>{let Re=we.get(Le);if(!Re)throw Error(`Missing tarball for site '${Le}'`);return Re},logger:{info:(Le)=>e(Le),warn:(Le)=>a(Le),error:(Le)=>r(Le),step:(Le)=>d(Le),success:(Le)=>n(Le)}});if(Ie&&!await Cs(D,"afterDeploy",ye))return!1;return Ie}async function wa(D){let t=new Gr;if(d("Running pre-deployment security scan..."),D.exclude?.length)e(`Excluding from the scan: ${D.exclude.join(", ")}`);let m=await t.scan({directory:D.sourceDir,failOnSeverity:D.failOnSeverity||"critical",skipPatterns:D.skipPatterns,exclude:D.exclude});return{passed:m.passed,result:m}}function Qf(){let D=su(ou(),`ts-cloud-empty-scan-${process.pid}`);return iu(D,{recursive:!0}),D}function Sa(D){let{summary:t,findings:m,scannedFiles:A,duration:_}=D;if(e(`
|
|
376
376
|
Scanned ${A} files in ${_}ms`),t.critical>0)r(` Critical: ${t.critical}`);else e(` Critical: ${t.critical}`);if(t.high>0)a(` High: ${t.high}`);else e(` High: ${t.high}`);if(e(` Medium: ${t.medium}`),e(` Low: ${t.low}`),m.length>0){e(`
|
|
377
377
|
Findings:`);let z=m.filter((re)=>re.pattern.severity==="critical"),U=m.filter((re)=>re.pattern.severity==="high"),Y=m.filter((re)=>re.pattern.severity==="medium"),X=m.filter((re)=>re.pattern.severity==="low"),ne=(re,se,he)=>{if(re.length>0){console.log(`
|
|
378
378
|
${jt(`[${se}]`,he)}`);for(let we of re.slice(0,10))e(` ${we.pattern.name}`),e(` File: ${we.file}:${we.line}`),e(` Match: ${we.match}`);if(re.length>10)e(` ... and ${re.length-10} more ${se.toLowerCase()} findings`)}};ne(z,"CRITICAL","red"),ne(U,"HIGH","yellow"),ne(Y,"MEDIUM","blue"),ne(X,"LOW","gray")}}function au(D,t){let m=Object.keys(D.environments??{});if(t&&m.includes(t))return t;if(m.includes("production"))return"production";if(m[0])return m[0];throw Error("A configured environment is required for security policy evaluation")}function sl(D,t,m){let A=au(D,t),_=y(process.cwd(),D);try{let z=new xt(_.store);ei(_);let U=le(_,A);if(m)ac(z,U,m);else cc(z,U);let Y=ar(z,{scope:U,desiredConfigHash:_.project.desiredConfigHash});return e(`Security policy: ${Y.decision.policyId} v${Y.decision.policyVersion} — ${Y.decision.outcome.toUpperCase()}`),e(`Security decision: ${Y.decision.explanation}`),{allowed:Y.decision.outcome!=="block",explanation:Y.decision.explanation}}finally{_.store.close()}}async function Xf(D,t){let m=au(D),A=y(process.cwd(),D);try{let _=new xt(A.store);ei(A);let z=le(A,m),U=t.imageSha256?`${t.imageRef}@sha256:${t.imageSha256.replace(/^sha256:/,"")}`:t.imageRef;d("Scanning the built image and producing release security artifacts...");let Y=await lc(_,{scope:z,releaseId:U,imageRef:t.imageRef,imageSha256:t.imageSha256,artifactRoot:t.artifactRoot,invocationId:t.invocationId,startedAt:t.startedAt,externalParameters:{imageRef:t.imageRef,artifactRoot:t.artifactRoot}});e(`Image scanner: ${Y.scan.run.status} (${Y.scan.findings.length} findings)`),e(`Release SBOM: ${Y.sbomSource==="image"?"full image inventory":Y.sbomSource==="manifest"?"package-manifest fallback":"unavailable"}`);let X=ar(_,{scope:z,operationId:t.invocationId,desiredConfigHash:A.project.desiredConfigHash});return e(`Release policy: ${X.decision.policyId} v${X.decision.policyVersion} — ${X.decision.outcome.toUpperCase()}`),e(`Release decision: ${X.decision.explanation}`),{allowed:X.decision.outcome!=="block",explanation:X.decision.explanation}}finally{A.store.close()}}function eg(D){try{let t=y(process.cwd(),D),m=new Yt(t.store).current();if(!m.attackMode&&!m.mitigationPause&&m.ipRules.allow.length===0&&m.ipRules.block.length===0)return D;let A=Zo(D,m);return e(`Applying protection controls: ${m.ipRules.allow.length} allow, ${m.ipRules.block.length} block${m.attackMode?", attack mode ON":""}${m.mitigationPause?", mitigation PAUSED":""}`),A}catch{return D}}function ll(D){D.command("deploy:security-scan","Run pre-deployment security scan").option("--source <path>","Source directory to scan",{default:"."}).option("--fail-on <severity>","Fail on severity level (critical, high, medium, low)",{default:"critical"}).option("--skip-patterns <patterns>","Comma-separated list of pattern names to skip").action(async(t)=>{i("Pre-Deployment Security Scan");try{let m=t?.source||".",A=t?.failOn||"critical",_=t?.skipPatterns?.split(",").map((Y)=>Y.trim())||[];if(!vn(m)){r(`Source directory not found: ${m}`);return}e(`Source: ${m}`),e(`Fail on: ${A} or higher severity`);let{passed:z,result:U}=await wa({sourceDir:m,failOnSeverity:A,skipPatterns:_});if(Sa(U),z)n(`
|
|
@@ -382,7 +382,7 @@ Recommendations:`),e(" 1. Remove any hardcoded credentials from your code"),e("
|
|
|
382
382
|
✗ Deployment configuration is invalid:`);for(let St of Y)r(` • ${St}`);process.exitCode=1;return}if(t?.dryRun){if(t.site&&!U.sites?.[t.site]){r(`Site '${t.site}' was not found. Configured sites: ${Object.keys(U.sites||{}).join(", ")||"none"}`),process.exitCode=1;return}let St=H(U),Bt=t.stack||T(U,A),un=B(U),mn=U.project.region||"us-east-1",Sr=t.site?[t.site]:Object.keys(U.sites||{});i("Deployment Plan"),e(`Cloud provider: ${un}`),e(`Mode: ${St}`),e(`Stack: ${Bt}`),e(`Region: ${mn}`),e(`Environment: ${A}`),e(`Sites: ${Sr.length>0?Sr.join(", "):"none"}`),await nu(U,{dryRun:!0}),await ru(U,{dryRun:!0}),n("Dry run complete. No infrastructure, DNS, certificates, or application files were changed.");return}if(B(U)==="aws"){let St=U.project?.region||"us-east-1";await Yf(St)}let ne;if(!t?.skipSecurityScan){let St=zc(U,t?.site);if(!St)e("This deploy ships no files — every site in scope is a redirect or proxy route, so only gateway routes and certificates change. Scanning an empty artifact.");let{result:Bt}=await wa({sourceDir:St?process.cwd():Qf(),failOnSeverity:t?.securityFailOn||"critical",exclude:U.infrastructure?.security?.scan?.exclude});Sa(Bt),ne=Bt}else a(`Security scan skipped (--skip-security-scan)
|
|
383
383
|
`);let re=sl(U,A,ne);if(!re.allowed){r(`
|
|
384
384
|
✗ Deployment blocked by security policy: ${re.explanation}`),e("Remediate the findings or create an authorized, time-limited waiver in the Security posture center."),process.exitCode=1;return}if(n(`✓ Security policy allows this deployment
|
|
385
|
-
`),!await nu(U)){process.exitCode=1;return}if(!await ru(U)){process.exitCode=1;return}if(H(U)==="serverless"){d("Serverless project detected: deploying the Lambda application");let{deployServerlessApp:St}=await import("./chunk-
|
|
385
|
+
`),!await nu(U)){process.exitCode=1;return}if(!await ru(U)){process.exitCode=1;return}if(H(U)==="serverless"){d("Serverless project detected: deploying the Lambda application");let{deployServerlessApp:St}=await import("./chunk-1fj03993.js");await St(U,A,{}),n("Serverless deployment complete");return}let se=t?.stack||T(U,A),he=U.project.region||"us-east-1",we=U.infrastructure?.deployStack!==!1,Pe=U.sites&&Object.keys(U.sites).length>0,ye=U.infrastructure?.dns?.provider;if(Pe&&ye){if(await og(U,t?.site,ye,he,t?.skipDnsVerification,A,m),!we){if(U.infrastructure?.compute){let St=Object.values(U.sites).map((Bt)=>Bt.domain).filter((Bt)=>!!Bt);if(St.length>0)d("Syncing CloudFront dynamic HTTP methods for app domains..."),await ia(St)}return}}if(!we){e("Infrastructure stack deployment disabled (infrastructure.deployStack: false)");return}let Ie=B(U);if(e(`Cloud provider: ${Ie}`),e(`Stack: ${se}`),e(`Region: ${he}`),e(`Environment: ${A}`),(Ie==="hetzner"||U.infrastructure?.compute?.mode==="server")&&ko(U)){d(`Provisioning ${Ie} compute infrastructure...`);let St=O({config:U,provider:Ie});if(!St.provisionComputeInfrastructure){r(`${Ie} driver does not support compute provisioning`),process.exitCode=1;return}let Bt=await St.provisionComputeInfrastructure({config:U,environment:A});if(n(`${Ie} compute infrastructure ready`),Bt.appPublicIp)e(`App server: ${Bt.appPublicIp}`);if(Bt.appInstanceId)e(`Server ID: ${Bt.appInstanceId}`);if(!await ol(U,A,he,t?.site))r(`App deploy to ${Ie} compute reported a failure`),process.exitCode=1;else{if(n(`App deployed to ${Ie} compute`),t?.skipDnsVerification)e("DNS verification and record reconciliation skipped (--skip-dns-verification)");else await ng(U,Bt.appPublicIp,ye,Bt.appPublicIpv6),await rg(U,ye);let mn=await no({config:U,environment:A,driver:St,logger:{info:e,warn:a,error:r,step:d,success:n}});if(!mn)r("App deployed, but rpx TLS certificate reconciliation failed");if(!mn)process.exitCode=1;if(!t?.skipDnsVerification)await ig(U,Bt.appPublicIp,Bt.appPublicIpv6)}return}d("Generating CloudFormation template...");let Re=new Eo({config:U,environment:A});Re.generate();let De=Re.toJSON(),Ee=JSON.parse(De);if(Object.keys(Ee.Resources||{}).length===0){e(`No infrastructure resources to deploy for environment '${A}' — skipping stack ${se}.`);return}d("Validating template...");let je=Xo(Ee),_e=us(De),et=ms(Ee),Rt=[...je.errors,..._e.errors,...et.errors];if(Rt.length>0){r("Template validation failed:");for(let St of Rt)r(` - ${St.path}: ${St.message}`);return}let Et=[...je.warnings,..._e.warnings,...et.warnings];if(Et.length>0)for(let St of Et)a(` - ${St.path}: ${St.message}`);n("Template validated successfully");let wt=Object.keys(Ee.Resources).length;e(`
|
|
386
386
|
Resources to deploy: ${wt}`);let Pt={};for(let St of Object.values(Ee.Resources)){let Bt=St.Type;Pt[Bt]=(Pt[Bt]||0)+1}for(let[St,Bt]of Object.entries(Pt).sort((un,mn)=>mn[1]-un[1]).slice(0,5))e(` - ${St}: ${Bt}`);if(!(m||await u(`
|
|
387
387
|
Deploy now?`,!0))){e("Deployment cancelled");return}let _t=new v(he),nt=va(Ee);if(nt.length>0){let{ensureFailoverReplicaBuckets:St}=await import("./chunk-gkdr09xx.js");d("Ensuring failover replica buckets..."),await St(nt,(Bt)=>e(` ${Bt}`))}d("Checking stack status...");let zt=!1;try{let St=await _t.describeStacks({stackName:se});zt=St.Stacks&&St.Stacks.length>0}catch(St){zt=!1}if(zt){e("Stack exists, updating...");let St=new s("Updating CloudFormation stack...");St.start();try{await _t.updateStack({stackName:se,templateBody:De,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:U.project.name},{Key:"Environment",Value:A},{Key:"ManagedBy",Value:"ts-cloud"}]}),St.succeed("Update initiated"),d("Waiting for stack update to complete..."),await _t.waitForStack(se,"stack-update-complete"),n("Stack updated successfully!")}catch(Bt){if(Bt.message.includes("No updates are to be performed"))St.succeed("No changes detected"),e("Stack is already up to date — continuing to app deploy");else throw Bt}}else{e("Creating new stack...");let St=new s("Creating CloudFormation stack...");St.start(),await _t.createStack({stackName:se,templateBody:De,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:U.project.name},{Key:"Environment",Value:A},{Key:"ManagedBy",Value:"ts-cloud"}]}),St.succeed("Stack creation initiated"),d("Waiting for stack creation to complete..."),await _t.waitForStack(se,"stack-create-complete"),n("Stack created successfully!")}let Vt=await _t.getStackOutputs(se);if(Ce(`Deployment Complete!
|
|
388
388
|
|
|
@@ -395,14 +395,14 @@ View in console:
|
|
|
395
395
|
https://console.aws.amazon.com/cloudformation/home?region=${he}#/stacks/stackinfo?stackId=${encodeURIComponent(se)}`,"green"),Object.keys(Vt).length>0){e(`
|
|
396
396
|
Stack Outputs:`);for(let[St,Bt]of Object.entries(Vt))e(` - ${St}: ${Bt}`)}if(nt.length>0){let{grantFailoverReplicaAccess:St}=await import("./chunk-gkdr09xx.js");d("Granting the distribution read access to its failover replicas..."),await St(nt,Vt,(Bt)=>e(` ${Bt}`))}if(U.infrastructure?.storage)for(let[St,Bt]of Object.entries(U.infrastructure.storage)){if(!Bt.website||!Bt.root)continue;let un=Vt[`${St}BucketName`]||Vt.FrontendBucketName,mn=Vt[`${St}CloudFrontDistributionId`];if(!un){a(`Could not find bucket name for storage '${St}' — skipping file upload`);continue}if(!vn(Bt.root)){a(`Source directory not found: ${Bt.root} — skipping file upload for '${St}'`);continue}d(`Uploading files from ${Bt.root} to s3://${un}...`);let{uploadStaticFiles:Sr,invalidateCache:Li}=await import("./chunk-thph4z3k.js"),fn=await Sr({sourceDir:Bt.root,bucket:un,region:he,onProgress:(Ht,wn,Cn)=>{if(Ht%10===0||Ht===wn)e(` ${Ht}/${wn}: ${Cn}`)}});if(fn.errors.length>0)a(`Upload completed with errors: ${fn.errors.join(", ")}`);else{let Ht=fn.skipped>0?`Uploaded ${fn.uploaded} files (${fn.skipped} unchanged)`:`Uploaded ${fn.uploaded} files`;n(Ht)}if(mn&&fn.uploaded>0){d("Invalidating CloudFront cache...");let{invalidationId:Ht}=await Li(mn);n(`Cache invalidation created: ${Ht}`)}}if(nt.length>0){let{seedFailoverReplicas:St}=await import("./chunk-gkdr09xx.js");d("Syncing failover replicas..."),await St(nt,(Bt)=>e(` ${Bt}`))}if(U.infrastructure?.compute)await ol(U,A,he,t?.site);if(U.infrastructure?.compute&&U.sites){let St=Object.values(U.sites).map((Bt)=>Bt.domain).filter((Bt)=>!!Bt);if(St.length>0)d("Syncing CloudFront dynamic HTTP methods for app domains..."),await ia(St)}}catch(z){if(r(`Deployment failed: ${z.message}`),process.exitCode=1,z.stack)e(`
|
|
397
397
|
Stack trace:`),console.error(z.stack)}finally{if(_)await _()}}),D.command("deploy:server","Deploy EC2 infrastructure").option("--env <environment>","Environment (production, staging, development)").option("--site <name>","Deploy specific site only (routes are still regenerated in full)").action(async(t)=>{i("Deploying Server Infrastructure");try{let m=await Fe(),A=t?.env||"production",_=m.project.region||"us-east-1",z=$r(m);if(z){r(z),process.exitCode=1;return}if(!ko(m)){r("No `infrastructure.compute` configured — nothing to deploy as a server."),e("Add an EC2 compute block to cloud.config, or use `cloud deploy:serverless` for Lambda apps."),process.exitCode=1;return}if(e(`Region: ${_}`),e(`Environment: ${A}`),!await ol(m,A,_,t?.site)){process.exitCode=1;return}if(m.sites){let Y=Object.values(m.sites).map((X)=>X.domain).filter((X)=>!!X);if(Y.length>0)d("Syncing CloudFront dynamic HTTP methods for app domains..."),await ia(Y)}n(`
|
|
398
|
-
Server deployment complete!`)}catch(m){r(`Deployment failed: ${m.message}`),process.exitCode=1}}),D.command("deploy:serverless","Deploy a serverless application (http/queue/cli Lambda functions)").option("--env <environment>","Environment (production, staging, development)").option("--skip-build","Skip local build hooks").option("--skip-hooks","Skip remote deploy hooks (e.g. migrations)").option("--redeploy","Re-activate the last build without rebuilding").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",_=$r(m);if(_){r(_),process.exitCode=1;return}let{deployServerlessApp:z,redeployServerlessApp:U}=await import("./chunk-
|
|
398
|
+
Server deployment complete!`)}catch(m){r(`Deployment failed: ${m.message}`),process.exitCode=1}}),D.command("deploy:serverless","Deploy a serverless application (http/queue/cli Lambda functions)").option("--env <environment>","Environment (production, staging, development)").option("--skip-build","Skip local build hooks").option("--skip-hooks","Skip remote deploy hooks (e.g. migrations)").option("--redeploy","Re-activate the last build without rebuilding").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",_=$r(m);if(_){r(_),process.exitCode=1;return}let{deployServerlessApp:z,redeployServerlessApp:U}=await import("./chunk-1fj03993.js");if(t?.redeploy){await U(m,A);return}await z(m,A,{skipBuild:t?.skipBuild,skipDeployHooks:t?.skipHooks})}catch(m){r(`Deployment failed: ${m.message}`),process.exitCode=1}}),D.command("serverless:rollback","Roll back a serverless app to the previous build").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{rollbackServerlessApp:_}=await import("./chunk-1fj03993.js");await _(m,A)}catch(m){r(`Rollback failed: ${m.message}`),process.exitCode=1}}),D.command("deploy:rollback [site]","Roll a compute site back to a previous release").option("--env <environment>","Environment (production, staging, development)").option("--to <release>","Release id to roll back to (default: the previous release)").option("--dry-run","Say which release it would roll back to, and change nothing").option("--all","Every configured site, instead of one").option("--only-if-live <release>","Only roll back a site whose live release is this commit (what a failed deploy switched)").action(async(t,m)=>{i("Rolling Back Release");try{let A=await Fe(),_=m?.env||"production",z=m?.all?Object.keys(A.sites||{}):[t||Object.keys(A.sites||{})[0]].filter(Boolean);if(z.length===0){r("No site configured to roll back."),process.exitCode=1;return}let{rollbackComputeSite:U}=await import("./chunk-hhhrze6m.js"),Y=O({config:A});for(let X of z){let ne=await U({driver:Y,slug:A.project.slug,environment:_,logger:f},{siteName:X,to:m?.to,dryRun:m?.dryRun,onlyIfLive:m?.onlyIfLive});for(let re of ne.perInstance||[])e(` ${X} ${re.instanceId}: ${re.output?.trim()||re.status}`);if(!ne.success)r(`Rollback of ${X} failed: ${ne.error||"unknown error"}`),process.exitCode=1}}catch(A){r(`Rollback failed: ${A.message}`),process.exitCode=1}}),D.command("deploy:history [site]","Show a compute site's deployment history").option("--env <environment>","Environment (production, staging, development)").option("--limit <n>","Number of entries to show",{default:"20"}).action(async(t,m)=>{i("Deployment History");try{let A=await Fe(),_=m?.env||"production",z=t||Object.keys(A.sites||{})[0];if(!z){r("No site configured."),process.exitCode=1;return}let{getComputeDeployHistory:U}=await import("./chunk-hhhrze6m.js"),Y=await U({driver:O({config:A}),slug:A.project.slug,environment:_,logger:f},{siteName:z,limit:Number.parseInt(m?.limit||"20",10)||20});if(!Y.success){r(`Could not read history: ${Y.error||"unknown error"}`),process.exitCode=1;return}for(let X of Y.perInstance||[])e(`
|
|
399
399
|
${X.instanceId}:`),e(X.output?.trimEnd()||"(no output)")}catch(A){r(`History lookup failed: ${A.message}`),process.exitCode=1}}),D.command("deploy:recipe <name> <script>","Run a reusable server recipe (a local bash file) across servers").option("--env <environment>","Environment (production, staging, development)").option("--user <user>","User to run the recipe as",{default:"root"}).action(async(t,m,A)=>{i(`Running Recipe: ${t}`);try{if(!vn(m)){r(`Recipe script not found: ${m}`),process.exitCode=1;return}let _=await Fe(),z=A?.env||"production",U=al(m,"utf8").split(`
|
|
400
|
-
`),{runComputeRecipe:Y}=await import("./chunk-
|
|
401
|
-
${ne.instanceId}:`),e(ne.output?.trimEnd()||"(no output)");if(!X.success)process.exitCode=1}catch(_){r(`Recipe failed: ${_.message}`),process.exitCode=1}}),D.command("db:restore-backup [from]","Restore the app database from an on-box ts-backups dump (latest, or a given file)").option("--env <environment>","Environment (production, staging, development)").action(async(t,m)=>{i("Database Restore");try{let A=await Fe(),_=m?.env||"production",{restoreDatabaseBackup:z}=await import("./chunk-
|
|
402
|
-
(recorded as #${U} — re-run with \`cloud command:again ${U}\`)`)}catch(A){r(`Command failed: ${A.message}`),process.exitCode=1}}),D.command("command:history","List recorded CLI-function command invocations").option("--env <environment>","Environment (production, staging, development)").option("--limit <n>","Show at most this many (most recent)",{default:"20"}).action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{listCommandHistory:_}=await import("./chunk-
|
|
403
|
-
(recorded as #${X.id})`)}catch(A){r(`Command failed: ${A.message}`),process.exitCode=1}}),D.command("serverless:db-shell <sql>","Run a SQL statement against a private serverless database (via the CLI function)").option("--env <environment>","Environment (production, staging, development)").action(async(t,m)=>{try{let A=await Fe(),_=m?.env||"production",{runDbQuery:z}=await import("./chunk-
|
|
404
|
-
`),"green")}catch(m){r(`Layer build failed: ${m.message}`),process.exitCode=1}}),D.command("serverless:build-node-layer","Build + publish a ts-cloud Node custom runtime layer (any version, incl. 24)").option("--arch <architecture>","x86_64 or arm64",{default:"x86_64"}).option("--node <version>","Node version (e.g. 24)",{default:"24"}).option("--name <name>","Layer name",{default:"tscloud-node"}).option("--bucket <bucket>","S3 bucket to stage the layer zip (defaults to {slug}-layers)").option("--region <region>","AWS region").action(async(t)=>{i("Building Node runtime layer");try{let m=await Fe().catch(()=>null),A=t?.region||m?.project.region||"us-east-1",_=t?.arch||"x86_64",z=t?.node||"24",{buildNodeRuntimeLayerZip:U}=await import("./chunk-n2c6y0k1.js"),{S3Client:Y}=await import("./chunk-d0tmmzy5.js"),{LambdaClient:X}=await import("./chunk-
|
|
405
|
-
`),"green")}catch(m){r(`Layer build failed: ${m.message}`),process.exitCode=1}}),D.command("serverless:build-bun-layer","Build + publish a ts-cloud Bun custom runtime layer").option("--arch <architecture>","x86_64 or arm64",{default:"x86_64"}).option("--bun <version>",'Bun version (e.g. 1.3.13, or "latest")',{default:"latest"}).option("--name <name>","Layer name",{default:"tscloud-bun"}).option("--bucket <bucket>","S3 bucket to stage the layer zip (defaults to {slug}-layers)").option("--region <region>","AWS region").action(async(t)=>{i("Building Bun runtime layer");try{let m=await Fe().catch(()=>null),A=t?.region||m?.project.region||"us-east-1",_=t?.arch||"x86_64",z=t?.bun||"latest",{buildBunRuntimeLayerZip:U}=await import("./chunk-n2c6y0k1.js"),{S3Client:Y}=await import("./chunk-d0tmmzy5.js"),{LambdaClient:X}=await import("./chunk-
|
|
400
|
+
`),{runComputeRecipe:Y}=await import("./chunk-hhhrze6m.js"),X=await Y({driver:O({config:_}),slug:_.project.slug,environment:z,logger:f},{name:t,script:U,user:A?.user});for(let ne of X.perInstance||[])e(`
|
|
401
|
+
${ne.instanceId}:`),e(ne.output?.trimEnd()||"(no output)");if(!X.success)process.exitCode=1}catch(_){r(`Recipe failed: ${_.message}`),process.exitCode=1}}),D.command("db:restore-backup [from]","Restore the app database from an on-box ts-backups dump (latest, or a given file)").option("--env <environment>","Environment (production, staging, development)").action(async(t,m)=>{i("Database Restore");try{let A=await Fe(),_=m?.env||"production",{restoreDatabaseBackup:z}=await import("./chunk-hhhrze6m.js"),U=await z({driver:O({config:A}),slug:A.project.slug,environment:_,logger:f},{database:w(A),from:t});for(let Y of U.perInstance||[])e(` ${Y.instanceId}: ${Y.output?.trim()||Y.status}`);if(!U.success)r(`Restore failed: ${U.error||"unknown error"}`),process.exitCode=1}catch(A){r(`Restore failed: ${A.message}`),process.exitCode=1}}),D.command("quick-deploy","Generate a push-to-deploy CI pipeline for your git provider (Forge Quick Deploy)").option("--env <environment>","Environment to deploy on push",{default:"production"}).option("--provider <provider>","CI provider (github, gitlab, bitbucket); defaults to the origin remote").option("--site <site>","Deploy only this configured site").option("--skip-dns-verification","Generate a deploy that skips DNS verification and record creation").option("--ssh-key-secret <name>","CI secret/variable containing the Hetzner deployment SSH private key",{default:"SSH_PRIVATE_KEY"}).option("--force","Overwrite an existing pipeline file").action(async(t)=>{i("Quick Deploy (push-to-deploy)");try{let m=await Fe(),{buildQuickDeployCi:A,inferQuickDeployProvider:_}=await import("./chunk-rdkr74y2.js"),z=t?.provider?.toLowerCase();if(z&&!["github","gitlab","bitbucket"].includes(z))throw Error(`Unsupported CI provider '${t?.provider}'. Use github, gitlab, or bitbucket.`);let U=z;if(!U)try{let X=cl("git remote get-url origin",{stdio:["ignore","pipe","ignore"]}).toString().trim();U=_(X)}catch{}let Y=A(m,t?.env||"production",{provider:U,site:t?.site,skipDnsVerification:t?.skipDnsVerification,setup:vn("pantry.lock")?"pantry":"bun",sshPrivateKeySecret:t?.sshKeySecret});if(!Y){a("Could not resolve a GitHub, GitLab, or Bitbucket provider from --provider, the origin remote, or a configured site repository."),process.exitCode=1;return}if(vn(Y.path)&&!t?.force){a(`${Y.path} already exists — re-run with --force to overwrite.`);return}iu(Kf(Y.path),{recursive:!0}),Hf(Y.path,Y.content),n(`Wrote ${Y.path} (${Y.provider}, deploys on push to '${Y.branch}').`),e("Next: commit it and add your provider credentials as CI secrets/variables.")}catch(m){r(`Quick deploy setup failed: ${m.message}`),process.exitCode=1}}),D.command("down","Put the serverless app into maintenance mode (503)").option("--env <environment>","Environment (production, staging, development)").option("--secret <secret>","Bypass secret (send as x-maintenance-bypass header)").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{setMaintenance:_}=await import("./chunk-1fj03993.js");await _(m,A,!0,t?.secret)}catch(m){r(`Failed to enable maintenance mode: ${m.message}`),process.exitCode=1}}),D.command("up","Bring the serverless app out of maintenance mode").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{setMaintenance:_}=await import("./chunk-1fj03993.js");await _(m,A,!1)}catch(m){r(`Failed to disable maintenance mode: ${m.message}`),process.exitCode=1}}),D.command("command <cmd>","Run a command on the serverless CLI function (e.g. migrations)").option("--env <environment>","Environment (production, staging, development)").action(async(t,m)=>{try{let A=await Fe(),_=m?.env||"production",{runAndRecordCommand:z}=await import("./chunk-1fj03993.js"),{id:U,output:Y}=await z(A,_,t);if(e(Y),U>0)e(`
|
|
402
|
+
(recorded as #${U} — re-run with \`cloud command:again ${U}\`)`)}catch(A){r(`Command failed: ${A.message}`),process.exitCode=1}}),D.command("command:history","List recorded CLI-function command invocations").option("--env <environment>","Environment (production, staging, development)").option("--limit <n>","Show at most this many (most recent)",{default:"20"}).action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{listCommandHistory:_}=await import("./chunk-1fj03993.js"),z=await _(m,A);if(!z.length){e("No command history yet.");return}let U=Number(t?.limit??20);i(`Command history — ${m.project.slug} (${A})`),c(["#","When","Status","Command"],z.slice(-U).reverse().map((Y)=>[String(Y.id),Y.timestamp,Y.status,Y.command]))}catch(m){r(`Failed to read history: ${m.message}`),process.exitCode=1}}),D.command("command:again [id]","Re-run a recorded command (defaults to the most recent)").option("--env <environment>","Environment (production, staging, development)").action(async(t,m)=>{try{let A=await Fe(),_=m?.env||"production",{getCommandRecord:z,runAndRecordCommand:U}=await import("./chunk-1fj03993.js"),Y=await z(A,_,t?Number(t):void 0);if(!Y){r(t?`No command #${t} in history.`:"No command history to re-run."),process.exitCode=1;return}e(`Re-running #${Y.id}: ${Y.command}`);let X=await U(A,_,Y.command);if(e(X.output),X.id>0)e(`
|
|
403
|
+
(recorded as #${X.id})`)}catch(A){r(`Command failed: ${A.message}`),process.exitCode=1}}),D.command("serverless:db-shell <sql>","Run a SQL statement against a private serverless database (via the CLI function)").option("--env <environment>","Environment (production, staging, development)").action(async(t,m)=>{try{let A=await Fe(),_=m?.env||"production",{runDbQuery:z}=await import("./chunk-1fj03993.js"),U=await z(A,_,t);e(U)}catch(A){r(`Query failed: ${A.message}`),process.exitCode=1}}),D.command("serverless:db-scale <min> <max>","Rescale the serverless Aurora cluster (min/max ACUs)").option("--env <environment>","Environment (production, staging, development)").action(async(t,m,A)=>{i("Scaling serverless database");try{let _=await Fe(),z=A?.env||"production",U=Number(t),Y=Number(m);if(!(U>0)||!(Y>=U)){r("Provide valid capacities: min > 0 and max >= min (e.g. `serverless:db-scale 0.5 8`)."),process.exitCode=1;return}let{scaleServerlessDatabase:X}=await import("./chunk-1fj03993.js");await X(_,z,U,Y)}catch(_){r(`Scale failed: ${_.message}`),process.exitCode=1}}),D.command("serverless:db-restore","Restore the serverless Aurora cluster to a point in time (as a new cluster)").option("--env <environment>","Environment (production, staging, development)").option("--to <timestamp>","Restore to this ISO-8601 time (UTC)").option("--latest","Restore to the latest restorable time").option("--target <id>","New cluster identifier (defaults to <cluster>-restore-<stamp>)").action(async(t)=>{i("Restoring serverless database");try{let m=await Fe(),A=t?.env||"production",_;if(t?.to){if(_=new Date(t.to),Number.isNaN(_.getTime())){r(`Invalid --to timestamp: ${t.to} (use ISO-8601, e.g. 2026-06-18T10:00:00Z).`),process.exitCode=1;return}}let{restoreServerlessDatabase:z}=await import("./chunk-1fj03993.js");await z(m,A,{toTime:_,latest:t?.latest,target:t?.target})}catch(m){r(`Restore failed: ${m.message}`),process.exitCode=1}}),D.command("serverless:info","Show an operational summary of a deployed serverless app").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{try{let m=await Fe(),A=t?.env||"production",{serverlessInfo:_}=await import("./chunk-1fj03993.js"),z=await _(m,A);if(i(`${z.slug} (${z.environment})`),e(`Stack: ${z.stackStatus}`),e(`Region: ${z.region}`),z.endpoint)e(`URL: ${z.endpoint}`);if(z.assetUrl)e(`Assets: ${z.assetUrl}`);if(e(`Scheduler:${z.scheduler==="off"?" off":` ${z.scheduler}`}`),e(`Queues: ${z.queues.length?z.queues.join(", "):"none"}`),e(`Warming: ${z.provisionedConcurrency>0?`provisioned concurrency ×${z.provisionedConcurrency}`:"none / ping-warm"}`),z.lastRelease)e(`Release: ${z.lastRelease.sha.slice(0,12)} @ ${z.lastRelease.timestamp}`);c(["Function","Version","Provisioned"],z.functions.map((U)=>[U.name,U.version,U.provisioned?`${U.provisioned.status} ${U.provisioned.allocated}/${U.provisioned.requested}`:"—"]))}catch(m){r(`Failed to read info: ${m.message}`),process.exitCode=1}}),D.command("dashboard:build","Build the management dashboard with live data baked in").option("--env <environment>","Environment (production, staging, development)").option("--ui <dir>","UI directory (default ./ui)",{default:"ui"}).action(async(t)=>{i("Building management dashboard (live data)");try{let{existsSync:m}=await import("node:fs"),A=t?.ui||"ui";if(!m(`${A}/package.json`)){r(`No UI project at ${A}/ (expected ${A}/package.json). Pass --ui <dir>.`),process.exitCode=1;return}let _=await Fe(),z=t?.env||"production";d("Gathering live data from AWS");let U=null;if(_.environments?.[z]?.app){let{resolveDashboardData:re}=await import("./chunk-2pzh0wxc.js"),se=await re(_,z).catch((he)=>(a(`Serverless data unavailable (${he.message}) — that view uses sample data.`),null));if(se)U={...U??{},...se}}if(_.infrastructure?.compute){let{resolveServerDashboardData:re}=await import("./chunk-akf9j8ps.js"),se=await re(_,z).catch(()=>null);if(se){if(se._serverReachable===!1)a("Server box not reachable over SSM — server view uses config + sample data.");U={...U??{},...se}}}d("Building dashboard");let{encodeDashboardPayload:Y}=await import("./chunk-jrztbsnb.js"),{execSync:X}=await import("node:child_process"),{rmSync:ne}=await import("node:fs");for(let re of[".stx/ssg-cache",".stx/cache"])ne(`${A}/${re}`,{recursive:!0,force:!0});X("bun run build",{cwd:A,stdio:"inherit",env:{...process.env,...U?{TSCLOUD_DASHBOARD_DATA:Y(U)}:{}}}),n(`Dashboard built → ${A}/dist${U?" (live data)":" (sample data)"}`)}catch(m){r(`Dashboard build failed: ${m.message}`),process.exitCode=1}}),D.command("serverless:build-php-layer","Build + publish the ts-cloud PHP runtime layer (requires Docker)").option("--arch <architecture>","x86_64 or arm64",{default:"x86_64"}).option("--php <version>","PHP version",{default:"8.3"}).option("--name <name>","Layer name",{default:"tscloud-php"}).option("--bucket <bucket>","S3 bucket to stage the layer zip (defaults to {slug}-layers)").option("--region <region>","AWS region").action(async(t)=>{i("Building PHP runtime layer");try{let m=await Fe().catch(()=>null),A=t?.region||m?.project.region||"us-east-1",_=t?.arch||"x86_64",z=t?.php||"8.3",U=`${t?.name||"tscloud-php"}-${z.replace(".","")}-${_}`,Y=t?.bucket||`${m?.project.slug||"tscloud"}-layers`,{buildPhpRuntimeLayerZip:X}=await import("./chunk-n2c6y0k1.js"),{S3Client:ne}=await import("./chunk-d0tmmzy5.js"),{LambdaClient:re}=await import("./chunk-zjvg5pg8.js"),se=X({architecture:_,phpVersion:z,onStep:(Ie)=>d(Ie)});e(`Layer: ${se.fileCount} files, ${(se.zip.length/1024/1024).toFixed(1)} MB`);let he=new ne(A);if(!await he.bucketExists(Y))d(`Creating layer bucket ${Y}`),await he.createBucket(Y);let we=`layers/${U}.zip`;d("Uploading layer zip"),await he.putObject({bucket:Y,key:we,body:se.zip,contentType:"application/zip"}),d("Publishing layer version");let ye=await new re(A).publishLayerVersion({LayerName:U,Description:`ts-cloud PHP ${z} runtime (${_})`,Content:{S3Bucket:Y,S3Key:we},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[_]});Ce(["PHP runtime layer published","",`ARN: ${ye.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'php', layers: ['${ye.LayerVersionArn}'] }`,"or set TSCLOUD_PHP_LAYER_ARN before deploying."].join(`
|
|
404
|
+
`),"green")}catch(m){r(`Layer build failed: ${m.message}`),process.exitCode=1}}),D.command("serverless:build-node-layer","Build + publish a ts-cloud Node custom runtime layer (any version, incl. 24)").option("--arch <architecture>","x86_64 or arm64",{default:"x86_64"}).option("--node <version>","Node version (e.g. 24)",{default:"24"}).option("--name <name>","Layer name",{default:"tscloud-node"}).option("--bucket <bucket>","S3 bucket to stage the layer zip (defaults to {slug}-layers)").option("--region <region>","AWS region").action(async(t)=>{i("Building Node runtime layer");try{let m=await Fe().catch(()=>null),A=t?.region||m?.project.region||"us-east-1",_=t?.arch||"x86_64",z=t?.node||"24",{buildNodeRuntimeLayerZip:U}=await import("./chunk-n2c6y0k1.js"),{S3Client:Y}=await import("./chunk-d0tmmzy5.js"),{LambdaClient:X}=await import("./chunk-zjvg5pg8.js"),ne=U({architecture:_,version:z,onStep:(Ie)=>d(Ie)}),re=`${t?.name||"tscloud-node"}-${ne.version.split(".")[0]}-${_}`,se=t?.bucket||`${m?.project.slug||"tscloud"}-layers`;e(`Layer: Node ${ne.version}, ${(ne.zip.length/1024/1024).toFixed(1)} MB`);let he=new Y(A);if(!await he.bucketExists(se))d(`Creating layer bucket ${se}`),await he.createBucket(se);let we=`layers/${re}.zip`;d("Uploading layer zip"),await he.putObject({bucket:se,key:we,body:ne.zip,contentType:"application/zip"}),d("Publishing layer version");let ye=await new X(A).publishLayerVersion({LayerName:re,Description:`ts-cloud Node ${ne.version} runtime (${_})`,Content:{S3Bucket:se,S3Key:we},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[_]});Ce(["Node runtime layer published","",`ARN: ${ye.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'node', runtimeVersion: '${ne.version.split(".")[0]}', layers: ['${ye.LayerVersionArn}'] }`,"or set TSCLOUD_NODE_LAYER_ARN before deploying."].join(`
|
|
405
|
+
`),"green")}catch(m){r(`Layer build failed: ${m.message}`),process.exitCode=1}}),D.command("serverless:build-bun-layer","Build + publish a ts-cloud Bun custom runtime layer").option("--arch <architecture>","x86_64 or arm64",{default:"x86_64"}).option("--bun <version>",'Bun version (e.g. 1.3.13, or "latest")',{default:"latest"}).option("--name <name>","Layer name",{default:"tscloud-bun"}).option("--bucket <bucket>","S3 bucket to stage the layer zip (defaults to {slug}-layers)").option("--region <region>","AWS region").action(async(t)=>{i("Building Bun runtime layer");try{let m=await Fe().catch(()=>null),A=t?.region||m?.project.region||"us-east-1",_=t?.arch||"x86_64",z=t?.bun||"latest",{buildBunRuntimeLayerZip:U}=await import("./chunk-n2c6y0k1.js"),{S3Client:Y}=await import("./chunk-d0tmmzy5.js"),{LambdaClient:X}=await import("./chunk-zjvg5pg8.js"),ne=U({architecture:_,version:z,onStep:(Ie)=>d(Ie)}),re=`${t?.name||"tscloud-bun"}-${ne.version.replace(/\./g,"")}-${_}`,se=t?.bucket||`${m?.project.slug||"tscloud"}-layers`;e(`Layer: Bun ${ne.version}, ${(ne.zip.length/1024/1024).toFixed(1)} MB`);let he=new Y(A);if(!await he.bucketExists(se))d(`Creating layer bucket ${se}`),await he.createBucket(se);let we=`layers/${re}.zip`;d("Uploading layer zip"),await he.putObject({bucket:se,key:we,body:ne.zip,contentType:"application/zip"}),d("Publishing layer version");let ye=await new X(A).publishLayerVersion({LayerName:re,Description:`ts-cloud Bun ${ne.version} runtime (${_})`,Content:{S3Bucket:se,S3Key:we},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[_]});Ce(["Bun runtime layer published","",`ARN: ${ye.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'bun', layers: ['${ye.LayerVersionArn}'] }`,"or set TSCLOUD_BUN_LAYER_ARN before deploying."].join(`
|
|
406
406
|
`),"green")}catch(m){r(`Layer build failed: ${m.message}`),process.exitCode=1}}),D.command("deploy:status","Check deployment status").option("--stack <name>","Stack name").option("--env <environment>","Environment").action(async(t)=>{i("Deployment Status");try{let m=await Fe(),A=t?.env||"production",_=t?.stack||T(m,A),z=m.project.region||"us-east-1";e(`Stack: ${_}`),e(`Region: ${z}`);let U=new s("Checking deployment status...");U.start();let X=await new v(z).describeStacks({stackName:_});if(X.Stacks.length===0){U.fail("Stack not found"),Rr("No deployment found for this environment");return}let ne=X.Stacks[0];if(U.succeed("Status retrieved"),e(`
|
|
407
407
|
Status: ${ne.StackStatus}`),e(`Created: ${ne.CreationTime}`),ne.LastUpdatedTime)e(`Last Updated: ${ne.LastUpdatedTime}`);if(ne.Outputs&&ne.Outputs.length>0){e(`
|
|
408
408
|
Outputs:`);for(let re of ne.Outputs)e(` ${re.OutputKey}: ${re.OutputValue}`)}}catch(m){r(`Failed to get status: ${m.message}`)}}),D.command("deploy:rollback","Rollback to previous version").option("--stack <name>","Stack name").option("--env <environment>","Environment").action(async(t)=>{i("Rolling Back Deployment");try{let m=await Fe(),A=t?.env||"production",_=t?.stack||T(m,A),z=m.project.region||"us-east-1";if(e(`Stack: ${_}`),e(`Region: ${z}`),!await u(`
|
|
@@ -429,7 +429,7 @@ Container deployment complete!`),e(`
|
|
|
429
429
|
Image: ${nt}`),e(`Cluster: ${_}`),e(`Service: ${z}`)}catch(m){r(`Deployment failed: ${m.message}`),process.exitCode=1}})}async function tg(D){let t=process.cwd(),m=`${t}/.env`,A=`${t}/.env.local`,U=({stage:["stage","staging"],staging:["staging","stage"]}[D]||[D]).map((se)=>`${t}/.env.${se}`).find((se)=>vn(se));if(!U){if(vn(m))a(`No .env.${D} found, falling back to .env`);else e(`No .env.${D} or .env file found; using the existing process environment`);return null}let Y=U.split("/").pop();d(`Loading environment file: ${Y}`);let X=null,ne=null;if(vn(A)){X=`${A}.bak`,xs(A,X);let se=al(A,"utf-8");for(let we of se.split(`
|
|
430
430
|
`)){let Pe=we.trim();if(!Pe||Pe.startsWith("#"))continue;let ye=Pe.indexOf("=");if(ye===-1)continue;let Ie=Pe.slice(0,ye).trim();delete process.env[Ie]}let{unlinkSync:he}=await import("node:fs");he(A),e("Temporarily moved .env.local out of the way and purged its values from process.env")}if(vn(m))ne=`${m}.bak`,xs(m,ne);xs(U,m);let re=al(U,"utf-8");for(let se of re.split(`
|
|
431
431
|
`)){let he=se.trim();if(!he||he.startsWith("#"))continue;let we=he.indexOf("=");if(we===-1)continue;let Pe=he.slice(0,we).trim(),ye=he.slice(we+1).trim();process.env[Pe]=ye}return n(`Loaded ${Y}`),async()=>{let{unlinkSync:se}=await import("node:fs");if(ne)xs(ne,`${t}/.env`),se(ne),e("Restored .env");if(X)xs(X,`${t}/.env.local`),se(X),e("Restored .env.local")}}async function ng(D,t,m,A){if(!m)return;let _=rs(D.sites||{});if(_.size===0)return;if(!t){a("DNS: no app server IP resolved — skipping A-record reconciliation.");return}let z;try{z=pn(m)}catch(re){a(`DNS: ${re instanceof Error?re.message:String(re)}`),e(`Point each site domain at ${t} manually.`);return}if(!z){a(`DNS provider '${m}' is not configured — skipping A-record reconciliation.`),e(`Point each site domain at ${t} manually.`);return}if(z.provider==="route53"){let re=D.infrastructure?.dns?.hostedZoneId;if(re&&!z.hostedZoneId)z.hostedZoneId=re}let U=M(z),Y=D.infrastructure?.dns?.domain,X=ue(A),ne=X?"A/AAAA":"A";d(`Reconciling DNS: ${_.size} ${ne} record(s) → ${t} via ${m}...`);for(let re of _){let se=Y&&re.endsWith(Y)?Y:re.split(".").slice(-2).join(".");try{let he=await ai({provider:U,zone:se,fqdn:re,ipv4:t,ipv6:X});for(let we of he.published)n(` ✓ ${we.fqdn} ${we.type} → ${we.content}`);for(let we of he.warnings)a(` ⚠ ${re}: ${we}`)}catch(he){a(` ⚠ ${re}: ${he instanceof Error?he.message:String(he)} — set the address record manually.`)}}}async function rg(D,t){let m=D.infrastructure?.dns?.records;if(!t||!m?.length)return;let A=D.infrastructure?.dns?.domain;if(!A){a("DNS records: infrastructure.dns.domain is not set — skipping declared records.");return}let _;try{_=pn(t)}catch(Y){a(`DNS records: ${Y instanceof Error?Y.message:String(Y)}`);return}if(!_){a(`DNS records: provider '${t}' is not configured — skipping.`);return}let z=M(_),U=yo(m,A);d(`Reconciling ${U.length} declared DNS record(s) on ${A}...`);try{let Y=await bo({provider:z,zone:A,records:U});for(let X of Y.outcomes){let{record:ne,action:re}=X,se=`${ne.type} ${ne.name} -> ${ne.content.slice(0,60)}`;if(re==="failed")a(` ⚠ ${se}: ${X.message||"failed"}`);else if(re==="unchanged")e(` = ${se}`);else n(` ✓ ${se} (${re})`)}for(let X of Y.warnings)a(` ⚠ ${X}`)}catch(Y){a(`DNS records: ${Y instanceof Error?Y.message:String(Y)}`)}}async function ig(D,t,m){let{plan:A,errors:_}=Yc(D);for(let U of _)a(`Cloudflare CDN: ${U}`);if(!A)return;if(!t){a("Cloudflare CDN: no app server IP resolved — skipping.");return}d(`Cloudflare CDN: reconciling ${A.hosts.length} host(s) on ${A.zone}...`);let z=new ge(A.apiToken,{zoneId:A.zoneId,accountId:A.accountId});try{let U=await Jc({provider:z,zone:A.zone,hosts:A.hosts,ipv4:t,ipv6:ue(m),proxied:A.proxied,settings:A.settings,cache:A.cache,originGuard:A.originGuard,purge:A.purge,skipOriginProbe:A.skipOriginProbe});for(let Y of U.records)n(` ✓ ${Y.host} ${Y.type} → ${Y.content}${Y.proxied?" (proxied)":" (DNS-only)"}`);for(let Y of U.settingsChanged)e(` ✓ ${Y.id}: ${JSON.stringify(Y.from)} → ${JSON.stringify(Y.to)}`);if(U.cacheRules>0)n(` ✓ ${U.cacheRules} cache rule(s) applied`);if(U.originGuard)n(" ✓ origin guard header applied");if(U.purged)n(" ✓ edge cache purged");for(let Y of U.warnings)a(` ⚠ ${Y}`)}catch(U){a(`Cloudflare CDN: ${U instanceof Error?U.message:String(U)}`)}}async function nu(D,t={}){let m=Object.keys(D.infrastructure?.r2?.buckets??{}).length;if(m===0)return!0;d(`Cloudflare R2: ${t.dryRun?"planning":"reconciling"} ${m} bucket(s)...`);try{let A=await Xc(D,{apiToken:process.env.CLOUDFLARE_API_TOKEN,accountId:process.env.CLOUDFLARE_ACCOUNT_ID,zoneId:process.env.CLOUDFLARE_ZONE_ID,dryRun:t.dryRun});for(let _ of A.buckets){if(_.changes.length===0)e(` = ${_.name} (in sync)`);for(let z of _.changes)n(` ✓ ${_.name}: ${z}`);for(let z of _.domains)e(` ${z.domain}: ${z.status??"unknown"}`)}for(let _ of A.warnings)a(` ⚠ ${_}`);return!0}catch(A){return r(`Cloudflare R2: ${A instanceof Error?A.message:String(A)}`),!1}}async function ru(D,t={}){let m=Object.keys(D.infrastructure?.workers??{}).length;if(m===0)return!0;d(`Cloudflare Workers: ${t.dryRun?"planning":"reconciling"} ${m} Worker(s)...`);try{let A=await il(D,{apiToken:process.env.CLOUDFLARE_API_TOKEN,accountId:process.env.CLOUDFLARE_ACCOUNT_ID,zoneId:process.env.CLOUDFLARE_ZONE_ID,projectRoot:process.cwd(),dryRun:t.dryRun});for(let _ of A.workers){for(let z of _.changes)n(` ✓ ${_.name}: ${z}`);for(let z of _.domains)e(` ${z.domain}: ${z.status??"unknown"}`)}for(let _ of A.warnings)a(` ⚠ ${_}`);return!0}catch(A){return r(`Cloudflare Workers: ${A instanceof Error?A.message:String(A)}`),!1}}async function og(D,t,m,A,_,z,U=!1){let Y=D.sites||{},X=t?[t]:Object.keys(Y);if(X.length===0){a("No sites configured in cloud.config.ts");return}let ne=null,re=!1,se=()=>{if(ne||re)return ne;let he=null;try{he=pn(m)}catch(we){r(we.message)}if(!he)return r(`DNS provider '${m}' is not configured. Please set the required environment variables.`),e(`
|
|
432
|
-
For Cloudflare: CLOUDFLARE_API_TOKEN`),e("For Porkbun: PORKBUN_API_KEY, PORKBUN_SECRET_KEY"),e("For GoDaddy: GODADDY_API_KEY, GODADDY_API_SECRET"),re=!0,null;if(he.provider==="route53"){let we=D.infrastructure?.dns?.hostedZoneId;if(we&&!he.hostedZoneId)he.hostedZoneId=we}return ne={config:he,provider:M(he)},ne};for(let he of X){let we=Y[he];if(!we){r(`Site '${he}' not found in configuration`);continue}if(!Vc(we)){let _e=
|
|
432
|
+
For Cloudflare: CLOUDFLARE_API_TOKEN`),e("For Porkbun: PORKBUN_API_KEY, PORKBUN_SECRET_KEY"),e("For GoDaddy: GODADDY_API_KEY, GODADDY_API_SECRET"),re=!0,null;if(he.provider==="route53"){let we=D.infrastructure?.dns?.hostedZoneId;if(we&&!he.hostedZoneId)he.hostedZoneId=we}return ne={config:he,provider:M(he)},ne};for(let he of X){let we=Y[he];if(!we){r(`Site '${he}' not found in configuration`);continue}if(!Vc(we)){let _e=I(we);e(`Site '${he}' is ${_e} — handled outside the static bucket pipeline.`);continue}let Pe=se();if(!Pe)return;let ye=we.domain;if(!ye){r(`Site '${he}' has no domain configured`);continue}if(i(`Deploying Site: ${he}`),e(`Domain: ${ye}`),e(`Source: ${we.root}`),e(`DNS Provider: ${m}`),we.build){d(`Running build command: ${we.build}`);try{let{execSync:_e}=await import("node:child_process");_e(we.build,{stdio:"inherit",cwd:process.cwd()}),n("Build completed successfully")}catch(_e){r(`Build failed: ${_e.message}`);continue}}if(!vn(we.root)){if(r(`Source directory not found: ${we.root}`),!we.build)e('Run your build command first (e.g., bun run generate) or add a "build" option to your site config');continue}d("Checking existing DNS records...");let Ie=await Pe.provider.listRecords(ye);if(Ie.success&&Ie.records.length>0){let _e=ye.split("."),et=_e.length>2?_e[0]:"@",Rt=_e.slice(-2).join("."),Et=Ie.records.find((wt)=>wt.type==="CNAME"&&(wt.name===ye||wt.name===et||wt.name===`${et}.${Rt}`));if(Et){let wt=Et.content.includes("netlify"),Pt=Et.content.includes("vercel");if(Et.content.includes("cloudfront.net"))e(`Domain already points to CloudFront: ${Et.content}`),e("Proceeding with file upload...");else{let _t=wt?"Netlify":Pt?"Vercel":"another provider";if(a(`
|
|
433
433
|
Existing CNAME record detected:`),e(` ${Et.name} -> ${Et.content}`),wt||Pt)e(`
|
|
434
434
|
This domain is currently pointing to ${_t}.`),e("Deploying will update this record to point to AWS CloudFront.");if(!(U||await u(`
|
|
435
435
|
Update DNS record to point to AWS CloudFront?`,!0))){e("Deployment cancelled");continue}d(`Removing old ${_t} CNAME record...`);let zt=await Pe.provider.deleteRecord(ye,{name:Et.name,type:"CNAME",content:Et.content});if(zt.success)n(`Removed CNAME: ${Et.name} -> ${Et.content}`);else a(`Could not remove old record: ${zt.message}`),e("The deployment will attempt to update it instead.")}}}let Le=we.root,Re,De=!!we.installScript;if(De){let{mkdtempSync:_e,copyFileSync:et,readdirSync:Rt,statSync:Et}=await import("node:fs"),{tmpdir:wt}=await import("node:os"),{join:Pt,resolve:Xt}=await import("node:path");Re=_e(Pt(wt(),"cloud-install-"));let _t=Xt(we.installScript);et(_t,Pt(Re,"index.html"));let nt=_t.split("/").pop();if(nt!=="index.html")et(_t,Pt(Re,nt));if(vn(we.root)){let zt=Rt(we.root);for(let Vt of zt){let St=Pt(we.root,Vt);if(Et(St).isFile()&&Vt!=="index.html")et(St,Pt(Re,Vt))}}Le=Re,e(`Install script: ${we.installScript}`),e(`Serving at: curl -fsSL https://${ye} | bash`)}d("Deploying to AWS (S3 + CloudFront)...");let Ee=la(D,he,we,z),Ke=da(D,he);e(`Stack: ${Ee}`);let je=await zi({siteName:Ke,stackName:Ee,domain:ye,region:A,bucket:ua(D.project.slug,z,he,we.bucket),sourceDir:Le,certificateArn:we.certificateArn,dnsProvider:Pe.config,skipDnsVerification:_,passthroughUrls:De,dynamicApp:!!D.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginDomain:D.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginPort:D.infrastructure?.compute?.cloudFrontOriginPort??D.infrastructure?.api?.port??3008,computeOriginId:D.infrastructure?.compute?.cloudFrontOriginId??`${D.project.slug}-site-ec2`,onProgress:(_e,et)=>{if(_e==="infrastructure")d(et||"Setting up infrastructure...");else if(_e==="upload"){if(et?.includes("/")&&!et.includes("1/")){let Rt=et.match(/(\d+)\/(\d+)/);if(Rt){let[,Et,wt]=Rt;if(Number(Et)%10===0||Et===wt)e(` Uploaded ${Et}/${wt} files`)}}}else if(_e==="invalidate")d("Invalidating CDN cache...")}});if(Re){let{rmSync:_e}=await import("node:fs");_e(Re,{recursive:!0,force:!0})}if(je.success){n(`
|
|
@@ -475,7 +475,7 @@ export const handler = async (event) => {
|
|
|
475
475
|
body: JSON.stringify({ ok: statusCode === 200, service: 'ts-cloud-static-api', path, coldStart: currentColdStart, initializedAt })
|
|
476
476
|
}
|
|
477
477
|
}
|
|
478
|
-
`;function Su(D){let t=D;return String(t?.code||t?.name||t?.statusCode||"")}function zg(D){return["NoSuchEntity","ResourceNotFoundException","404"].includes(Su(D))}function bu(D){return["EntityAlreadyExists","ResourceAlreadyExistsException","ResourceConflictException","409"].includes(Su(D))}function wu(D,t){let m=D?.Items;if(!m)return[];if(Array.isArray(m))return m;let A=m[t];if(A===void 0)return[];return Array.isArray(A)?A:[A]}function vl(D){let t=D?.Aliases?.Items,m=t?.CNAME??t;if(Array.isArray(m))return m.map(String);if(typeof m==="string")return[m];return[]}function Ug(D){if(!/^[A-Z0-9]{8,32}$/.test(D.distributionId))throw Error("CloudFront distribution ID is invalid");if(!/^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z0-9-]{2,63}$/.test(D.expectedAlias))throw Error("Expected CloudFront alias is invalid");if(!/^[A-Za-z0-9-_]{1,64}$/.test(D.functionName))throw Error("Lambda function name is invalid");let t=D.pathPattern?.startsWith("/")?D.pathPattern:`/${D.pathPattern||"api/*"}`;if(t==="/"||t==="/*"||!t.endsWith("*"))throw Error("API path must be a non-default wildcard pattern such as /api/*");let m=D.originId||`${D.functionName}-url`,A=D.roleName||`${D.functionName}-execution`;if(!/^[A-Za-z0-9_.-]{1,64}$/.test(A))throw Error("IAM role name is invalid");let _=D.memorySize??256,z=D.timeout??10,U=D.logRetentionDays??14;if(_<128||_>10240)throw Error("Lambda memory must be between 128 and 10240 MB");if(z<1||z>900)throw Error("Lambda timeout must be between 1 and 900 seconds");return{region:D.region||"us-east-1",pathPattern:t,originId:m,roleName:A,memorySize:_,timeout:z,logRetentionDays:U}}function Wg(D){let t=D.region||"us-east-1";return{cloudfront:new E(D.profile),iam:new te(t,D.profile),lambda:new I(t,D.profile),logs:new oe(t,D.profile),sts:new lt(t,D.profile),sleep:(m)=>new Promise((A)=>setTimeout(A,m))}}async function Cu(D,t){let m=Ug(D),A=t||Wg({profile:D.profile,region:m.region}),[_,z,U]=await Promise.all([A.sts.getCallerIdentity(),A.cloudfront.getDistribution(D.distributionId),A.cloudfront.getDistributionConfig(D.distributionId)]);if(!_.Account)throw Error("AWS identity did not return an account ID");if(!z.Enabled)throw Error(`CloudFront distribution ${D.distributionId} is disabled`);if(!(vl(z).length?vl(z):vl(U.DistributionConfig)).includes(D.expectedAlias))throw Error(`Distribution ${D.distributionId} does not contain expected alias ${D.expectedAlias}`);let X,ne=!0;try{X=await A.iam.getRole({RoleName:m.roleName})}catch(_t){if(!zg(_t))throw _t;ne=!1}let re=X?.Arn||`arn:aws:iam::${_.Account}:role/${m.roleName}`,se=await A.lambda.functionExists(D.functionName),he=se?await A.lambda.getFunctionUrl(D.functionName):null,we=await A.cloudfront.listOriginAccessControls(),Pe=`${D.functionName}-lambda-url`,ye=we.some((_t)=>_t.Name===Pe&&_t.OriginAccessControlOriginType==="lambda"),Ie=wu(U.DistributionConfig.Origins,"Origin"),Le=wu(U.DistributionConfig.CacheBehaviors,"CacheBehavior"),Re=Ie.some((_t)=>String(_t.Id)===m.originId),De=Le.some((_t)=>String(_t.PathPattern)===m.pathPattern),Ee=[...!ne?[`create IAM execution role ${m.roleName}`]:[`reconcile IAM execution policy ${m.roleName}`],...se?[`update Lambda ${D.functionName} code and configuration`]:[`create Lambda ${D.functionName}`],...!he?["create private AWS_IAM function URL"]:[],...!ye?[`create Lambda origin access control ${Pe}`]:[],"grant CloudFront function-URL and invocation permissions",`${De?"reconcile":"add"} ${m.pathPattern} behavior without changing the default behavior`],Ke={command:`cloud cdn:origin:remove ${D.distributionId} FUNCTION_URL_HOST --id ${m.originId} --path '${m.pathPattern}' --apply --confirm 'remove:${D.distributionId}:${m.pathPattern}'`,removeFunction:!se,removeRole:!ne},je={mode:D.apply?"apply":"plan",accountId:_.Account,identityArn:_.Arn,distribution:{id:z.Id,arn:z.ARN,alias:D.expectedAlias,domainName:z.DomainName,status:z.Status},function:{name:D.functionName,existed:se,runtime:"nodejs22.x",urlExisted:!!he,url:he?.FunctionUrl},role:{name:m.roleName,existed:ne,arn:re},origin:{id:m.originId,pathPattern:m.pathPattern,existed:Re,behaviorExisted:De,accessControlExisted:ye},changes:Ee,rollback:Ke,applied:!1};if(!D.apply)return je;let _e=`${D.distributionId}:${m.pathPattern}`;if(D.confirm!==_e)throw Error(`Pass exact confirmation token ${_e} to mutate the live distribution`);if(!X)X=await A.iam.createRole({RoleName:m.roleName,Description:`Execution role for ${D.functionName}`,AssumeRolePolicyDocument:JSON.stringify({Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"lambda.amazonaws.com"},Action:"sts:AssumeRole"}]}),Tags:[{Key:"ManagedBy",Value:"ts-cloud"}]});await A.iam.putRolePolicy({RoleName:m.roleName,PolicyName:"lambda-logs",PolicyDocument:JSON.stringify({Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:`arn:aws:logs:${m.region}:${_.Account}:log-group:/aws/lambda/${D.functionName}:*`}]})});try{await A.logs.createLogGroup(`/aws/lambda/${D.functionName}`,{ManagedBy:"ts-cloud"})}catch(_t){if(!bu(_t))throw _t}await A.logs.putRetentionPolicy(`/aws/lambda/${D.functionName}`,m.logRetentionDays);let et=D.code||Mg;if(se)await A.lambda.updateFunctionCodeInline(D.functionName,et,"index.mjs"),await A.lambda.waitForFunctionActive(D.functionName),await A.lambda.updateFunctionConfiguration({FunctionName:D.functionName,Runtime:"nodejs22.x",Role:X.Arn,Handler:"index.handler",Description:`Private API origin for ${D.expectedAlias}`,Timeout:m.timeout,MemorySize:m.memorySize});else{let _t;for(let nt=0;nt<8;nt++)try{await A.lambda.createFunctionWithCode({FunctionName:D.functionName,Runtime:"nodejs22.x",Role:X.Arn,Handler:"index.handler",Code:et,Filename:"index.mjs",Description:`Private API origin for ${D.expectedAlias}`,Timeout:m.timeout,MemorySize:m.memorySize}),_t=void 0;break}catch(zt){if(_t=zt,!String(zt.message).toLowerCase().includes("role"))throw zt;await A.sleep(1000)}if(_t)throw _t}await A.lambda.waitForFunctionActive(D.functionName);let Rt=he||await A.lambda.createFunctionUrl({FunctionName:D.functionName,AuthType:"AWS_IAM",InvokeMode:"BUFFERED"});if(!Rt.FunctionUrl)throw Error("Lambda did not return a function URL");let Et=new URL(Rt.FunctionUrl).hostname,wt=await A.cloudfront.findOrCreateOriginAccessControl(Pe,"lambda"),Pt=z.ARN||`arn:aws:cloudfront::${_.Account}:distribution/${D.distributionId}`,Xt=[{FunctionName:D.functionName,StatementId:"AllowCloudFrontFunctionUrl",Action:"lambda:InvokeFunctionUrl",Principal:"cloudfront.amazonaws.com",SourceArn:Pt,FunctionUrlAuthType:"AWS_IAM"},{FunctionName:D.functionName,StatementId:"AllowCloudFrontInvoke",Action:"lambda:InvokeFunction",Principal:"cloudfront.amazonaws.com",SourceArn:Pt,InvokedViaFunctionUrl:!0}];for(let _t of Xt)try{await A.lambda.addPermission(_t)}catch(nt){if(!bu(nt))throw nt}return await A.cloudfront.upsertExistingDistributionOrigin(D.distributionId,{id:m.originId,domainName:Et,pathPattern:m.pathPattern,originAccessControlId:wt.Id}),{...je,function:{...je.function,url:Rt.FunctionUrl},applied:!0}}async function $u(D){let t=`https://${D.alias}/`,m=`https://${D.alias}/api/health`,A=await fetch(t,{redirect:"follow",headers:{"cache-control":"no-cache"}}),_=new Uint8Array(await A.arrayBuffer()),z=_g("sha256").update(_).digest("hex"),U=Date.now(),Y=await fetch(m,{redirect:"follow",headers:{"cache-control":"no-cache"}}),X=Date.now()-U,ne=await Y.text(),re=ne;try{re=JSON.parse(ne)}catch{}let se;if(D.logs&&D.functionName){let he=await D.logs.filterLogEvents({logGroupName:`/aws/lambda/${D.functionName}`,startTime:U-60000,filterPattern:'"Init Duration"',limit:25});for(let we of he.events||[]){let Pe=we.message?.match(/Init Duration:\s*([0-9.]+)\s*ms/i);if(Pe)se=Number(Pe[1])}}return{frontend:{url:t,status:A.status,sha256:z,unchanged:D.expectedFrontendSha256?z===D.expectedFrontendSha256:void 0,bytes:_.byteLength},api:{url:m,status:Y.status,latencyMs:X,healthy:Y.ok&&!!re?.ok,body:re},coldStart:{observed:se!==void 0,initDurationMs:se},verifiedAt:(D.now?.()||new Date).toISOString()}}function ku(D){let t=(D.memoryMb||256)/1024,m=Math.max(0,D.requests-(D.includeFreeTier===!1?0:1e6)),A=Math.max(0,D.requests*(D.averageDurationMs/1000)*t-(D.includeFreeTier===!1?0:400000)),_=m/1e6*0.2+A*0.0000166667,z=9.010025,U=16.425;return{lambdaUsd:Number(_.toFixed(2)),alwaysOnFargateAndAlbUsd:Number(25.435025000000003 .toFixed(2)),assumptions:["us-east-1 public list rates snapshot, July 2026","Lambda x86 request and duration pricing with optional monthly free tier","Fargate comparison uses one always-on 0.25 vCPU/0.5 GB task plus one ALB before LCU charges","excludes CloudFront, data transfer, logs, taxes, and application data services"]}}function bl(D){D.command("cdn:api:deploy <distributionId> <alias>","Plan or deploy a private Lambda API behind an existing static distribution").option("--function <name>","Lambda function name",{default:"ts-cloud-static-api"}).option("--path <pattern>","CloudFront API path",{default:"/api/*"}).option("--origin-id <id>","Stable CloudFront origin ID").option("--role <name>","Lambda execution role name").option("--region <region>","Lambda region",{default:"us-east-1"}).option("--profile <name>","AWS credential profile").option("--memory <mb>","Lambda memory in MB",{default:"256"}).option("--timeout <seconds>","Lambda timeout",{default:"10"}).option("--retention <days>","CloudWatch log retention",{default:"14"}).option("--apply","Create resources and patch the live distribution").option("--confirm <text>","Exact distribution:path confirmation").action(async(t,m,A)=>{try{let _=await Cu({distributionId:t,expectedAlias:m,functionName:A.function||"ts-cloud-static-api",pathPattern:A.path,originId:A.originId,roleName:A.role,region:A.region,profile:A.profile,memorySize:Number(A.memory),timeout:Number(A.timeout),logRetentionDays:Number(A.retention),apply:!!A.apply,confirm:A.confirm});if(e(JSON.stringify(_,null,2)),_.applied)n("Private API origin submitted to CloudFront. Run cdn:api:verify after propagation.");else e(`Plan only. Re-run with --apply --confirm '${t}:${_.origin.pathPattern}'.`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}}),D.command("cdn:api:verify <alias>","Verify static frontend integrity, API health, latency, and Lambda initialization").option("--function <name>","Lambda function name",{default:"ts-cloud-static-api"}).option("--frontend-sha256 <digest>","Expected SHA-256 of the frontend response").option("--region <region>","Lambda region",{default:"us-east-1"}).option("--profile <name>","AWS credential profile").action(async(t,m)=>{try{let A=new oe(m.region||"us-east-1",m.profile),_=await $u({alias:t,expectedFrontendSha256:m.frontendSha256,logs:A,functionName:m.function||"ts-cloud-static-api"});if(e(JSON.stringify(_,null,2)),!_.api.healthy||_.frontend.unchanged===!1)throw Error("Static API verification failed");n("Static frontend and Lambda API verification passed.")}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),D.command("cdn:api:cost <requests>","Compare a low-volume Lambda API with one always-on Fargate task and ALB").option("--duration <milliseconds>","Average Lambda duration",{default:"100"}).option("--memory <mb>","Lambda memory in MB",{default:"256"}).option("--no-free-tier","Exclude the Lambda free tier").action((t,m)=>{let A=ku({requests:Number(t),averageDurationMs:Number(m.duration),memoryMb:Number(m.memory),includeFreeTier:m.freeTier!==!1});e(JSON.stringify(A,null,2))}),D.command("cdn:origin:add <distributionId> <domain>","Safely add a backend origin and path to an existing distribution").option("--id <origin>","Stable origin ID",{default:"api-backend"}).option("--path <pattern>","Path behavior",{default:"/api/*"}).option("--profile <name>","AWS credential profile").option("--replace","Replace a reviewed path/origin collision").option("--apply","Apply the ETag-protected patch").option("--confirm <text>","Exact distribution:path confirmation").action(async(t,m,A)=>{try{let _=new E(A.profile),z={id:A.id??"api-backend",domainName:m,pathPattern:A.path??"/api/*",replaceExisting:!!A.replace},U=await _.upsertExistingDistributionOrigin(t,{...z,dryRun:!0});if(e(JSON.stringify(U,null,2)),!A.apply){e("Dry run only. Re-run with --apply and the exact --confirm token.");return}let Y=`${t}:${U.pathPattern}`;if(A.confirm!==Y)throw Error(`Pass --confirm "${Y}" to apply this live distribution change`);let X=await _.upsertExistingDistributionOrigin(t,z);n(X.changed?"Backend origin patch submitted.":"Backend origin already matches."),e(JSON.stringify(X,null,2))}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}}),D.command("cdn:origin:remove <distributionId> <domain>","Remove one exact backend behavior and its unreferenced origin").option("--id <origin>","Origin ID",{default:"api-backend"}).option("--path <pattern>","Path behavior",{default:"/api/*"}).option("--profile <name>","AWS credential profile").option("--apply","Apply the ETag-protected rollback").option("--confirm <text>","Exact remove:distribution:path confirmation").action(async(t,m,A)=>{try{let _=new E(A.profile),z={id:A.id??"api-backend",domainName:m,pathPattern:A.path??"/api/*"},U=await _.removeExistingDistributionOrigin(t,{...z,dryRun:!0});if(e(JSON.stringify(U,null,2)),!A.apply){e("Dry run only. Re-run with --apply and the exact --confirm token.");return}let Y=`remove:${t}:${U.pathPattern}`;if(A.confirm!==Y)throw Error(`Pass --confirm "${Y}" to apply this live distribution rollback`);let X=await _.removeExistingDistributionOrigin(t,z);n(X.changed?"Backend behavior rollback submitted.":"Backend behavior was already absent."),e(JSON.stringify(X,null,2))}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}}),D.command("cdn:list","List all CloudFront distributions").action(async()=>{i("CloudFront Distributions");try{let t=await Fe(),m=new E,A=new s("Fetching distributions...");A.start();let _=await m.listDistributions();if(A.succeed(`Found ${_.length} distribution(s)`),_.length===0){e("No CloudFront distributions found"),e("Use `cloud cdn:create` to create a new distribution");return}c(["ID","Domain","Status","Enabled","Aliases"],_.map((z)=>[z.Id||"N/A",z.DomainName||"N/A",z.Status||"N/A",z.Enabled?"Yes":"No",(z.Aliases?.Items?.length||0).toString()]))}catch(t){r(`Failed to list distributions: ${t.message}`),process.exit(1)}}),D.command("cdn:status <distributionId>","Get CloudFront distribution status").action(async(t)=>{i(`CloudFront Distribution: ${t}`);try{let m=new E,A=new s("Fetching distribution details...");A.start();let _=await m.getDistribution(t);if(A.succeed("Distribution details loaded"),e(`
|
|
478
|
+
`;function Su(D){let t=D;return String(t?.code||t?.name||t?.statusCode||"")}function zg(D){return["NoSuchEntity","ResourceNotFoundException","404"].includes(Su(D))}function bu(D){return["EntityAlreadyExists","ResourceAlreadyExistsException","ResourceConflictException","409"].includes(Su(D))}function wu(D,t){let m=D?.Items;if(!m)return[];if(Array.isArray(m))return m;let A=m[t];if(A===void 0)return[];return Array.isArray(A)?A:[A]}function vl(D){let t=D?.Aliases?.Items,m=t?.CNAME??t;if(Array.isArray(m))return m.map(String);if(typeof m==="string")return[m];return[]}function Ug(D){if(!/^[A-Z0-9]{8,32}$/.test(D.distributionId))throw Error("CloudFront distribution ID is invalid");if(!/^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+[A-Za-z0-9-]{2,63}$/.test(D.expectedAlias))throw Error("Expected CloudFront alias is invalid");if(!/^[A-Za-z0-9-_]{1,64}$/.test(D.functionName))throw Error("Lambda function name is invalid");let t=D.pathPattern?.startsWith("/")?D.pathPattern:`/${D.pathPattern||"api/*"}`;if(t==="/"||t==="/*"||!t.endsWith("*"))throw Error("API path must be a non-default wildcard pattern such as /api/*");let m=D.originId||`${D.functionName}-url`,A=D.roleName||`${D.functionName}-execution`;if(!/^[A-Za-z0-9_.-]{1,64}$/.test(A))throw Error("IAM role name is invalid");let _=D.memorySize??256,z=D.timeout??10,U=D.logRetentionDays??14;if(_<128||_>10240)throw Error("Lambda memory must be between 128 and 10240 MB");if(z<1||z>900)throw Error("Lambda timeout must be between 1 and 900 seconds");return{region:D.region||"us-east-1",pathPattern:t,originId:m,roleName:A,memorySize:_,timeout:z,logRetentionDays:U}}function Wg(D){let t=D.region||"us-east-1";return{cloudfront:new E(D.profile),iam:new te(t,D.profile),lambda:new P(t,D.profile),logs:new oe(t,D.profile),sts:new lt(t,D.profile),sleep:(m)=>new Promise((A)=>setTimeout(A,m))}}async function Cu(D,t){let m=Ug(D),A=t||Wg({profile:D.profile,region:m.region}),[_,z,U]=await Promise.all([A.sts.getCallerIdentity(),A.cloudfront.getDistribution(D.distributionId),A.cloudfront.getDistributionConfig(D.distributionId)]);if(!_.Account)throw Error("AWS identity did not return an account ID");if(!z.Enabled)throw Error(`CloudFront distribution ${D.distributionId} is disabled`);if(!(vl(z).length?vl(z):vl(U.DistributionConfig)).includes(D.expectedAlias))throw Error(`Distribution ${D.distributionId} does not contain expected alias ${D.expectedAlias}`);let X,ne=!0;try{X=await A.iam.getRole({RoleName:m.roleName})}catch(_t){if(!zg(_t))throw _t;ne=!1}let re=X?.Arn||`arn:aws:iam::${_.Account}:role/${m.roleName}`,se=await A.lambda.functionExists(D.functionName),he=se?await A.lambda.getFunctionUrl(D.functionName):null,we=await A.cloudfront.listOriginAccessControls(),Pe=`${D.functionName}-lambda-url`,ye=we.some((_t)=>_t.Name===Pe&&_t.OriginAccessControlOriginType==="lambda"),Ie=wu(U.DistributionConfig.Origins,"Origin"),Le=wu(U.DistributionConfig.CacheBehaviors,"CacheBehavior"),Re=Ie.some((_t)=>String(_t.Id)===m.originId),De=Le.some((_t)=>String(_t.PathPattern)===m.pathPattern),Ee=[...!ne?[`create IAM execution role ${m.roleName}`]:[`reconcile IAM execution policy ${m.roleName}`],...se?[`update Lambda ${D.functionName} code and configuration`]:[`create Lambda ${D.functionName}`],...!he?["create private AWS_IAM function URL"]:[],...!ye?[`create Lambda origin access control ${Pe}`]:[],"grant CloudFront function-URL and invocation permissions",`${De?"reconcile":"add"} ${m.pathPattern} behavior without changing the default behavior`],Ke={command:`cloud cdn:origin:remove ${D.distributionId} FUNCTION_URL_HOST --id ${m.originId} --path '${m.pathPattern}' --apply --confirm 'remove:${D.distributionId}:${m.pathPattern}'`,removeFunction:!se,removeRole:!ne},je={mode:D.apply?"apply":"plan",accountId:_.Account,identityArn:_.Arn,distribution:{id:z.Id,arn:z.ARN,alias:D.expectedAlias,domainName:z.DomainName,status:z.Status},function:{name:D.functionName,existed:se,runtime:"nodejs22.x",urlExisted:!!he,url:he?.FunctionUrl},role:{name:m.roleName,existed:ne,arn:re},origin:{id:m.originId,pathPattern:m.pathPattern,existed:Re,behaviorExisted:De,accessControlExisted:ye},changes:Ee,rollback:Ke,applied:!1};if(!D.apply)return je;let _e=`${D.distributionId}:${m.pathPattern}`;if(D.confirm!==_e)throw Error(`Pass exact confirmation token ${_e} to mutate the live distribution`);if(!X)X=await A.iam.createRole({RoleName:m.roleName,Description:`Execution role for ${D.functionName}`,AssumeRolePolicyDocument:JSON.stringify({Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"lambda.amazonaws.com"},Action:"sts:AssumeRole"}]}),Tags:[{Key:"ManagedBy",Value:"ts-cloud"}]});await A.iam.putRolePolicy({RoleName:m.roleName,PolicyName:"lambda-logs",PolicyDocument:JSON.stringify({Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:`arn:aws:logs:${m.region}:${_.Account}:log-group:/aws/lambda/${D.functionName}:*`}]})});try{await A.logs.createLogGroup(`/aws/lambda/${D.functionName}`,{ManagedBy:"ts-cloud"})}catch(_t){if(!bu(_t))throw _t}await A.logs.putRetentionPolicy(`/aws/lambda/${D.functionName}`,m.logRetentionDays);let et=D.code||Mg;if(se)await A.lambda.updateFunctionCodeInline(D.functionName,et,"index.mjs"),await A.lambda.waitForFunctionActive(D.functionName),await A.lambda.updateFunctionConfiguration({FunctionName:D.functionName,Runtime:"nodejs22.x",Role:X.Arn,Handler:"index.handler",Description:`Private API origin for ${D.expectedAlias}`,Timeout:m.timeout,MemorySize:m.memorySize});else{let _t;for(let nt=0;nt<8;nt++)try{await A.lambda.createFunctionWithCode({FunctionName:D.functionName,Runtime:"nodejs22.x",Role:X.Arn,Handler:"index.handler",Code:et,Filename:"index.mjs",Description:`Private API origin for ${D.expectedAlias}`,Timeout:m.timeout,MemorySize:m.memorySize}),_t=void 0;break}catch(zt){if(_t=zt,!String(zt.message).toLowerCase().includes("role"))throw zt;await A.sleep(1000)}if(_t)throw _t}await A.lambda.waitForFunctionActive(D.functionName);let Rt=he||await A.lambda.createFunctionUrl({FunctionName:D.functionName,AuthType:"AWS_IAM",InvokeMode:"BUFFERED"});if(!Rt.FunctionUrl)throw Error("Lambda did not return a function URL");let Et=new URL(Rt.FunctionUrl).hostname,wt=await A.cloudfront.findOrCreateOriginAccessControl(Pe,"lambda"),Pt=z.ARN||`arn:aws:cloudfront::${_.Account}:distribution/${D.distributionId}`,Xt=[{FunctionName:D.functionName,StatementId:"AllowCloudFrontFunctionUrl",Action:"lambda:InvokeFunctionUrl",Principal:"cloudfront.amazonaws.com",SourceArn:Pt,FunctionUrlAuthType:"AWS_IAM"},{FunctionName:D.functionName,StatementId:"AllowCloudFrontInvoke",Action:"lambda:InvokeFunction",Principal:"cloudfront.amazonaws.com",SourceArn:Pt,InvokedViaFunctionUrl:!0}];for(let _t of Xt)try{await A.lambda.addPermission(_t)}catch(nt){if(!bu(nt))throw nt}return await A.cloudfront.upsertExistingDistributionOrigin(D.distributionId,{id:m.originId,domainName:Et,pathPattern:m.pathPattern,originAccessControlId:wt.Id}),{...je,function:{...je.function,url:Rt.FunctionUrl},applied:!0}}async function $u(D){let t=`https://${D.alias}/`,m=`https://${D.alias}/api/health`,A=await fetch(t,{redirect:"follow",headers:{"cache-control":"no-cache"}}),_=new Uint8Array(await A.arrayBuffer()),z=_g("sha256").update(_).digest("hex"),U=Date.now(),Y=await fetch(m,{redirect:"follow",headers:{"cache-control":"no-cache"}}),X=Date.now()-U,ne=await Y.text(),re=ne;try{re=JSON.parse(ne)}catch{}let se;if(D.logs&&D.functionName){let he=await D.logs.filterLogEvents({logGroupName:`/aws/lambda/${D.functionName}`,startTime:U-60000,filterPattern:'"Init Duration"',limit:25});for(let we of he.events||[]){let Pe=we.message?.match(/Init Duration:\s*([0-9.]+)\s*ms/i);if(Pe)se=Number(Pe[1])}}return{frontend:{url:t,status:A.status,sha256:z,unchanged:D.expectedFrontendSha256?z===D.expectedFrontendSha256:void 0,bytes:_.byteLength},api:{url:m,status:Y.status,latencyMs:X,healthy:Y.ok&&!!re?.ok,body:re},coldStart:{observed:se!==void 0,initDurationMs:se},verifiedAt:(D.now?.()||new Date).toISOString()}}function ku(D){let t=(D.memoryMb||256)/1024,m=Math.max(0,D.requests-(D.includeFreeTier===!1?0:1e6)),A=Math.max(0,D.requests*(D.averageDurationMs/1000)*t-(D.includeFreeTier===!1?0:400000)),_=m/1e6*0.2+A*0.0000166667,z=9.010025,U=16.425;return{lambdaUsd:Number(_.toFixed(2)),alwaysOnFargateAndAlbUsd:Number(25.435025000000003 .toFixed(2)),assumptions:["us-east-1 public list rates snapshot, July 2026","Lambda x86 request and duration pricing with optional monthly free tier","Fargate comparison uses one always-on 0.25 vCPU/0.5 GB task plus one ALB before LCU charges","excludes CloudFront, data transfer, logs, taxes, and application data services"]}}function bl(D){D.command("cdn:api:deploy <distributionId> <alias>","Plan or deploy a private Lambda API behind an existing static distribution").option("--function <name>","Lambda function name",{default:"ts-cloud-static-api"}).option("--path <pattern>","CloudFront API path",{default:"/api/*"}).option("--origin-id <id>","Stable CloudFront origin ID").option("--role <name>","Lambda execution role name").option("--region <region>","Lambda region",{default:"us-east-1"}).option("--profile <name>","AWS credential profile").option("--memory <mb>","Lambda memory in MB",{default:"256"}).option("--timeout <seconds>","Lambda timeout",{default:"10"}).option("--retention <days>","CloudWatch log retention",{default:"14"}).option("--apply","Create resources and patch the live distribution").option("--confirm <text>","Exact distribution:path confirmation").action(async(t,m,A)=>{try{let _=await Cu({distributionId:t,expectedAlias:m,functionName:A.function||"ts-cloud-static-api",pathPattern:A.path,originId:A.originId,roleName:A.role,region:A.region,profile:A.profile,memorySize:Number(A.memory),timeout:Number(A.timeout),logRetentionDays:Number(A.retention),apply:!!A.apply,confirm:A.confirm});if(e(JSON.stringify(_,null,2)),_.applied)n("Private API origin submitted to CloudFront. Run cdn:api:verify after propagation.");else e(`Plan only. Re-run with --apply --confirm '${t}:${_.origin.pathPattern}'.`)}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}}),D.command("cdn:api:verify <alias>","Verify static frontend integrity, API health, latency, and Lambda initialization").option("--function <name>","Lambda function name",{default:"ts-cloud-static-api"}).option("--frontend-sha256 <digest>","Expected SHA-256 of the frontend response").option("--region <region>","Lambda region",{default:"us-east-1"}).option("--profile <name>","AWS credential profile").action(async(t,m)=>{try{let A=new oe(m.region||"us-east-1",m.profile),_=await $u({alias:t,expectedFrontendSha256:m.frontendSha256,logs:A,functionName:m.function||"ts-cloud-static-api"});if(e(JSON.stringify(_,null,2)),!_.api.healthy||_.frontend.unchanged===!1)throw Error("Static API verification failed");n("Static frontend and Lambda API verification passed.")}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),D.command("cdn:api:cost <requests>","Compare a low-volume Lambda API with one always-on Fargate task and ALB").option("--duration <milliseconds>","Average Lambda duration",{default:"100"}).option("--memory <mb>","Lambda memory in MB",{default:"256"}).option("--no-free-tier","Exclude the Lambda free tier").action((t,m)=>{let A=ku({requests:Number(t),averageDurationMs:Number(m.duration),memoryMb:Number(m.memory),includeFreeTier:m.freeTier!==!1});e(JSON.stringify(A,null,2))}),D.command("cdn:origin:add <distributionId> <domain>","Safely add a backend origin and path to an existing distribution").option("--id <origin>","Stable origin ID",{default:"api-backend"}).option("--path <pattern>","Path behavior",{default:"/api/*"}).option("--profile <name>","AWS credential profile").option("--replace","Replace a reviewed path/origin collision").option("--apply","Apply the ETag-protected patch").option("--confirm <text>","Exact distribution:path confirmation").action(async(t,m,A)=>{try{let _=new E(A.profile),z={id:A.id??"api-backend",domainName:m,pathPattern:A.path??"/api/*",replaceExisting:!!A.replace},U=await _.upsertExistingDistributionOrigin(t,{...z,dryRun:!0});if(e(JSON.stringify(U,null,2)),!A.apply){e("Dry run only. Re-run with --apply and the exact --confirm token.");return}let Y=`${t}:${U.pathPattern}`;if(A.confirm!==Y)throw Error(`Pass --confirm "${Y}" to apply this live distribution change`);let X=await _.upsertExistingDistributionOrigin(t,z);n(X.changed?"Backend origin patch submitted.":"Backend origin already matches."),e(JSON.stringify(X,null,2))}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}}),D.command("cdn:origin:remove <distributionId> <domain>","Remove one exact backend behavior and its unreferenced origin").option("--id <origin>","Origin ID",{default:"api-backend"}).option("--path <pattern>","Path behavior",{default:"/api/*"}).option("--profile <name>","AWS credential profile").option("--apply","Apply the ETag-protected rollback").option("--confirm <text>","Exact remove:distribution:path confirmation").action(async(t,m,A)=>{try{let _=new E(A.profile),z={id:A.id??"api-backend",domainName:m,pathPattern:A.path??"/api/*"},U=await _.removeExistingDistributionOrigin(t,{...z,dryRun:!0});if(e(JSON.stringify(U,null,2)),!A.apply){e("Dry run only. Re-run with --apply and the exact --confirm token.");return}let Y=`remove:${t}:${U.pathPattern}`;if(A.confirm!==Y)throw Error(`Pass --confirm "${Y}" to apply this live distribution rollback`);let X=await _.removeExistingDistributionOrigin(t,z);n(X.changed?"Backend behavior rollback submitted.":"Backend behavior was already absent."),e(JSON.stringify(X,null,2))}catch(_){r(_ instanceof Error?_.message:String(_)),process.exitCode=1}}),D.command("cdn:list","List all CloudFront distributions").action(async()=>{i("CloudFront Distributions");try{let t=await Fe(),m=new E,A=new s("Fetching distributions...");A.start();let _=await m.listDistributions();if(A.succeed(`Found ${_.length} distribution(s)`),_.length===0){e("No CloudFront distributions found"),e("Use `cloud cdn:create` to create a new distribution");return}c(["ID","Domain","Status","Enabled","Aliases"],_.map((z)=>[z.Id||"N/A",z.DomainName||"N/A",z.Status||"N/A",z.Enabled?"Yes":"No",(z.Aliases?.Items?.length||0).toString()]))}catch(t){r(`Failed to list distributions: ${t.message}`),process.exit(1)}}),D.command("cdn:status <distributionId>","Get CloudFront distribution status").action(async(t)=>{i(`CloudFront Distribution: ${t}`);try{let m=new E,A=new s("Fetching distribution details...");A.start();let _=await m.getDistribution(t);if(A.succeed("Distribution details loaded"),e(`
|
|
479
479
|
Distribution Information:`),e(` ID: ${_.Id}`),e(` Domain: ${_.DomainName}`),e(` Status: ${_.Status}`),e(` ARN: ${_.ARN}`),e(` Enabled: ${_.Enabled?"Yes":"No"}`),_.Aliases?.Items?.length)e(` Aliases: ${_.Aliases.Items.join(", ")}`);let z=await m.getDistributionConfig(t);e(`
|
|
480
480
|
Origins:`);for(let U of N(z.DistributionConfig?.Origins?.Items?.Origin))e(` - ${U.Id}: ${U.DomainName}`);if(z.DistributionConfig?.DefaultCacheBehavior){let U=z.DistributionConfig.DefaultCacheBehavior;e(`
|
|
481
481
|
Default Cache Behavior:`),e(` Target Origin: ${U.TargetOriginId}`),e(` Viewer Protocol: ${U.ViewerProtocolPolicy}`)}}catch(m){r(`Failed to get distribution: ${m.message}`),process.exit(1)}}),D.command("cdn:create","Create a new CloudFront distribution for an S3 bucket").option("--bucket <name>","S3 bucket name").option("--region <region>","S3 bucket region",{default:"us-east-1"}).option("--alias <domain>","Custom domain alias (CNAME)").option("--certificate <arn>","ACM certificate ARN for custom domain").option("--comment <text>","Distribution comment/description").action(async(t)=>{i("Create CloudFront Distribution");try{if(!t.bucket)r("--bucket is required"),e("Example: cloud cdn:create --bucket my-bucket --region us-east-1"),process.exit(1);let m=new E;if(e(`Bucket: ${t.bucket}`),e(`Region: ${t.region}`),t.alias)e(`Alias: ${t.alias}`);if(!await u(`
|
|
@@ -629,7 +629,7 @@ Full Event Record:`);let ne=JSON.parse(X.CloudTrailEvent);console.log(JSON.strin
|
|
|
629
629
|
Create this trail?`,!0)){e("Operation cancelled");return}let z=new s("Creating trail...");z.start();let U=await A.createTrail({Name:t,S3BucketName:m.bucket,S3KeyPrefix:m.prefix,IsMultiRegionTrail:m.multiRegion,EnableLogFileValidation:m.validation,IncludeGlobalServiceEvents:m.globalEvents??!0});z.text="Starting logging...",await A.startLogging(t),z.succeed("Trail created and logging started"),n(`
|
|
630
630
|
Trail ARN: ${U.TrailARN}`),e(`S3 Bucket: ${U.S3BucketName}`)}catch(A){r(`Failed to create trail: ${A.message}`),process.exit(1)}}),D.command("audit:start <trailName>","Start CloudTrail logging").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Start CloudTrail Logging");try{let A=await oo(m.region),_=new s("Starting logging...");_.start(),await A.startLogging(t),_.succeed("Logging started")}catch(A){r(`Failed to start logging: ${A.message}`),process.exit(1)}}),D.command("audit:stop <trailName>","Stop CloudTrail logging").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Stop CloudTrail Logging");try{let A=await oo(m.region);if(a(`This will stop logging for trail: ${t}`),!await u(`
|
|
631
631
|
Stop logging?`,!1)){e("Operation cancelled");return}let z=new s("Stopping logging...");z.start(),await A.stopLogging(t),z.succeed("Logging stopped")}catch(A){r(`Failed to stop logging: ${A.message}`),process.exit(1)}}),D.command("audit:delete <trailName>","Delete a CloudTrail trail").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Delete CloudTrail Trail");try{let A=await oo(m.region);if(a(`This will permanently delete trail: ${t}`),!await u(`
|
|
632
|
-
Delete this trail?`,!1)){e("Operation cancelled");return}let z=new s("Deleting trail...");z.start(),await A.deleteTrail(t),z.succeed("Trail deleted")}catch(A){r(`Failed to delete trail: ${A.message}`),process.exit(1)}})}function El(D){D.command("status","Show overall infrastructure health dashboard").option("--region <region>","AWS region").action(async(t)=>{i("Infrastructure Status Dashboard");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1";e(`Region: ${A}`),e(`Project: ${m.project.name||"Unknown"}`),e("");let _=[],z=new s("Checking EC2 instances...");z.start();try{let{EC2Client:ye}=await import("./chunk-5xr76cms.js"),Le=await new ye(A).describeInstances({Filters:[{Name:"instance-state-name",Values:["running","pending","stopping","stopped"]}]}),Re=0,De=0,Ee=0;for(let Ke of Le.Reservations||[])for(let je of Ke.Instances||[])if(Ee++,je.State?.Name==="running")Re++;else if(je.State?.Name==="stopped")De++;z.succeed("EC2 instances checked"),_.push({name:"EC2 Instances",status:Re>0?"OK":Ee>0?"WARN":"INFO",details:`${Re} running, ${De} stopped, ${Ee} total`})}catch(ye){z.fail("EC2 check failed"),_.push({name:"EC2 Instances",status:"ERROR",details:ye.message})}let U=new s("Checking RDS instances...");U.start();try{let{RDSClient:ye}=await import("./chunk-5f1n5d80.js"),Re=(await new ye(A).describeDBInstances()).DBInstances||[],De=Re.filter((Ee)=>Ee.DBInstanceStatus==="available").length;U.succeed("RDS instances checked"),_.push({name:"RDS Databases",status:Re.length>0&&De===Re.length?"OK":Re.length>0?"WARN":"INFO",details:`${De} available, ${Re.length} total`})}catch(ye){U.fail("RDS check failed"),_.push({name:"RDS Databases",status:"ERROR",details:ye.message})}let Y=new s("Checking Lambda functions...");Y.start();try{let{LambdaClient:ye}=await import("./chunk-
|
|
632
|
+
Delete this trail?`,!1)){e("Operation cancelled");return}let z=new s("Deleting trail...");z.start(),await A.deleteTrail(t),z.succeed("Trail deleted")}catch(A){r(`Failed to delete trail: ${A.message}`),process.exit(1)}})}function El(D){D.command("status","Show overall infrastructure health dashboard").option("--region <region>","AWS region").action(async(t)=>{i("Infrastructure Status Dashboard");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1";e(`Region: ${A}`),e(`Project: ${m.project.name||"Unknown"}`),e("");let _=[],z=new s("Checking EC2 instances...");z.start();try{let{EC2Client:ye}=await import("./chunk-5xr76cms.js"),Le=await new ye(A).describeInstances({Filters:[{Name:"instance-state-name",Values:["running","pending","stopping","stopped"]}]}),Re=0,De=0,Ee=0;for(let Ke of Le.Reservations||[])for(let je of Ke.Instances||[])if(Ee++,je.State?.Name==="running")Re++;else if(je.State?.Name==="stopped")De++;z.succeed("EC2 instances checked"),_.push({name:"EC2 Instances",status:Re>0?"OK":Ee>0?"WARN":"INFO",details:`${Re} running, ${De} stopped, ${Ee} total`})}catch(ye){z.fail("EC2 check failed"),_.push({name:"EC2 Instances",status:"ERROR",details:ye.message})}let U=new s("Checking RDS instances...");U.start();try{let{RDSClient:ye}=await import("./chunk-5f1n5d80.js"),Re=(await new ye(A).describeDBInstances()).DBInstances||[],De=Re.filter((Ee)=>Ee.DBInstanceStatus==="available").length;U.succeed("RDS instances checked"),_.push({name:"RDS Databases",status:Re.length>0&&De===Re.length?"OK":Re.length>0?"WARN":"INFO",details:`${De} available, ${Re.length} total`})}catch(ye){U.fail("RDS check failed"),_.push({name:"RDS Databases",status:"ERROR",details:ye.message})}let Y=new s("Checking Lambda functions...");Y.start();try{let{LambdaClient:ye}=await import("./chunk-zjvg5pg8.js"),Re=(await new ye(A).listFunctions()).Functions||[];Y.succeed("Lambda functions checked"),_.push({name:"Lambda Functions",status:"OK",details:`${Re.length} function(s)`})}catch(ye){Y.fail("Lambda check failed"),_.push({name:"Lambda Functions",status:"ERROR",details:ye.message})}let X=new s("Checking S3 buckets...");X.start();try{let{S3Client:ye}=await import("./chunk-d0tmmzy5.js"),Re=(await new ye(A).listBuckets()).Buckets||[];X.succeed("S3 buckets checked"),_.push({name:"S3 Buckets",status:"OK",details:`${Re.length} bucket(s)`})}catch(ye){X.fail("S3 check failed"),_.push({name:"S3 Buckets",status:"ERROR",details:ye.message})}let ne=new s("Checking CloudFront distributions...");ne.start();try{let{CloudFrontClient:ye}=await import("./chunk-5mm6qn6m.js"),Le=await new ye().listDistributions(),Re=Le.filter((De)=>De.Status==="Deployed").length;ne.succeed("CloudFront checked"),_.push({name:"CloudFront",status:Le.length>0&&Re===Le.length?"OK":Le.length>0?"WARN":"INFO",details:`${Re} deployed, ${Le.length} total`})}catch(ye){ne.fail("CloudFront check failed"),_.push({name:"CloudFront",status:"ERROR",details:ye.message})}let re=new s("Checking SQS queues...");re.start();try{let{SQSClient:ye}=await import("./chunk-1xxmyx0r.js"),Re=(await new ye(A).listQueues()).QueueUrls||[];re.succeed("SQS queues checked"),_.push({name:"SQS Queues",status:"OK",details:`${Re.length} queue(s)`})}catch(ye){re.fail("SQS check failed"),_.push({name:"SQS Queues",status:"ERROR",details:ye.message})}let se=new s("Checking CloudFormation stacks...");se.start();try{let{CloudFormationClient:ye}=await import("./chunk-txykq6f1.js"),Re=(await new ye(A).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE"])).StackSummaries||[],De=Re.filter((je)=>je.StackStatus==="CREATE_COMPLETE"||je.StackStatus==="UPDATE_COMPLETE").length,Ee=Re.filter((je)=>je.StackStatus?.includes("IN_PROGRESS")).length,Ke=Re.filter((je)=>je.StackStatus?.includes("ROLLBACK")).length;se.succeed("CloudFormation checked"),_.push({name:"CloudFormation",status:Ke>0?"WARN":Ee>0?"INFO":"OK",details:`${De} healthy, ${Ee} in progress, ${Ke} rolled back`})}catch(ye){se.fail("CloudFormation check failed"),_.push({name:"CloudFormation",status:"ERROR",details:ye.message})}let he=new s("Checking SSL certificates...");he.start();try{let{ACMClient:ye}=await import("./chunk-fqghdpvr.js"),Ie=new ye("us-east-1"),Re=(await Ie.listCertificates()).CertificateSummaryList||[],De=await Promise.all(Re.map((_e)=>Ie.describeCertificate({CertificateArn:_e.CertificateArn}))),Ee=De.filter((_e)=>_e.Status==="ISSUED").length,Ke=De.filter((_e)=>_e.Status==="PENDING_VALIDATION").length,je=De.filter((_e)=>{if(_e.NotAfter)return(new Date(_e.NotAfter).getTime()-Date.now())/86400000<30;return!1}).length;he.succeed("SSL certificates checked"),_.push({name:"SSL Certificates",status:je>0?"WARN":Ke>0?"INFO":"OK",details:`${Ee} issued, ${Ke} pending${je>0?`, ${je} expiring soon`:""}`})}catch(ye){he.fail("ACM check failed"),_.push({name:"SSL Certificates",status:"ERROR",details:ye.message})}e(`
|
|
633
633
|
`+"=".repeat(60)),e("HEALTH SUMMARY"),e("=".repeat(60)+`
|
|
634
634
|
`);for(let ye of _){let Ie="";if(ye.status==="OK")Ie=`${C.green}[OK]${C.reset}`;else if(ye.status==="WARN")Ie=`${C.yellow}[WARN]${C.reset}`;else if(ye.status==="ERROR")Ie=`${C.red}[ERROR]${C.reset}`;else Ie=`${C.blue}[INFO]${C.reset}`;console.log(`${Ie} ${ye.name.padEnd(20)} ${ye.details}`)}let we=_.some((ye)=>ye.status==="ERROR"),Pe=_.some((ye)=>ye.status==="WARN");if(e(""),we)r("Some services have errors. Check the details above.");else if(Pe)a("Some services need attention. Check the warnings above.");else n("All services are healthy!")}catch(m){r(`Failed to get status: ${m.message}`),process.exit(1)}}),D.command("status:costs","Show current month cost summary").option("--region <region>","AWS region").action(async(t)=>{i("Cost Summary");try{e("Fetching cost data from AWS Cost Explorer..."),e("");let m=new Date,A=new Date(m.getFullYear(),m.getMonth(),1),_=new Date(m.getFullYear(),m.getMonth()+1,0);e(`Period: ${A.toISOString().split("T")[0]} to ${_.toISOString().split("T")[0]}`),e(""),e("Note: Cost data requires AWS Cost Explorer API access."),e("Run `cloud cost` for detailed cost analysis.")}catch(m){r(`Failed to get cost summary: ${m.message}`),process.exit(1)}}),D.command("status:alarms","Show CloudWatch alarm status").option("--region <region>","AWS region").action(async(t)=>{i("CloudWatch Alarms");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1",{AWSClient:_}=await import("./chunk-4nn00brw.js");class z{client;region;constructor(we){this.region=we,this.client=new _}async describeAlarms(){return this.client.request({service:"monitoring",region:this.region,method:"POST",path:"/",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:"Action=DescribeAlarms&Version=2010-08-01"})}}let U=new z(A),Y=new s("Fetching alarms...");Y.start();let ne=(await U.describeAlarms()).MetricAlarms||[];if(Y.succeed(`Found ${ne.length} alarm(s)`),ne.length===0){e("No CloudWatch alarms configured");return}let re=ne.filter((we)=>we.StateValue==="ALARM"),se=ne.filter((we)=>we.StateValue==="OK"),he=ne.filter((we)=>we.StateValue==="INSUFFICIENT_DATA");if(e(""),e(`Alarming: ${re.length}`),e(`OK: ${se.length}`),e(`Insufficient Data: ${he.length}`),re.length>0){e(`
|
|
635
635
|
Alarms in ALARM state:`);for(let we of re)r(` - ${we.AlarmName}: ${we.AlarmDescription||"No description"}`)}}catch(m){r(`Failed to get alarms: ${m.message}`),process.exit(1)}})}async function Ur(D){let{AWSClient:t}=await import("./chunk-4nn00brw.js");class m{client;region;constructor(A){this.region=A,this.client=new t}async jsonRpcRequest(A,_){return this.client.request({service:"backup",region:this.region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":`CryoControllerFrontendService.${A}`},body:JSON.stringify(_)})}async listBackupVaults(){return this.jsonRpcRequest("ListBackupVaults",{})}async listBackupPlans(){return this.jsonRpcRequest("ListBackupPlans",{})}async listRecoveryPointsByBackupVault(A,_){return this.jsonRpcRequest("ListRecoveryPointsByBackupVault",{BackupVaultName:A,..._})}async describeBackupVault(A){return this.jsonRpcRequest("DescribeBackupVault",{BackupVaultName:A})}async getBackupPlan(A){return this.jsonRpcRequest("GetBackupPlan",{BackupPlanId:A})}async createBackupVault(A,_){return this.jsonRpcRequest("CreateBackupVault",{BackupVaultName:A,..._})}async deleteBackupVault(A){return this.jsonRpcRequest("DeleteBackupVault",{BackupVaultName:A})}async createBackupPlan(A){return this.jsonRpcRequest("CreateBackupPlan",{BackupPlan:A})}async deleteBackupPlan(A){return this.jsonRpcRequest("DeleteBackupPlan",{BackupPlanId:A})}async startBackupJob(A){return this.jsonRpcRequest("StartBackupJob",A)}async startRestoreJob(A){return this.jsonRpcRequest("StartRestoreJob",A)}async describeBackupJob(A){return this.jsonRpcRequest("DescribeBackupJob",{BackupJobId:A})}async describeRestoreJob(A){return this.jsonRpcRequest("DescribeRestoreJob",{RestoreJobId:A})}async listBackupJobs(A){return this.jsonRpcRequest("ListBackupJobs",A||{})}async listRestoreJobs(A){return this.jsonRpcRequest("ListRestoreJobs",A||{})}async createBackupSelection(A,_){return this.jsonRpcRequest("CreateBackupSelection",{BackupPlanId:A,BackupSelection:_})}async listBackupSelections(A){return this.jsonRpcRequest("ListBackupSelections",{BackupPlanId:A})}}return new m(D)}function Tl(D){D.command("backup:vaults","List backup vaults").option("--region <region>","AWS region").action(async(t)=>{i("Backup Vaults");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1",_=await Ur(A),z=new s("Fetching vaults...");z.start();let Y=(await _.listBackupVaults()).BackupVaultList||[];if(z.succeed(`Found ${Y.length} vault(s)`),Y.length===0){e("No backup vaults found"),e("Use `cloud backup:create-vault` to create a new vault");return}c(["Vault Name","Recovery Points","Created","Encrypted"],Y.map((X)=>[X.BackupVaultName||"N/A",(X.NumberOfRecoveryPoints||0).toString(),X.CreationDate?new Date(X.CreationDate).toLocaleDateString():"N/A",X.EncryptionKeyArn?"Yes":"Default"]))}catch(m){r(`Failed to list vaults: ${m.message}`),process.exit(1)}}),D.command("backup:list","List backup plans").option("--region <region>","AWS region").action(async(t)=>{i("Backup Plans");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1",_=await Ur(A),z=new s("Fetching backup plans...");z.start();let Y=(await _.listBackupPlans()).BackupPlansList||[];if(z.succeed(`Found ${Y.length} backup plan(s)`),Y.length===0){e("No backup plans found"),e("Use `cloud backup:create` to create a backup plan");return}c(["Plan Name","Plan ID","Version","Created"],Y.map((X)=>[X.BackupPlanName||"N/A",X.BackupPlanId||"N/A",X.VersionId?.substring(0,8)||"N/A",X.CreationDate?new Date(X.CreationDate).toLocaleDateString():"N/A"]))}catch(m){r(`Failed to list backup plans: ${m.message}`),process.exit(1)}}),D.command("backup:recovery-points <vaultName>","List recovery points in a vault").option("--region <region>","AWS region").option("--limit <number>","Maximum results",{default:"50"}).action(async(t,m)=>{i(`Recovery Points: ${t}`);try{let A=await Fe(),_=m.region||A.project.region||"us-east-1",z=await Ur(_),U=new s("Fetching recovery points...");U.start();let X=(await z.listRecoveryPointsByBackupVault(t,{MaxResults:Number.parseInt(m.limit)})).RecoveryPoints||[];if(U.succeed(`Found ${X.length} recovery point(s)`),X.length===0){e("No recovery points found");return}c(["Resource","Status","Created","Size","Lifecycle"],X.map((ne)=>[ne.ResourceArn?.split(":").pop()||"N/A",ne.Status||"N/A",ne.CreationDate?new Date(ne.CreationDate).toLocaleString():"N/A",ne.BackupSizeInBytes?Du(ne.BackupSizeInBytes):"N/A",ne.Lifecycle?.DeleteAfterDays?`${ne.Lifecycle.DeleteAfterDays} days`:"Indefinite"]))}catch(A){r(`Failed to list recovery points: ${A.message}`),process.exit(1)}}),D.command("backup:create-vault <vaultName>","Create a backup vault").option("--region <region>","AWS region",{default:"us-east-1"}).option("--kms-key <arn>","KMS key ARN for encryption").action(async(t,m)=>{i("Create Backup Vault");try{let A=await Ur(m.region);if(e(`Vault Name: ${t}`),e(`Region: ${m.region}`),e(`Encryption: ${m.kmsKey?"Custom KMS":"AWS Managed"}`),!await u(`
|
|
@@ -644,7 +644,7 @@ Job ID: ${U.BackupJobId}`),e("Use `cloud backup:jobs` to check status")}catch(A)
|
|
|
644
644
|
Restore will create new resources. Review carefully.`),!await u(`
|
|
645
645
|
Start restore?`,!1)){e("Operation cancelled");return}let U=new s("Starting restore job...");U.start();let Y=await A.startRestoreJob({RecoveryPointArn:t,IamRoleArn:m.role,Metadata:_,IdempotencyToken:`cli-${Date.now()}`});U.succeed("Restore job started"),n(`
|
|
646
646
|
Job ID: ${Y.RestoreJobId}`),e("Use `cloud backup:restore-jobs` to check status")}catch(A){r(`Failed to start restore: ${A.message}`),process.exit(1)}}),D.command("backup:jobs","List recent backup jobs").option("--region <region>","AWS region").option("--state <state>","Filter by state (CREATED, PENDING, RUNNING, COMPLETED, FAILED)").action(async(t)=>{i("Backup Jobs");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1",_=await Ur(A),z=new s("Fetching backup jobs...");z.start();let Y=(await _.listBackupJobs({ByState:t.state,MaxResults:50})).BackupJobs||[];if(z.succeed(`Found ${Y.length} job(s)`),Y.length===0){e("No backup jobs found");return}c(["Job ID","Resource","State","Started","Size"],Y.map((X)=>[X.BackupJobId?.substring(0,16)||"N/A",X.ResourceArn?.split(":").pop()||"N/A",X.State||"N/A",X.CreationDate?new Date(X.CreationDate).toLocaleString():"N/A",X.BackupSizeInBytes?Du(X.BackupSizeInBytes):"N/A"]))}catch(m){r(`Failed to list backup jobs: ${m.message}`),process.exit(1)}}),D.command("backup:restore-jobs","List recent restore jobs").option("--region <region>","AWS region").option("--status <status>","Filter by status").action(async(t)=>{i("Restore Jobs");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1",_=await Ur(A),z=new s("Fetching restore jobs...");z.start();let Y=(await _.listRestoreJobs({ByStatus:t.status,MaxResults:50})).RestoreJobs||[];if(z.succeed(`Found ${Y.length} job(s)`),Y.length===0){e("No restore jobs found");return}c(["Job ID","Resource","Status","Started","Completed"],Y.map((X)=>[X.RestoreJobId?.substring(0,16)||"N/A",X.CreatedResourceArn?.split(":").pop()||"Pending",X.Status||"N/A",X.CreationDate?new Date(X.CreationDate).toLocaleString():"N/A",X.CompletionDate?new Date(X.CompletionDate).toLocaleString():"-"]))}catch(m){r(`Failed to list restore jobs: ${m.message}`),process.exit(1)}}),D.command("backup:schedule","Show backup schedule overview").option("--region <region>","AWS region").action(async(t)=>{i("Backup Schedule Overview");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1",_=await Ur(A),z=new s("Fetching backup plans...");z.start();let Y=(await _.listBackupPlans()).BackupPlansList||[];if(z.succeed(`Found ${Y.length} backup plan(s)`),Y.length===0){e("No backup plans configured");return}for(let X of Y){e(`
|
|
647
|
-
${X.BackupPlanName}`),e("=".repeat(40));try{let re=(await _.getBackupPlan(X.BackupPlanId)).BackupPlan?.Rules||[];for(let he of re)if(e(` Rule: ${he.RuleName}`),e(` Schedule: ${he.ScheduleExpression}`),e(` Vault: ${he.TargetBackupVaultName}`),he.Lifecycle?.DeleteAfterDays)e(` Retention: ${he.Lifecycle.DeleteAfterDays} days`);let se=await _.listBackupSelections(X.BackupPlanId);if(se.BackupSelectionsList&&se.BackupSelectionsList.length>0)e(` Selections: ${se.BackupSelectionsList.length}`)}catch{e(" (Unable to load plan details)")}}}catch(m){r(`Failed to get schedule: ${m.message}`),process.exit(1)}})}function Du(D){if(D===0)return"0 B";let t=1024,m=["B","KB","MB","GB","TB"],A=Math.floor(Math.log(D)/Math.log(t));return`${Number.parseFloat((D/t**A).toFixed(2))} ${m[A]}`}async function yh(D){let t=await Fe(),m=y(process.cwd(),t),A=D??Object.keys(t.environments??{})[0]??"production",_=m.environments.get(A);if(!_)throw m.store.close(),Error(`Environment ${A} was not found.`);let z=new S(m.store,{workerId:`cli:${process.pid}`}),U=new ni(m.store),Y=new Jr(U,z),X=new er(m.store),ne=new tr(m.store,x(process.cwd())),re=m.store.getActorByExternalId("system","cli")??m.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:t,controlPlane:m,env:A,environmentRecord:_,queue:z,store:U,coordinator:Y,dataServices:X,secrets:ne,actor:re}}async function $i(D,t){let m=await yh(D);try{return await t(m)}finally{m.controlPlane.store.close()}}async function ki(D){try{await D()}catch(t){r(t instanceof Error?t.message:String(t))}}function Ni(D,t,m,A){D.controlPlane.store.appendEvent({organizationId:D.controlPlane.organization.id,projectId:D.controlPlane.project.id,resourceId:A,actorId:D.actor.id,type:`backup.${t}`,payload:m})}function vh(D,t){let m=D.store.getPolicy(t)??D.store.listPolicies(D.controlPlane.project.id,D.environmentRecord.id).find((A)=>A.name===t);if(!m||m.environmentId!==D.environmentRecord.id)throw Error(`Backup policy ${t} was not found in ${D.env}.`);return m}function Ia(D,t){let m=D.store.getRecoveryPoint(t),A=D.store.listRecoveryPoints(D.controlPlane.project.id).filter((z)=>z.id.startsWith(t)),_=m??(A.length===1?A[0]:void 0);if(!_)throw Error(A.length>1?`Recovery point prefix ${t} is ambiguous.`:`Recovery point ${t} was not found.`);return _}function xu(D,t){let m=D.store.getDestination(t)??D.store.listDestinations(D.controlPlane.project.id).find((A)=>A.name===t);if(!m)throw Error(`Backup destination ${t} was not found.`);return m}function bh(D,t){return D.kind==="volume"?{volumeName:t}:D.kind==="logical_database"?{targetId:t,dataServiceId:t}:{targetId:t}}function wh(D,t=[]){let m=new Map(t.map((A)=>[A.id,A.name]));return D.map((A)=>[A.id,A.status,A.verificationState,A.kind,m.get(A.policyId??"")??"manual",A.pointInTime,A.sizeBytes?`${A.sizeBytes} B`:"provider",A.held?"hold":A.pinned?"pinned":"retention"])}function Nl(D){D.command("recovery:list","Show backup coverage, destination health, and recent recovery points").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>ki(async()=>$i(t.env,async(m)=>{let A=m.store.listPolicies(m.controlPlane.project.id,m.environmentRecord.id),_=new Set(A.map((Y)=>Y.id)),z=m.store.listRecoveryPoints(m.controlPlane.project.id).filter((Y)=>!Y.policyId||_.has(Y.policyId)),U={coverage:m.store.coverage(m.controlPlane.project.id).filter((Y)=>_.has(Y.policy.id)),destinations:m.store.listDestinations(m.controlPlane.project.id).map((Y)=>({...Y,credentialRef:void 0,encryptionKeyRef:void 0,credentialsConfigured:!!Y.credentialRef,clientEncryptionConfigured:!!Y.encryptionKeyRef})),policies:A,recoveryPoints:z,jobs:m.store.listJobs(m.controlPlane.project.id).filter((Y)=>!Y.policyId||_.has(Y.policyId))};if(t.json)e(JSON.stringify(U,null,2));else c(["Policy","RPO","Destination","Latest point","Verification"],U.coverage.map((Y)=>[Y.policy.name,Y.missedRpo?"MISSED":"protected",Y.destinationHealthy?"healthy":"attention",Y.lastRecoveryPoint?.pointInTime??"none",Y.lastRecoveryPoint?.verificationState??"none"])),c(["ID","State","Verification","Kind","Policy","Point in time","Size","Protection"],wh(z,A))}))),D.command("recovery:jobs","List durable backup, verification, restore, drill, and cleanup jobs").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>ki(async()=>$i(t.env,async(m)=>{let A=new Set(m.store.listPolicies(m.controlPlane.project.id,m.environmentRecord.id).map((z)=>z.id)),_=m.store.listJobs(m.controlPlane.project.id).filter((z)=>!z.policyId||A.has(z.policyId));if(t.json)e(JSON.stringify(_,null,2));else c(["ID","Kind","State","Phase","Operation","Started","Finished","Error"],_.map((z)=>[z.id,z.kind,z.status,String(z.progress.phase??"queued"),z.operationId??"—",z.startedAt??"—",z.finishedAt??"—",z.error??"—"]))}))),D.command("recovery:destination:add <name>","Connect an S3, S3-compatible, or AWS Backup destination").option("--env <environment>","Target environment").option("--provider <provider>","aws_s3, s3_compatible, or aws_backup",{default:"aws_s3"}).option("--bucket <bucket>","Object bucket").option("--endpoint <url>","Origin-only HTTPS endpoint").option("--allow-private","Allow a private endpoint").option("--path-style","Force path-style S3 URLs").option("--prefix <prefix>","Object key prefix").option("--region <region>","Destination region").option("--credentials-env <name>","Environment variable containing credential JSON").option("--encryption <mode>","provider, client_side, or both",{default:"provider"}).option("--encryption-key-env <name>","Environment variable containing the client encryption key").option("--lock-days <days>","Default immutable retention days").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{if(!["aws_s3","s3_compatible","aws_backup"].includes(m.provider??""))throw Error("--provider must be aws_s3, s3_compatible, or aws_backup.");if(!["provider","client_side","both"].includes(m.encryption??""))throw Error("--encryption must be provider, client_side, or both.");let _=`secret://data-services/backups/${A.controlPlane.project.id}/${t}`,z=[];try{let U,Y;if(m.credentialsEnv){let ne=process.env[m.credentialsEnv];if(!ne)throw Error(`${m.credentialsEnv} is empty.`);U=`${_}/credentials`,await A.secrets.put(U,ne),z.push(U)}if(m.encryption!=="provider"){if(!m.encryptionKeyEnv)throw Error("Client encryption requires --encryption-key-env.");let ne=process.env[m.encryptionKeyEnv];if(!ne||ne.length<32)throw Error(`${m.encryptionKeyEnv} must contain at least 32 characters.`);Y=`${_}/encryption-key`,await A.secrets.put(Y,ne),z.push(Y)}let X=A.store.createDestination({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,provider:m.provider,endpoint:m.endpoint,endpointPolicy:m.allowPrivate?"allow_private":"public_https",bucket:m.bucket,prefix:m.prefix??"",region:m.region??A.config.project.region,forcePathStyle:!!m.pathStyle,credentialRef:U,encryption:m.encryption,encryptionKeyRef:Y,immutability:{objectLock:!!m.lockDays,defaultRetentionDays:m.lockDays?Number(m.lockDays):void 0},status:"untested"});Ni(A,"destination_created",{destinationId:X.id,provider:X.provider}),n(`Created destination ${X.name} (${X.id}); test it with cloud recovery:destination:test ${X.name}`)}catch(U){for(let Y of z)await A.secrets.remove(Y).catch(()=>{});throw U}}))),D.command("recovery:destination:test <destination>","Write, read, verify, and clean a destination health object").option("--env <environment>","Target environment").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{let _=xu(A,t);if(_.provider==="aws_backup")throw Error("AWS Backup is verified by creating a provider recovery point.");try{await new ii(A.secrets).test(_),A.store.recordDestinationTest(_.id,{ok:!0}),Ni(A,"destination_tested",{destinationId:_.id,ok:!0}),n(`${_.name} passed write, read, checksum, and cleanup checks.`)}catch(z){let U=z instanceof Error?z.message:String(z);throw A.store.recordDestinationTest(_.id,{ok:!1,error:U}),Ni(A,"destination_tested",{destinationId:_.id,ok:!1}),z}}))),D.command("recovery:policy:add <name>","Create a scheduled recovery policy").option("--env <environment>","Target environment").option("--destination <id>","Destination ID or name").option("--kind <kind>","managed_database, logical_database, volume, files, control_plane, or infrastructure").option("--data-service <id>","Data service ID or name").option("--resource <id>","Resource ID or slug").option("--volume <name>","Named Docker volume").option("--include <path>","Project-relative include path or provider setting (repeatable)").option("--exclude <pattern>","Archive exclude pattern (repeatable)").option("--schedule <expression>","Cron, rate, or preset",{default:"daily"}).option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--rpo <minutes>","Recovery point objective",{default:"1440"}).option("--rto <minutes>","Recovery time objective",{default:"120"}).option("--keep <count>","Minimum latest points",{default:"7"}).option("--expire <days>","Expiry days",{default:"30"}).option("--compression <kind>","none, gzip, or zstd",{default:"gzip"}).option("--disabled","Create disabled").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{if(!m.destination)throw Error("--destination is required.");if(!["managed_database","logical_database","volume","files","control_plane","infrastructure"].includes(m.kind??""))throw Error("--kind must be managed_database, logical_database, volume, files, control_plane, or infrastructure.");let _=xu(A,m.destination),z=A.dataServices.list(A.controlPlane.project.id,A.environmentRecord.id),U=m.dataService?A.dataServices.get(m.dataService)??z.find((he)=>he.name===m.dataService):void 0,Y=A.controlPlane.store.listResources(A.controlPlane.project.id,A.environmentRecord.id),X=m.resource?Y.find((he)=>he.id===m.resource||he.slug===m.resource):void 0,ne=m.include?Array.isArray(m.include)?m.include:[m.include]:[],re=m.exclude?Array.isArray(m.exclude)?m.exclude:[m.exclude]:[];if(["managed_database","logical_database"].includes(m.kind??"")&&!U)throw Error("--data-service must identify a service in this environment.");if(m.kind==="volume"&&(!X||!m.volume))throw Error("Volume policies require --resource and --volume.");if(m.kind==="files"&&(!X||!ne.length))throw Error("File policies require --resource and at least one --include path.");if(m.kind==="infrastructure"&&(!ne.some((he)=>he.startsWith("resource:arn:"))||!ne.some((he)=>he.startsWith("role:arn:"))))throw Error("Infrastructure policies require --include resource:<arn> and --include role:<arn>.");let se=A.store.createPolicy({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,environmentId:A.environmentRecord.id,resourceId:X?.id,dataServiceId:U?.id,destinationId:_.id,name:t,resourceKind:m.kind,schedule:m.schedule??"daily",timezone:m.timezone??"UTC",retention:{keepLast:Number(m.keep)||7,expireAfterDays:Number(m.expire)||30},compression:m.compression,encryption:"destination",includePatterns:m.volume?[m.volume,...ne]:ne,excludePatterns:re,expectedRpoMinutes:Number(m.rpo)||1440,expectedRtoMinutes:Number(m.rto)||120,enabled:!m.disabled});Ni(A,"policy_created",{policyId:se.id,resourceKind:se.resourceKind},se.resourceId),n(`Created policy ${se.name} (${se.id}); next run ${se.nextRunAt}`)}))),D.command("recovery:run <policy>","Queue an on-demand run of a recovery policy").option("--env <environment>","Target environment").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{let _=vh(A,t),z=A.coordinator.enqueueBackup(_,new Date().toISOString(),A.actor.id);Ni(A,"run_queued",{policyId:_.id,backupJobId:z.id},_.resourceId),n(`Queued backup job ${z.id}; inspect it with cloud recovery:jobs`)}))),D.command("recovery:verify <point>","Queue independent verification of a recovery point").option("--env <environment>","Target environment").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{let _=Ia(A,t),z=A.coordinator.enqueueVerification(_);Ni(A,"verification_queued",{recoveryPointId:_.id,backupJobId:z.id},_.resourceId),n(`Queued verification job ${z.id}`)}))),D.command("recovery:restore <point> <target>","Validate or queue an isolated, in-place, or drill restore").option("--env <environment>","Target environment").option("--mode <mode>","isolated or in_place",{default:"isolated"}).option("--drill","Validate health and clean the isolated target").option("--execute","Queue the validated restore").option("--confirm <target>","Exact in-place target confirmation").option("--ack-downtime","Acknowledge downtime and data replacement").option("--safety <point>","Distinct verified safety recovery point").option("--metadata <json>","Provider restore metadata JSON").action(async(t,m,A)=>ki(async()=>$i(A.env,async(_)=>{if(!["isolated","in_place"].includes(A.mode??""))throw Error("--mode must be isolated or in_place.");if(A.drill&&A.mode==="in_place")throw Error("Recovery drills require --mode isolated.");let z=Ia(_,t),U={mode:A.mode,target:{...bh(z,m),...A.metadata?{metadata:JSON.parse(A.metadata)}:{},inPlace:A.mode==="in_place"},targetName:m,confirm:A.confirm,recentAuth:!0,downtimeAcknowledged:!!A.ackDowntime,safetyBackupId:A.safety?Ia(_,A.safety).id:void 0},Y=_.coordinator.planRestore(z,U);if(!A.execute){e(JSON.stringify({...Y,productionExecutionCreated:!1},null,2));return}let X=_.coordinator.enqueueRestore(z,{...U,drill:!!A.drill,actorId:_.actor.id});Ni(_,A.drill?"drill_queued":"restore_queued",{recoveryPointId:z.id,backupJobId:X.id,mode:U.mode,target:m},z.resourceId),n(`Queued ${A.drill?"drill":"restore"} job ${X.id}`)}))),D.command("recovery:protect <point>","Pin, hold, or release retention protection").option("--env <environment>","Target environment").option("--pin","Pin the point").option("--unpin","Remove its pin").option("--hold","Place a legal hold").option("--release-hold","Release its legal hold").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{let _=Ia(A,t);if(![m.pin,m.unpin,m.hold,m.releaseHold].filter(Boolean).length)throw Error("Choose --pin, --unpin, --hold, or --release-hold.");let z=A.store.updateRecoveryPoint(_.id,{pinned:m.pin?!0:m.unpin?!1:_.pinned,held:m.hold?!0:m.releaseHold?!1:_.held});Ni(A,"protection_updated",{recoveryPointId:_.id,pinned:z.pinned,held:z.held},_.resourceId),n(`${z.id}: pinned=${z.pinned} held=${z.held}`)}))),D.command("recovery:retention","Queue deletion of expired, unlocked, unheld recovery points").option("--env <environment>","Target environment").action(async(t)=>ki(async()=>$i(t.env,async(m)=>{let A=m.coordinator.enqueueRetention();Ni(m,"retention_queued",{jobs:A.length}),n(`Queued ${A.length} retention cleanup job${A.length===1?"":"s"}.`)})))}function Ol(D){D.command("api:list","List all API Gateway APIs").option("--region <region>","AWS region").action(async(t)=>{i("API Gateway APIs");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1",{LambdaClient:_}=await import("./chunk-vt3hjh9q.js"),z=new _(A),U=new s("Fetching APIs...");U.start();let{CloudFormationClient:Y}=await import("./chunk-txykq6f1.js"),re=(await new Y(A).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE"])).StackSummaries.filter((se)=>se.StackName?.includes("api")||se.StackName?.includes("Api")||se.StackName?.includes("API"));if(U.succeed("APIs listed"),e(`
|
|
647
|
+
${X.BackupPlanName}`),e("=".repeat(40));try{let re=(await _.getBackupPlan(X.BackupPlanId)).BackupPlan?.Rules||[];for(let he of re)if(e(` Rule: ${he.RuleName}`),e(` Schedule: ${he.ScheduleExpression}`),e(` Vault: ${he.TargetBackupVaultName}`),he.Lifecycle?.DeleteAfterDays)e(` Retention: ${he.Lifecycle.DeleteAfterDays} days`);let se=await _.listBackupSelections(X.BackupPlanId);if(se.BackupSelectionsList&&se.BackupSelectionsList.length>0)e(` Selections: ${se.BackupSelectionsList.length}`)}catch{e(" (Unable to load plan details)")}}}catch(m){r(`Failed to get schedule: ${m.message}`),process.exit(1)}})}function Du(D){if(D===0)return"0 B";let t=1024,m=["B","KB","MB","GB","TB"],A=Math.floor(Math.log(D)/Math.log(t));return`${Number.parseFloat((D/t**A).toFixed(2))} ${m[A]}`}async function yh(D){let t=await Fe(),m=y(process.cwd(),t),A=D??Object.keys(t.environments??{})[0]??"production",_=m.environments.get(A);if(!_)throw m.store.close(),Error(`Environment ${A} was not found.`);let z=new S(m.store,{workerId:`cli:${process.pid}`}),U=new ni(m.store),Y=new Jr(U,z),X=new er(m.store),ne=new tr(m.store,x(process.cwd())),re=m.store.getActorByExternalId("system","cli")??m.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:t,controlPlane:m,env:A,environmentRecord:_,queue:z,store:U,coordinator:Y,dataServices:X,secrets:ne,actor:re}}async function $i(D,t){let m=await yh(D);try{return await t(m)}finally{m.controlPlane.store.close()}}async function ki(D){try{await D()}catch(t){r(t instanceof Error?t.message:String(t))}}function Ni(D,t,m,A){D.controlPlane.store.appendEvent({organizationId:D.controlPlane.organization.id,projectId:D.controlPlane.project.id,resourceId:A,actorId:D.actor.id,type:`backup.${t}`,payload:m})}function vh(D,t){let m=D.store.getPolicy(t)??D.store.listPolicies(D.controlPlane.project.id,D.environmentRecord.id).find((A)=>A.name===t);if(!m||m.environmentId!==D.environmentRecord.id)throw Error(`Backup policy ${t} was not found in ${D.env}.`);return m}function Ia(D,t){let m=D.store.getRecoveryPoint(t),A=D.store.listRecoveryPoints(D.controlPlane.project.id).filter((z)=>z.id.startsWith(t)),_=m??(A.length===1?A[0]:void 0);if(!_)throw Error(A.length>1?`Recovery point prefix ${t} is ambiguous.`:`Recovery point ${t} was not found.`);return _}function xu(D,t){let m=D.store.getDestination(t)??D.store.listDestinations(D.controlPlane.project.id).find((A)=>A.name===t);if(!m)throw Error(`Backup destination ${t} was not found.`);return m}function bh(D,t){return D.kind==="volume"?{volumeName:t}:D.kind==="logical_database"?{targetId:t,dataServiceId:t}:{targetId:t}}function wh(D,t=[]){let m=new Map(t.map((A)=>[A.id,A.name]));return D.map((A)=>[A.id,A.status,A.verificationState,A.kind,m.get(A.policyId??"")??"manual",A.pointInTime,A.sizeBytes?`${A.sizeBytes} B`:"provider",A.held?"hold":A.pinned?"pinned":"retention"])}function Nl(D){D.command("recovery:list","Show backup coverage, destination health, and recent recovery points").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>ki(async()=>$i(t.env,async(m)=>{let A=m.store.listPolicies(m.controlPlane.project.id,m.environmentRecord.id),_=new Set(A.map((Y)=>Y.id)),z=m.store.listRecoveryPoints(m.controlPlane.project.id).filter((Y)=>!Y.policyId||_.has(Y.policyId)),U={coverage:m.store.coverage(m.controlPlane.project.id).filter((Y)=>_.has(Y.policy.id)),destinations:m.store.listDestinations(m.controlPlane.project.id).map((Y)=>({...Y,credentialRef:void 0,encryptionKeyRef:void 0,credentialsConfigured:!!Y.credentialRef,clientEncryptionConfigured:!!Y.encryptionKeyRef})),policies:A,recoveryPoints:z,jobs:m.store.listJobs(m.controlPlane.project.id).filter((Y)=>!Y.policyId||_.has(Y.policyId))};if(t.json)e(JSON.stringify(U,null,2));else c(["Policy","RPO","Destination","Latest point","Verification"],U.coverage.map((Y)=>[Y.policy.name,Y.missedRpo?"MISSED":"protected",Y.destinationHealthy?"healthy":"attention",Y.lastRecoveryPoint?.pointInTime??"none",Y.lastRecoveryPoint?.verificationState??"none"])),c(["ID","State","Verification","Kind","Policy","Point in time","Size","Protection"],wh(z,A))}))),D.command("recovery:jobs","List durable backup, verification, restore, drill, and cleanup jobs").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>ki(async()=>$i(t.env,async(m)=>{let A=new Set(m.store.listPolicies(m.controlPlane.project.id,m.environmentRecord.id).map((z)=>z.id)),_=m.store.listJobs(m.controlPlane.project.id).filter((z)=>!z.policyId||A.has(z.policyId));if(t.json)e(JSON.stringify(_,null,2));else c(["ID","Kind","State","Phase","Operation","Started","Finished","Error"],_.map((z)=>[z.id,z.kind,z.status,String(z.progress.phase??"queued"),z.operationId??"—",z.startedAt??"—",z.finishedAt??"—",z.error??"—"]))}))),D.command("recovery:destination:add <name>","Connect an S3, S3-compatible, or AWS Backup destination").option("--env <environment>","Target environment").option("--provider <provider>","aws_s3, s3_compatible, or aws_backup",{default:"aws_s3"}).option("--bucket <bucket>","Object bucket").option("--endpoint <url>","Origin-only HTTPS endpoint").option("--allow-private","Allow a private endpoint").option("--path-style","Force path-style S3 URLs").option("--prefix <prefix>","Object key prefix").option("--region <region>","Destination region").option("--credentials-env <name>","Environment variable containing credential JSON").option("--encryption <mode>","provider, client_side, or both",{default:"provider"}).option("--encryption-key-env <name>","Environment variable containing the client encryption key").option("--lock-days <days>","Default immutable retention days").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{if(!["aws_s3","s3_compatible","aws_backup"].includes(m.provider??""))throw Error("--provider must be aws_s3, s3_compatible, or aws_backup.");if(!["provider","client_side","both"].includes(m.encryption??""))throw Error("--encryption must be provider, client_side, or both.");let _=`secret://data-services/backups/${A.controlPlane.project.id}/${t}`,z=[];try{let U,Y;if(m.credentialsEnv){let ne=process.env[m.credentialsEnv];if(!ne)throw Error(`${m.credentialsEnv} is empty.`);U=`${_}/credentials`,await A.secrets.put(U,ne),z.push(U)}if(m.encryption!=="provider"){if(!m.encryptionKeyEnv)throw Error("Client encryption requires --encryption-key-env.");let ne=process.env[m.encryptionKeyEnv];if(!ne||ne.length<32)throw Error(`${m.encryptionKeyEnv} must contain at least 32 characters.`);Y=`${_}/encryption-key`,await A.secrets.put(Y,ne),z.push(Y)}let X=A.store.createDestination({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,provider:m.provider,endpoint:m.endpoint,endpointPolicy:m.allowPrivate?"allow_private":"public_https",bucket:m.bucket,prefix:m.prefix??"",region:m.region??A.config.project.region,forcePathStyle:!!m.pathStyle,credentialRef:U,encryption:m.encryption,encryptionKeyRef:Y,immutability:{objectLock:!!m.lockDays,defaultRetentionDays:m.lockDays?Number(m.lockDays):void 0},status:"untested"});Ni(A,"destination_created",{destinationId:X.id,provider:X.provider}),n(`Created destination ${X.name} (${X.id}); test it with cloud recovery:destination:test ${X.name}`)}catch(U){for(let Y of z)await A.secrets.remove(Y).catch(()=>{});throw U}}))),D.command("recovery:destination:test <destination>","Write, read, verify, and clean a destination health object").option("--env <environment>","Target environment").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{let _=xu(A,t);if(_.provider==="aws_backup")throw Error("AWS Backup is verified by creating a provider recovery point.");try{await new ii(A.secrets).test(_),A.store.recordDestinationTest(_.id,{ok:!0}),Ni(A,"destination_tested",{destinationId:_.id,ok:!0}),n(`${_.name} passed write, read, checksum, and cleanup checks.`)}catch(z){let U=z instanceof Error?z.message:String(z);throw A.store.recordDestinationTest(_.id,{ok:!1,error:U}),Ni(A,"destination_tested",{destinationId:_.id,ok:!1}),z}}))),D.command("recovery:policy:add <name>","Create a scheduled recovery policy").option("--env <environment>","Target environment").option("--destination <id>","Destination ID or name").option("--kind <kind>","managed_database, logical_database, volume, files, control_plane, or infrastructure").option("--data-service <id>","Data service ID or name").option("--resource <id>","Resource ID or slug").option("--volume <name>","Named Docker volume").option("--include <path>","Project-relative include path or provider setting (repeatable)").option("--exclude <pattern>","Archive exclude pattern (repeatable)").option("--schedule <expression>","Cron, rate, or preset",{default:"daily"}).option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--rpo <minutes>","Recovery point objective",{default:"1440"}).option("--rto <minutes>","Recovery time objective",{default:"120"}).option("--keep <count>","Minimum latest points",{default:"7"}).option("--expire <days>","Expiry days",{default:"30"}).option("--compression <kind>","none, gzip, or zstd",{default:"gzip"}).option("--disabled","Create disabled").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{if(!m.destination)throw Error("--destination is required.");if(!["managed_database","logical_database","volume","files","control_plane","infrastructure"].includes(m.kind??""))throw Error("--kind must be managed_database, logical_database, volume, files, control_plane, or infrastructure.");let _=xu(A,m.destination),z=A.dataServices.list(A.controlPlane.project.id,A.environmentRecord.id),U=m.dataService?A.dataServices.get(m.dataService)??z.find((he)=>he.name===m.dataService):void 0,Y=A.controlPlane.store.listResources(A.controlPlane.project.id,A.environmentRecord.id),X=m.resource?Y.find((he)=>he.id===m.resource||he.slug===m.resource):void 0,ne=m.include?Array.isArray(m.include)?m.include:[m.include]:[],re=m.exclude?Array.isArray(m.exclude)?m.exclude:[m.exclude]:[];if(["managed_database","logical_database"].includes(m.kind??"")&&!U)throw Error("--data-service must identify a service in this environment.");if(m.kind==="volume"&&(!X||!m.volume))throw Error("Volume policies require --resource and --volume.");if(m.kind==="files"&&(!X||!ne.length))throw Error("File policies require --resource and at least one --include path.");if(m.kind==="infrastructure"&&(!ne.some((he)=>he.startsWith("resource:arn:"))||!ne.some((he)=>he.startsWith("role:arn:"))))throw Error("Infrastructure policies require --include resource:<arn> and --include role:<arn>.");let se=A.store.createPolicy({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,environmentId:A.environmentRecord.id,resourceId:X?.id,dataServiceId:U?.id,destinationId:_.id,name:t,resourceKind:m.kind,schedule:m.schedule??"daily",timezone:m.timezone??"UTC",retention:{keepLast:Number(m.keep)||7,expireAfterDays:Number(m.expire)||30},compression:m.compression,encryption:"destination",includePatterns:m.volume?[m.volume,...ne]:ne,excludePatterns:re,expectedRpoMinutes:Number(m.rpo)||1440,expectedRtoMinutes:Number(m.rto)||120,enabled:!m.disabled});Ni(A,"policy_created",{policyId:se.id,resourceKind:se.resourceKind},se.resourceId),n(`Created policy ${se.name} (${se.id}); next run ${se.nextRunAt}`)}))),D.command("recovery:run <policy>","Queue an on-demand run of a recovery policy").option("--env <environment>","Target environment").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{let _=vh(A,t),z=A.coordinator.enqueueBackup(_,new Date().toISOString(),A.actor.id);Ni(A,"run_queued",{policyId:_.id,backupJobId:z.id},_.resourceId),n(`Queued backup job ${z.id}; inspect it with cloud recovery:jobs`)}))),D.command("recovery:verify <point>","Queue independent verification of a recovery point").option("--env <environment>","Target environment").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{let _=Ia(A,t),z=A.coordinator.enqueueVerification(_);Ni(A,"verification_queued",{recoveryPointId:_.id,backupJobId:z.id},_.resourceId),n(`Queued verification job ${z.id}`)}))),D.command("recovery:restore <point> <target>","Validate or queue an isolated, in-place, or drill restore").option("--env <environment>","Target environment").option("--mode <mode>","isolated or in_place",{default:"isolated"}).option("--drill","Validate health and clean the isolated target").option("--execute","Queue the validated restore").option("--confirm <target>","Exact in-place target confirmation").option("--ack-downtime","Acknowledge downtime and data replacement").option("--safety <point>","Distinct verified safety recovery point").option("--metadata <json>","Provider restore metadata JSON").action(async(t,m,A)=>ki(async()=>$i(A.env,async(_)=>{if(!["isolated","in_place"].includes(A.mode??""))throw Error("--mode must be isolated or in_place.");if(A.drill&&A.mode==="in_place")throw Error("Recovery drills require --mode isolated.");let z=Ia(_,t),U={mode:A.mode,target:{...bh(z,m),...A.metadata?{metadata:JSON.parse(A.metadata)}:{},inPlace:A.mode==="in_place"},targetName:m,confirm:A.confirm,recentAuth:!0,downtimeAcknowledged:!!A.ackDowntime,safetyBackupId:A.safety?Ia(_,A.safety).id:void 0},Y=_.coordinator.planRestore(z,U);if(!A.execute){e(JSON.stringify({...Y,productionExecutionCreated:!1},null,2));return}let X=_.coordinator.enqueueRestore(z,{...U,drill:!!A.drill,actorId:_.actor.id});Ni(_,A.drill?"drill_queued":"restore_queued",{recoveryPointId:z.id,backupJobId:X.id,mode:U.mode,target:m},z.resourceId),n(`Queued ${A.drill?"drill":"restore"} job ${X.id}`)}))),D.command("recovery:protect <point>","Pin, hold, or release retention protection").option("--env <environment>","Target environment").option("--pin","Pin the point").option("--unpin","Remove its pin").option("--hold","Place a legal hold").option("--release-hold","Release its legal hold").action(async(t,m)=>ki(async()=>$i(m.env,async(A)=>{let _=Ia(A,t);if(![m.pin,m.unpin,m.hold,m.releaseHold].filter(Boolean).length)throw Error("Choose --pin, --unpin, --hold, or --release-hold.");let z=A.store.updateRecoveryPoint(_.id,{pinned:m.pin?!0:m.unpin?!1:_.pinned,held:m.hold?!0:m.releaseHold?!1:_.held});Ni(A,"protection_updated",{recoveryPointId:_.id,pinned:z.pinned,held:z.held},_.resourceId),n(`${z.id}: pinned=${z.pinned} held=${z.held}`)}))),D.command("recovery:retention","Queue deletion of expired, unlocked, unheld recovery points").option("--env <environment>","Target environment").action(async(t)=>ki(async()=>$i(t.env,async(m)=>{let A=m.coordinator.enqueueRetention();Ni(m,"retention_queued",{jobs:A.length}),n(`Queued ${A.length} retention cleanup job${A.length===1?"":"s"}.`)})))}function Ol(D){D.command("api:list","List all API Gateway APIs").option("--region <region>","AWS region").action(async(t)=>{i("API Gateway APIs");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1",{LambdaClient:_}=await import("./chunk-zjvg5pg8.js"),z=new _(A),U=new s("Fetching APIs...");U.start();let{CloudFormationClient:Y}=await import("./chunk-txykq6f1.js"),re=(await new Y(A).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE"])).StackSummaries.filter((se)=>se.StackName?.includes("api")||se.StackName?.includes("Api")||se.StackName?.includes("API"));if(U.succeed("APIs listed"),e(`
|
|
648
648
|
API-related CloudFormation stacks:`),re.length===0)e("No API Gateway stacks found"),e(`
|
|
649
649
|
To create an API, you can:`),e(" 1. Use cloud.config.ts to define your API"),e(" 2. Deploy with `cloud deploy`");else c(["Stack Name","Status","Created"],re.map((se)=>[se.StackName||"N/A",se.StackStatus||"N/A",se.CreationTime?new Date(se.CreationTime).toLocaleDateString():"N/A"]));e("\nTip: Use AWS Console or `aws apigateway get-rest-apis` for detailed API listing")}catch(m){r(`Failed to list APIs: ${m.message}`),process.exit(1)}}),D.command("api:describe <apiId>","Show API Gateway API details").option("--region <region>","AWS region").action(async(t,m)=>{i(`API: ${t}`);try{let A=await Fe(),_=m.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Region: ${_}`),e(""),e("For detailed API information, use AWS CLI:"),e(` aws apigateway get-rest-api --rest-api-id ${t} --region ${_}`),e(` aws apigateway get-resources --rest-api-id ${t} --region ${_}`),e(` aws apigateway get-stages --rest-api-id ${t} --region ${_}`)}catch(A){r(`Failed to describe API: ${A.message}`),process.exit(1)}}),D.command("api:stages <apiId>","List API stages").option("--region <region>","AWS region").action(async(t,m)=>{i(`API Stages: ${t}`);try{let A=await Fe(),_=m.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Region: ${_}`),e(""),e("Common stages:"),e(" - prod (production)"),e(" - staging"),e(" - dev (development)"),e(""),e("For detailed stage information, use AWS CLI:"),e(` aws apigateway get-stages --rest-api-id ${t} --region ${_}`)}catch(A){r(`Failed to list stages: ${A.message}`),process.exit(1)}}),D.command("api:deploy <apiId>","Deploy API to a stage").option("--region <region>","AWS region",{default:"us-east-1"}).option("--stage <name>","Stage name",{default:"prod"}).option("--description <text>","Deployment description").action(async(t,m)=>{i("Deploy API");try{if(e(`API ID: ${t}`),e(`Stage: ${m.stage}`),!await u(`
|
|
650
650
|
Deploy to this stage?`,!0)){e("Operation cancelled");return}if(e(""),e("To deploy an API Gateway API:"),e(" aws apigateway create-deployment \\"),e(` --rest-api-id ${t} \\`),e(` --stage-name ${m.stage} \\`),e(` --region ${m.region}`),m.description)e(` --description "${m.description}"`)}catch(A){r(`Failed to deploy API: ${A.message}`),process.exit(1)}}),D.command("api:domains","List custom domain names").option("--region <region>","AWS region").action(async(t)=>{i("API Gateway Custom Domains");try{let m=await Fe(),A=t.region||m.project.region||"us-east-1";e(`Region: ${A}`),e(""),e("To list custom domains, use AWS CLI:"),e(` aws apigateway get-domain-names --region ${A}`),e(""),e("To create a custom domain:"),e(" 1. Request or import an SSL certificate in ACM"),e(" 2. Create a custom domain in API Gateway"),e(" 3. Create a base path mapping to your API"),e(" 4. Add a DNS record pointing to the distribution")}catch(m){r(`Failed to list domains: ${m.message}`),process.exit(1)}}),D.command("api:usage <apiId>","Show API usage statistics").option("--region <region>","AWS region").option("--stage <name>","Stage name",{default:"prod"}).option("--days <number>","Number of days to show",{default:"7"}).action(async(t,m)=>{i("API Usage Statistics");try{let A=await Fe(),_=m.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Stage: ${m.stage}`),e(`Period: Last ${m.days} days`),e("");let z=new Date,U=new Date;U.setDate(U.getDate()-Number.parseInt(m.days)),e("To view API metrics in CloudWatch:"),e(" aws cloudwatch get-metric-statistics \\"),e(" --namespace AWS/ApiGateway \\"),e(" --metric-name Count \\"),e(` --dimensions Name=ApiName,Value=${t} Name=Stage,Value=${m.stage} \\`),e(` --start-time ${U.toISOString()} \\`),e(` --end-time ${z.toISOString()} \\`),e(" --period 86400 \\"),e(" --statistics Sum \\"),e(` --region ${_}`),e(""),e("Available metrics:"),e(" - Count: Total API calls"),e(" - Latency: Response latency"),e(" - 4XXError: Client errors"),e(" - 5XXError: Server errors"),e(" - IntegrationLatency: Backend latency")}catch(A){r(`Failed to get usage: ${A.message}`),process.exit(1)}}),D.command("api:export <apiId>","Export API specification").option("--region <region>","AWS region",{default:"us-east-1"}).option("--stage <name>","Stage name",{default:"prod"}).option("--format <format>","Export format (oas30, swagger)",{default:"oas30"}).option("--output <file>","Output file path").action(async(t,m)=>{i("Export API Specification");try{e(`API ID: ${t}`),e(`Stage: ${m.stage}`),e(`Format: ${m.format==="oas30"?"OpenAPI 3.0":"Swagger 2.0"}`),e("");let A=m.format==="oas30"?"oas30":"swagger";e("To export the API specification:"),e(" aws apigateway get-export \\"),e(` --rest-api-id ${t} \\`),e(` --stage-name ${m.stage} \\`),e(` --export-type ${A} \\`),e(" --accepts application/json \\"),e(` --region ${m.region} \\`),e(` ${m.output||"api-spec.json"}`)}catch(A){r(`Failed to export API: ${A.message}`),process.exit(1)}}),D.command("api:logs <apiId>","View API Gateway logs").option("--region <region>","AWS region").option("--stage <name>","Stage name",{default:"prod"}).option("--tail","Tail the logs").action(async(t,m)=>{i("API Gateway Logs");try{let A=await Fe(),_=m.region||A.project.region||"us-east-1",z=`API-Gateway-Execution-Logs_${t}/${m.stage}`;if(e(`Log Group: ${z}`),e(""),e("To view logs:"),e(" aws logs filter-log-events \\"),e(` --log-group-name "${z}" \\`),e(` --region ${_}`),m.tail)e(""),e("For real-time log tailing, use:"),e(` aws logs tail "${z}" --follow --region ${_}`);e(""),e("Note: Ensure logging is enabled for the API stage."),e("You can enable it in the stage settings.")}catch(A){r(`Failed to get logs: ${A.message}`),process.exit(1)}}),D.command("api:test <apiId> <path>","Test an API endpoint").option("--region <region>","AWS region").option("--stage <name>","Stage name",{default:"prod"}).option("--method <method>","HTTP method",{default:"GET"}).option("--body <json>","Request body (JSON)").option("--header <header>","Request header (can be specified multiple times)").action(async(t,m,A)=>{i("Test API Endpoint");try{let _=await Fe(),z=A.region||_.project.region||"us-east-1",U=`https://${t}.execute-api.${z}.amazonaws.com/${A.stage}${m.startsWith("/")?m:`/${m}`}`;e(`URL: ${U}`),e(`Method: ${A.method}`);let Y={"Content-Type":"application/json"};if(A.header){let Pe=Array.isArray(A.header)?A.header:[A.header];for(let ye of Pe){let[Ie,...Le]=ye.split(":");Y[Ie.trim()]=Le.join(":").trim()}}e("Headers:");for(let[Pe,ye]of Object.entries(Y))e(` ${Pe}: ${ye}`);if(A.body)e(`Body: ${A.body}`);if(!await u(`
|