@stacksjs/ts-cloud 0.16.16 → 0.16.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/dist/aws/index.js +1 -1
  2. package/dist/bin/chunk-0h6cggyt.js +1 -0
  3. package/dist/bin/{chunk-tfasfg4a.js → chunk-0trtkv9g.js} +4 -4
  4. package/dist/bin/{chunk-2v91xnqx.js → chunk-148x1zq8.js} +2 -2
  5. package/dist/bin/{chunk-xxf1vhfr.js → chunk-1j8b1d58.js} +1 -1
  6. package/dist/bin/chunk-2g2zrtjs.js +2 -0
  7. package/dist/bin/{chunk-3qfd0vnc.js → chunk-2mv692y4.js} +5 -5
  8. package/dist/bin/{chunk-grs2cwm7.js → chunk-3n733cgx.js} +2 -2
  9. package/dist/bin/chunk-4cs098d1.js +1 -0
  10. package/dist/bin/chunk-52fhq35h.js +1 -0
  11. package/dist/bin/{chunk-j0wfftk1.js → chunk-5a7sxtwn.js} +1 -1
  12. package/dist/bin/{chunk-yk4p2k8w.js → chunk-6kx50dyc.js} +2 -2
  13. package/dist/bin/chunk-6se7ygxa.js +1 -0
  14. package/dist/bin/chunk-72g1hr4z.js +1 -0
  15. package/dist/bin/chunk-75ak501k.js +3 -0
  16. package/dist/{chunk-fdn4wnd8.js → bin/chunk-8jbkphmg.js} +7 -7
  17. package/dist/bin/{chunk-edfh9ts2.js → chunk-90102shh.js} +1 -1
  18. package/dist/bin/{chunk-pd8tk3z5.js → chunk-9vmtm7nq.js} +1 -1
  19. package/dist/bin/chunk-a7kmjq4y.js +1 -0
  20. package/dist/bin/{chunk-whww424p.js → chunk-acqj5mfq.js} +108 -108
  21. package/dist/bin/chunk-c7vxdwth.js +1 -0
  22. package/dist/bin/chunk-ckk70zc9.js +1 -0
  23. package/dist/bin/{chunk-90c3rkd8.js → chunk-cnbvm43k.js} +5 -5
  24. package/dist/bin/chunk-d9yzg97w.js +1 -0
  25. package/dist/bin/{chunk-zacbzz1w.js → chunk-djjj62c0.js} +2 -2
  26. package/dist/bin/chunk-dk9f6h0m.js +1 -0
  27. package/dist/bin/{chunk-1baj8p0r.js → chunk-dmdg23a5.js} +1 -1
  28. package/dist/bin/{chunk-y4c26gaf.js → chunk-dyh0vndw.js} +1 -1
  29. package/dist/bin/chunk-e0c3agf2.js +1 -0
  30. package/dist/bin/{chunk-khta83xb.js → chunk-e24bscb0.js} +15 -15
  31. package/dist/bin/{chunk-bhay1f8a.js → chunk-egn33pr9.js} +1 -1
  32. package/dist/bin/{chunk-3x4p2s2r.js → chunk-eyvxsaaz.js} +3 -3
  33. package/dist/bin/{chunk-b3yp7bak.js → chunk-ezhbcpw1.js} +3 -3
  34. package/dist/bin/{chunk-2943bszc.js → chunk-gfnw9nhq.js} +30 -30
  35. package/dist/bin/{chunk-gv9deppc.js → chunk-h1rb8qvt.js} +2 -2
  36. package/dist/bin/chunk-hagakkgm.js +1 -0
  37. package/dist/bin/chunk-hr3vbg6y.js +1 -0
  38. package/dist/bin/chunk-hyb5v8wr.js +1 -0
  39. package/dist/bin/{chunk-fjz07v7g.js → chunk-k7wh1wr5.js} +1 -1
  40. package/dist/bin/{chunk-cs6a98r1.js → chunk-mx9ppkmn.js} +4 -4
  41. package/dist/bin/{chunk-zzmk290g.js → chunk-mxvekmdx.js} +126 -126
  42. package/dist/bin/chunk-nagvc7e4.js +1 -0
  43. package/dist/bin/{chunk-0znhykt9.js → chunk-ngyqqy6d.js} +5 -5
  44. package/dist/bin/chunk-nqb1faqj.js +1 -0
  45. package/dist/bin/chunk-nrrprf04.js +724 -0
  46. package/dist/bin/chunk-p831sptd.js +1 -0
  47. package/dist/bin/chunk-padk76ac.js +1 -0
  48. package/dist/bin/chunk-qxwkg8yg.js +2 -0
  49. package/dist/bin/chunk-qxx1a51w.js +1 -0
  50. package/dist/bin/{chunk-80295akq.js → chunk-rbtt23vx.js} +3 -3
  51. package/dist/bin/chunk-swfmx4f8.js +1 -0
  52. package/dist/bin/{chunk-dczw0nxw.js → chunk-t10e32an.js} +7 -7
  53. package/dist/bin/{chunk-vx9qk1g1.js → chunk-vjkyj3xy.js} +1 -1
  54. package/dist/bin/chunk-w8k1gyzr.js +1 -0
  55. package/dist/bin/chunk-wagzxm7z.js +1 -0
  56. package/dist/bin/{chunk-n02421y9.js → chunk-x63yz8xg.js} +1 -1
  57. package/dist/bin/chunk-xdxk6baf.js +1 -0
  58. package/dist/bin/{chunk-tvv6vdx6.js → chunk-ys5n5c84.js} +4 -4
  59. package/dist/bin/{chunk-tc24txss.js → chunk-z1dtdw3j.js} +5 -5
  60. package/dist/bin/chunk-zww0xfdp.js +1 -0
  61. package/dist/bin/cli.js +281 -281
  62. package/dist/bin/dashboard-server.js +1 -1
  63. package/dist/{chunk-wdrqt329.js → chunk-2p71e3x7.js} +162 -162
  64. package/dist/{chunk-yxn6tpbg.js → chunk-31tvc877.js} +1 -1
  65. package/dist/{chunk-gstz8w2d.js → chunk-3m9ghthh.js} +2 -2
  66. package/dist/{chunk-4xta1s4z.js → chunk-5far81q7.js} +2 -2
  67. package/dist/{chunk-a6aw2jtz.js → chunk-60nr1ehk.js} +14 -14
  68. package/dist/chunk-6kcmbymp.js +1 -0
  69. package/dist/{chunk-0bbrbzf9.js → chunk-7xnwa0s8.js} +4 -4
  70. package/dist/{chunk-whwg2nzb.js → chunk-9syc8s7r.js} +5 -5
  71. package/dist/{chunk-emx1j2yt.js → chunk-9wmer0zx.js} +1 -1
  72. package/dist/{chunk-4twnkjyq.js → chunk-bxrzsth8.js} +2 -2
  73. package/dist/{chunk-a21phhyj.js → chunk-c3f89ryr.js} +7 -7
  74. package/dist/chunk-dak9hfrd.js +1 -0
  75. package/dist/{chunk-jgv0x24e.js → chunk-e52me1c6.js} +1 -1
  76. package/dist/{chunk-v9t92ak2.js → chunk-e8e1h2h9.js} +2 -2
  77. package/dist/{bin/chunk-dp2fsnpe.js → chunk-g6g33pt9.js} +7 -7
  78. package/dist/chunk-hjgjbkhk.js +1 -0
  79. package/dist/{chunk-6eq5xfcx.js → chunk-k53agv2m.js} +1 -1
  80. package/dist/{chunk-rmn71xjb.js → chunk-kczzc599.js} +25 -25
  81. package/dist/{chunk-g0yyw5k8.js → chunk-ptq9v346.js} +1 -1
  82. package/dist/{chunk-fz70vxrk.js → chunk-qcaqc5b8.js} +2 -2
  83. package/dist/chunk-rtn4gj6y.js +1 -0
  84. package/dist/chunk-s2ve9zxn.js +584 -0
  85. package/dist/{chunk-pekptc1b.js → chunk-srrscggm.js} +5 -5
  86. package/dist/{chunk-jncbm332.js → chunk-v2p7c7s4.js} +325 -325
  87. package/dist/{chunk-gn42mekr.js → chunk-vs0qrm09.js} +1 -1
  88. package/dist/{chunk-rgw06xbk.js → chunk-w9jhyy09.js} +4 -4
  89. package/dist/{chunk-z87dekhm.js → chunk-yd40pg8g.js} +21 -21
  90. package/dist/{chunk-sm5rn3db.js → chunk-z3wynefj.js} +19 -19
  91. package/dist/chunk-z4xzd43p.js +1 -0
  92. package/dist/{chunk-ss4q8kge.js → chunk-zx8ecz8b.js} +4 -4
  93. package/dist/deploy/index.d.ts +1 -0
  94. package/dist/deploy/index.js +1 -1
  95. package/dist/deploy/storage-failover.d.ts +54 -0
  96. package/dist/dns/index.js +1 -1
  97. package/dist/drivers/index.d.ts +2 -0
  98. package/dist/drivers/index.js +1 -1
  99. package/dist/drivers/shared/cloudfront-origin.d.ts +9 -2
  100. package/dist/drivers/shared/deploy-script.d.ts +7 -5
  101. package/dist/drivers/shared/host-cleanup.d.ts +90 -0
  102. package/dist/generators/infrastructure.d.ts +9 -1
  103. package/dist/index.d.ts +2 -2
  104. package/dist/index.js +49 -49
  105. package/dist/operations/index.js +1 -1
  106. package/dist/protection/index.js +1 -1
  107. package/dist/spend/index.js +1 -1
  108. package/dist/ui/404.html +1 -1
  109. package/dist/ui/accept-invitation.html +1 -1
  110. package/dist/ui/access-denied.html +1 -1
  111. package/dist/ui/account/automation.html +6 -6
  112. package/dist/ui/account/security.html +2 -2
  113. package/dist/ui/applications/compose.html +6 -6
  114. package/dist/ui/applications/new.html +2 -2
  115. package/dist/ui/data/backups.html +6 -6
  116. package/dist/ui/data/services.html +6 -6
  117. package/dist/ui/data/volumes.html +6 -6
  118. package/dist/ui/index.html +6 -6
  119. package/dist/ui/infrastructure/topology.html +6 -6
  120. package/dist/ui/integrations.html +1 -1
  121. package/dist/ui/operations/alerts.html +6 -6
  122. package/dist/ui/operations/configuration.html +5 -5
  123. package/dist/ui/operations/jobs.html +6 -6
  124. package/dist/ui/operations/maintenance.html +5 -5
  125. package/dist/ui/operations/observability.html +5 -5
  126. package/dist/ui/operations/previews.html +6 -6
  127. package/dist/ui/operations/queue.html +5 -5
  128. package/dist/ui/operations/regions.html +5 -5
  129. package/dist/ui/operations/releases.html +6 -6
  130. package/dist/ui/operations/spend.html +6 -6
  131. package/dist/ui/operations/workloads.html +6 -6
  132. package/dist/ui/security.html +1 -1
  133. package/dist/ui/server/actions.html +6 -6
  134. package/dist/ui/server/activity.html +1 -1
  135. package/dist/ui/server/capacity.html +5 -5
  136. package/dist/ui/server/database.html +6 -6
  137. package/dist/ui/server/deployments.html +5 -5
  138. package/dist/ui/server/diagnostics.html +2 -2
  139. package/dist/ui/server/firewall.html +6 -6
  140. package/dist/ui/server/fleet.html +6 -6
  141. package/dist/ui/server/logs.html +6 -6
  142. package/dist/ui/server/metrics.html +5 -5
  143. package/dist/ui/server/security.html +2 -2
  144. package/dist/ui/server/services.html +2 -2
  145. package/dist/ui/server/sites.html +6 -6
  146. package/dist/ui/server/ssh-keys.html +6 -6
  147. package/dist/ui/server/team.html +6 -6
  148. package/dist/ui/server/terminal.html +2 -2
  149. package/dist/ui/serverless/alarms.html +6 -6
  150. package/dist/ui/serverless/assets.html +2 -2
  151. package/dist/ui/serverless/cost.html +2 -2
  152. package/dist/ui/serverless/data.html +6 -6
  153. package/dist/ui/serverless/deployments.html +2 -2
  154. package/dist/ui/serverless/firewall.html +2 -2
  155. package/dist/ui/serverless/functions.html +6 -6
  156. package/dist/ui/serverless/logs.html +6 -6
  157. package/dist/ui/serverless/metrics.html +2 -2
  158. package/dist/ui/serverless/queues.html +6 -6
  159. package/dist/ui/serverless/secrets.html +6 -6
  160. package/dist/ui/serverless/traces.html +6 -6
  161. package/dist/ui/serverless.html +5 -5
  162. package/package.json +3 -3
  163. package/dist/bin/chunk-2tn43y0d.js +0 -1
  164. package/dist/bin/chunk-2w591cvg.js +0 -1
  165. package/dist/bin/chunk-4v6f496n.js +0 -1
  166. package/dist/bin/chunk-64cdtvar.js +0 -1
  167. package/dist/bin/chunk-66egshmp.js +0 -1
  168. package/dist/bin/chunk-7raskz63.js +0 -1
  169. package/dist/bin/chunk-9bxyy4th.js +0 -1
  170. package/dist/bin/chunk-bbt0vyzj.js +0 -1
  171. package/dist/bin/chunk-cpcae7e2.js +0 -1
  172. package/dist/bin/chunk-dsc7ef1w.js +0 -1
  173. package/dist/bin/chunk-f2wn46nw.js +0 -1
  174. package/dist/bin/chunk-fddwwac7.js +0 -1
  175. package/dist/bin/chunk-ffcyj2jj.js +0 -1
  176. package/dist/bin/chunk-gbejgj71.js +0 -1
  177. package/dist/bin/chunk-j76935z3.js +0 -3
  178. package/dist/bin/chunk-kjbkwzxd.js +0 -1
  179. package/dist/bin/chunk-knthwdhz.js +0 -1
  180. package/dist/bin/chunk-m2kr333s.js +0 -2
  181. package/dist/bin/chunk-m5wc1ent.js +0 -1
  182. package/dist/bin/chunk-nm000jpj.js +0 -725
  183. package/dist/bin/chunk-pjgbnk3q.js +0 -1
  184. package/dist/bin/chunk-rn0pgnt4.js +0 -1
  185. package/dist/bin/chunk-v93xyx1p.js +0 -1
  186. package/dist/bin/chunk-vx9mmpaz.js +0 -1
  187. package/dist/bin/chunk-xtzy90zh.js +0 -1
  188. package/dist/bin/chunk-z6mnhd0j.js +0 -1
  189. package/dist/chunk-051jwkd2.js +0 -1
  190. package/dist/chunk-1gne7rkf.js +0 -1
  191. package/dist/chunk-38c310s1.js +0 -1
  192. package/dist/chunk-acpxtkfy.js +0 -585
  193. package/dist/chunk-dk3t3hgq.js +0 -1
  194. package/dist/chunk-qc2v4h2q.js +0 -1
package/dist/bin/cli.js CHANGED
@@ -1,39 +1,39 @@
1
1
  #!/usr/bin/env bun
2
- import{T,Mt,n,r,a,hr,e,m,i,o,I,d,ho,c,Yr,he,Ns,Ds,ks,xs,g}from"./chunk-3qfd0vnc.js";import{Us,V,wr,C,U,So,l,s,E,js,Vs,zs,Pe,Le,Ws,vo,To,B,S,_,Ze,D,pe,Eo,Ue,Bt,mi,ve,wo,gi,Ft,Et,Gs,Ks}from"./chunk-zzmk290g.js";import{ke,k,ln,y,dn,Ei,un,$o,Ir,tt,Ie,pn,mn,gn,fn,hn,yn,Sn,bn,te,Ht,Wt,vn,Rr,Tn,En,wn,Cn,An,In,Rn,Pn,rt,Gt,Kt,Ct,Pr,Jt,Xt,wi,At,Nn,se,Dn,kn,xn,Nr,Dr,_n,kr,Qt,Yt,nt,It,Rt,xr,ae,$n,da,ua,Zt,pa,z,Ci,Be,it,ot,On,_r,$r,Ln,Mn,Un,Or,Oo,ce,me,xe,ge,Bn,er}from"./chunk-whww424p.js";import{u,_s}from"./chunk-cs6a98r1.js";import{ie,h,Er}from"./chunk-tfasfg4a.js";import{yr,bt,Sr,br,vr,Tr,Zr}from"./chunk-tc24txss.js";import{b,Nt,Hn,Go,cr,ha,ya,Sa,Wn,Gn,dt,Vr,Dt,zr,ut,ze,$e,qe,pt,lr,Oe,He,Kn,Jn,We,qr,Hr,Ui,Fi,pr,kt,mt,xt,X,qi,Yn,Zn,os}from"./chunk-khta83xb.js";import{Ke,_t,ds,us,Je,Ki,Ji,Xi,Qi,Yi,Zi,q,$t,O,ti}from"./chunk-2v91xnqx.js";import{H,R,io,hs,ys,Va,za,ii,ht,oi}from"./chunk-dczw0nxw.js";import{v}from"./chunk-vx9qk1g1.js";import{K}from"./chunk-edfh9ts2.js";import{St,f}from"./chunk-2943bszc.js";import{Fe,Lo,le,Ai,jn,Ii,Ri,Lr,Pi,Ni,fe,J,je,Re,Mr,tr,Ve,rr,Ur,at,de,ct,ir,or,sr,Br,ar,x,ki,xi,Vn,Mo,Fr,Uo,Bo,ma,_i,Fo,jo,Vo,zo,lt,qo,$i,Oi}from"./chunk-nm000jpj.js";import"./chunk-tvv6vdx6.js";import{P}from"./chunk-yk4p2k8w.js";import{ue}from"./chunk-fjz07v7g.js";import{W,yt,go}from"./chunk-j76935z3.js";import{ne,Q,Ga,Ka,Qe}from"./chunk-3x4p2s2r.js";import{zt,an,qt,vi,cn,_o}from"./chunk-n02421y9.js";import"./chunk-80295akq.js";import{w}from"./chunk-y4c26gaf.js";import{st}from"./chunk-j0wfftk1.js";import{Y,ye}from"./chunk-b3yp7bak.js";import{F}from"./chunk-bhay1f8a.js";import"./chunk-gv9deppc.js";import{Ti}from"./chunk-90c3rkd8.js";import"./chunk-zacbzz1w.js";import{ei}from"./chunk-0znhykt9.js";import{mr}from"./chunk-dp2fsnpe.js";import{gt}from"./chunk-xxf1vhfr.js";import{M}from"./chunk-pd8tk3z5.js";import{existsSync as jd}from"node:fs";import ta from"node:fs/promises";import nl from"node:os";import na from"node:path";import oo from"node:process";import{EventEmitter as Fd}from"node:events";import Vi from"node:process";import rl from"node:process";import oa from"node:process";import vs from"node:process";import Ho from"node:process";import al from"node:tty";import ku,{stdin as kg,stdout as Rg}from"node:process";class cl{configPath;config=null;events=[];retryCount=0;maxRetries=3;retryDelayMs=1000;constructor(){let N=nl.homedir(),t=na.join(N,".config","clapp");this.configPath=na.join(t,"telemetry.json")}async isEnabled(){if(oo.env.DO_NOT_TRACK==="1"||oo.env.DO_NOT_TRACK==="true")return!1;if(oo.env.NO_TELEMETRY==="1"||oo.env.NO_TELEMETRY==="true")return!1;return(await this.loadConfig()).enabled}async enable(){let N=await this.loadConfig();if(N.enabled=!0,!N.userId)N.userId=this.generateUserId();await this.saveConfig(N)}async disable(){let N=await this.loadConfig();N.enabled=!1,await this.saveConfig(N)}async track(N,t){if(!await this.isEnabled())return;let A={event:N,...t,timestamp:Date.now(),platform:nl.platform(),nodeVersion:oo.version};if(this.events.push(A),this.events.length>=10)await this.send()}async trackCommand(N,t){await this.track("command",{command:N,duration:t})}async trackError(N,t){await this.track("error",{error:N,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(N){return new Promise((t)=>setTimeout(t,N))}async status(){let N=await this.loadConfig();return{enabled:N.enabled,doNotTrack:oo.env.DO_NOT_TRACK==="1"||oo.env.DO_NOT_TRACK==="true",eventsQueued:this.events.length,lastSent:N.lastSent}}async loadConfig(){if(this.config)return this.config;try{if(jd(this.configPath)){let N=await ta.readFile(this.configPath,"utf-8");return this.config=JSON.parse(N),this.config}}catch{}return this.config={enabled:!1},this.config}async saveConfig(N){this.config=N;try{let t=na.dirname(this.configPath);await ta.mkdir(t,{recursive:!0}),await ta.writeFile(this.configPath,JSON.stringify(N,null,2),"utf-8")}catch{}}generateUserId(){let N=Math.random().toString(36).substring(2,15),t=Date.now().toString(36);return`${N}-${t}`}}var mg=new cl;function ia(N){return N.replace(/[<[].+/,"").trim()}function Bd(N){let t=/<([^>]+)>/g,p=/\[([^\]]+)\]/g,A=[],L=(Z)=>{let ee=!1,re=Z[1];if(re.startsWith("..."))re=re.slice(3),ee=!0;return{required:Z[0].startsWith("<"),value:re,variadic:ee}},j;while(j=t.exec(N))A.push(L(j));let G;while(G=p.exec(N))A.push(L(G));return A}function Md(N){let t={alias:{},boolean:[]};for(let[p,A]of N.entries()){if(A.names.length>1)t.alias[A.names[0]]=A.names.slice(1);if(A.isBoolean)if(A.negated){if(!N.some((j,G)=>G!==p&&j.names.some((Z)=>A.names.includes(Z))&&typeof j.required==="boolean"))t.boolean.push(A.names[0])}else t.boolean.push(A.names[0])}return t}function il(N){return N.reduce((t,p)=>t.length>=p.length?t:p,"")}function ra(N,t){return N.length>=t?N:`${N}${" ".repeat(t-N.length)}`}function gs(N){return N.replace(/([a-z])-([a-z])/g,(t,p,A)=>p+A.toUpperCase())}function _d(N,t,p){let A=0,L=t.length,j=N,G;for(;A<L;++A)G=j[t[A]],j=j[t[A]]=A===L-1?p:G!=null?G:!!~t[A+1].indexOf(".")||!(+t[A+1]>-1)?{}:[]}function zd(N,t){for(let p of Object.keys(t)){let A=t[p];if(A.shouldTransform){if(N[p]=Array.prototype.concat.call([],N[p]),typeof A.transformFunction==="function")N[p]=N[p].map(A.transformFunction)}}}function Ud(N){let t=/([^\\/]+)$/.exec(N);return t?t[1]:""}function Ko(N){return N.split(".").map((t,p)=>p===0?gs(t):t).join(".")}class Jo extends Error{exitCode=2;isUsageError=!0;usage;constructor(N,t){super(N);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(N).stack}format(N=!1){if(N&&this.stack)return`${this.message}
2
+ import{T,Lt,n,r,a,fr,e,p,i,o,I,d,lo,c,Xr,he,Es,ws,Cs,As,f}from"./chunk-2mv692y4.js";import{zs,V,Cr,C,U,To,l,s,E,Ws,Gs,Ks,Pe,Le,Ut,wo,gi,Co,fi,Ao,Ys,Zs,ea,ta,na,B,S,x,Ze,D,pe,Ro,Ue,Ft,hi,ve,Po,yi,jt,Et,ia,oa}from"./chunk-mxvekmdx.js";import{ke,k,dn,y,un,Ai,pn,Bo,Rr,tt,Ie,mn,gn,fn,hn,yn,Sn,bn,vn,te,Wt,Gt,Tn,Pr,En,wn,Cn,An,In,Rn,Pn,Nn,rt,Kt,Jt,Ct,Nr,Xt,Qt,Ii,At,Dn,se,kn,_n,xn,Dr,kr,$n,_r,Yt,Zt,nt,It,Rt,xr,ae,On,Ta,Ea,er,wa,z,Ri,Be,it,ot,Ln,$r,Or,Mn,Un,Bn,Lr,Fo,ce,me,_e,ge,jn,tr}from"./chunk-acqj5mfq.js";import{u,Us}from"./chunk-mx9ppkmn.js";import{ie,h,wr}from"./chunk-0trtkv9g.js";import{Sr,bt,br,vr,Tr,Er,en}from"./chunk-z1dtdw3j.js";import{b,Nt,Gn,Yo,lr,Da,ka,_a,Kn,Jn,dt,Hr,Dt,qr,ut,He,$e,qe,pt,dr,Oe,We,Xn,Qn,Ge,Wr,Gr,ji,zi,mr,kt,mt,_t,Q,Gi,ei,ti,ds}from"./chunk-e24bscb0.js";import{Je,xt,fs,hs,Xe,Qi,Yi,Zi,eo,to,ro,H,$t,O,ni}from"./chunk-148x1zq8.js";import{W,P,uo,Ns,Ds,rc,nc,si,yt,ai}from"./chunk-t10e32an.js";import{v}from"./chunk-vjkyj3xy.js";import{K}from"./chunk-90102shh.js";import{St,g}from"./chunk-gfnw9nhq.js";import{je,jo,le,Pi,zn,Ni,Di,Mr,ki,_i,fe,X,Ve,Re,Ur,rr,ze,nr,Br,at,de,ct,or,sr,ar,Fr,cr,_,$i,Oi,Hn,Vo,jr,zo,Ho,Ia,Li,qo,Wo,Go,Ko,lt,Jo,Mi,Ui}from"./chunk-nrrprf04.js";import"./chunk-ys5n5c84.js";import{R}from"./chunk-6kx50dyc.js";import{ue}from"./chunk-k7wh1wr5.js";import{q,ft,ao}from"./chunk-75ak501k.js";import{re,J,Ca,Aa,Fe}from"./chunk-eyvxsaaz.js";import{Ht,cn,qt,wi,ln,Uo}from"./chunk-x63yz8xg.js";import"./chunk-rbtt23vx.js";import{w}from"./chunk-dyh0vndw.js";import{st}from"./chunk-5a7sxtwn.js";import{Y,ye}from"./chunk-ezhbcpw1.js";import{F}from"./chunk-egn33pr9.js";import"./chunk-h1rb8qvt.js";import{Ci}from"./chunk-cnbvm43k.js";import"./chunk-qxwkg8yg.js";import"./chunk-djjj62c0.js";import{ri}from"./chunk-ngyqqy6d.js";import{gr}from"./chunk-8jbkphmg.js";import{gt}from"./chunk-1j8b1d58.js";import{M}from"./chunk-9vmtm7nq.js";import{existsSync as Hd}from"node:fs";import ua from"node:fs/promises";import pl from"node:os";import pa from"node:path";import po from"node:process";import{EventEmitter as Kd}from"node:events";import Ji from"node:process";import ml from"node:process";import ga from"node:process";import $s from"node:process";import Zo from"node:process";import yl from"node:tty";import ju,{stdin as jg,stdout as Fg}from"node:process";class vl{configPath;config=null;events=[];retryCount=0;maxRetries=3;retryDelayMs=1000;constructor(){let N=pl.homedir(),t=pa.join(N,".config","clapp");this.configPath=pa.join(t,"telemetry.json")}async isEnabled(){if(po.env.DO_NOT_TRACK==="1"||po.env.DO_NOT_TRACK==="true")return!1;if(po.env.NO_TELEMETRY==="1"||po.env.NO_TELEMETRY==="true")return!1;return(await this.loadConfig()).enabled}async enable(){let N=await this.loadConfig();if(N.enabled=!0,!N.userId)N.userId=this.generateUserId();await this.saveConfig(N)}async disable(){let N=await this.loadConfig();N.enabled=!1,await this.saveConfig(N)}async track(N,t){if(!await this.isEnabled())return;let A={event:N,...t,timestamp:Date.now(),platform:pl.platform(),nodeVersion:po.version};if(this.events.push(A),this.events.length>=10)await this.send()}async trackCommand(N,t){await this.track("command",{command:N,duration:t})}async trackError(N,t){await this.track("error",{error:N,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(N){return new Promise((t)=>setTimeout(t,N))}async status(){let N=await this.loadConfig();return{enabled:N.enabled,doNotTrack:po.env.DO_NOT_TRACK==="1"||po.env.DO_NOT_TRACK==="true",eventsQueued:this.events.length,lastSent:N.lastSent}}async loadConfig(){if(this.config)return this.config;try{if(Hd(this.configPath)){let N=await ua.readFile(this.configPath,"utf-8");return this.config=JSON.parse(N),this.config}}catch{}return this.config={enabled:!1},this.config}async saveConfig(N){this.config=N;try{let t=pa.dirname(this.configPath);await ua.mkdir(t,{recursive:!0}),await ua.writeFile(this.configPath,JSON.stringify(N,null,2),"utf-8")}catch{}}generateUserId(){let N=Math.random().toString(36).substring(2,15),t=Date.now().toString(36);return`${N}-${t}`}}var Cg=new vl;function fa(N){return N.replace(/[<[].+/,"").trim()}function Jd(N){let t=/<([^>]+)>/g,m=/\[([^\]]+)\]/g,A=[],L=(Z)=>{let ee=!1,ne=Z[1];if(ne.startsWith("..."))ne=ne.slice(3),ee=!0;return{required:Z[0].startsWith("<"),value:ne,variadic:ee}},j;while(j=t.exec(N))A.push(L(j));let G;while(G=m.exec(N))A.push(L(G));return A}function Yd(N){let t={alias:{},boolean:[]};for(let[m,A]of N.entries()){if(A.names.length>1)t.alias[A.names[0]]=A.names.slice(1);if(A.isBoolean)if(A.negated){if(!N.some((j,G)=>G!==m&&j.names.some((Z)=>A.names.includes(Z))&&typeof j.required==="boolean"))t.boolean.push(A.names[0])}else t.boolean.push(A.names[0])}return t}function fl(N){return N.reduce((t,m)=>t.length>=m.length?t:m,"")}function ma(N,t){return N.length>=t?N:`${N}${" ".repeat(t-N.length)}`}function Ss(N){return N.replace(/([a-z])-([a-z])/g,(t,m,A)=>m+A.toUpperCase())}function Zd(N,t,m){let A=0,L=t.length,j=N,G;for(;A<L;++A)G=j[t[A]],j=j[t[A]]=A===L-1?m:G!=null?G:!!~t[A+1].indexOf(".")||!(+t[A+1]>-1)?{}:[]}function Qd(N,t){for(let m of Object.keys(t)){let A=t[m];if(A.shouldTransform){if(N[m]=Array.prototype.concat.call([],N[m]),typeof A.transformFunction==="function")N[m]=N[m].map(A.transformFunction)}}}function Xd(N){let t=/([^\\/]+)$/.exec(N);return t?t[1]:""}function Qo(N){return N.split(".").map((t,m)=>m===0?Ss(t):t).join(".")}class Xo extends Error{exitCode=2;isUsageError=!0;usage;constructor(N,t){super(N);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(N).stack}format(N=!1){if(N&&this.stack)return`${this.message}
3
3
 
4
4
  Stack trace:
5
- ${this.stack}`;return this.message}}function Vd(N){if(N instanceof Jo)return!0;return!!N&&typeof N==="object"&&N.name==="ClappError"&&typeof N.message==="string"}function Wd(){let{env:N}=rl,{TERM:t,TERM_PROGRAM:p}=N;if(rl.platform!=="win32")return t!=="linux";return Boolean(N.WT_SESSION)||Boolean(N.TERMINUS_SUBLIME)||N.ConEmuTask==="{cmd::Cmder}"||p==="Terminus-Sublime"||p==="vscode"||t==="xterm-256color"||t==="alacritty"||t==="rxvt-unicode"||t==="rxvt-unicode-256color"||N.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var bs="\x1B",zn=`${bs}[`;var ol={to(N,t){if(!t)return`${zn}${N+1}G`;return`${zn}${t+1};${N+1}H`},move(N,t){let p="";if(N<0)p+=`${zn}${-N}D`;else if(N>0)p+=`${zn}${N}C`;if(t<0)p+=`${zn}${-t}A`;else if(t>0)p+=`${zn}${t}B`;return p},up:(N=1)=>`${zn}${N}A`,down:(N=1)=>`${zn}${N}B`,forward:(N=1)=>`${zn}${N}C`,backward:(N=1)=>`${zn}${N}D`,nextLine:(N=1)=>`${zn}E`.repeat(N),prevLine:(N=1)=>`${zn}F`.repeat(N),left:`${zn}G`,hide:`${zn}?25l`,show:`${zn}?25h`,save:`${bs}7`,restore:`${bs}8`};var hg={screen:`${zn}2J`,up:(N=1)=>`${zn}1J`.repeat(N),down:(N=1)=>`${zn}J`.repeat(N),line:`${zn}2K`,lineEnd:`${zn}K`,lineStart:`${zn}1K`,lines(N){let t="";for(let p=0;p<N;p++)t+=this.line+(p<N-1?ol.up():"");if(N)t+=ol.left;return t}},yg={screen:`${bs}c`};function qd(N,t){let p=[];for(let A=0;A<=t.length;A++)p[A]=[A];for(let A=0;A<=N.length;A++)p[0][A]=A;for(let A=1;A<=t.length;A++)for(let L=1;L<=N.length;L++)if(t.charAt(A-1)===N.charAt(L-1))p[A][L]=p[A-1][L-1];else p[A][L]=Math.min(p[A-1][L-1]+1,p[A][L-1]+1,p[A-1][L]+1);return p[t.length][N.length]}function ll(N,t,p=2,A=3){return t.map((L)=>({cmd:L,distance:qd(N,L)})).filter(({distance:L})=>L<=p).sort((L,j)=>L.distance-j.distance).slice(0,A).map(({cmd:L})=>L)}function Gd(N,t={}){let p={_:[]},A=t.alias||{},L=new Set(t.boolean||[]),j={};for(let ee of Object.keys(A))for(let re of A[ee])j[re]=ee;for(let ee of L)if(A[ee])for(let re of A[ee])L.add(re);function G(ee,re){if(ee===void 0)return re;if(typeof re==="boolean")return re;return Array.isArray(ee)?[...ee,re]:[ee,re]}function Z(ee,re){let oe=j[ee]||ee,be=G(p[oe],re);if(p[oe]=be,A[oe])for(let we of A[oe])p[we]=be;if(j[ee]&&A[j[ee]])for(let we of A[j[ee]])p[we]=be;p[ee]=be}for(let ee=0;ee<N.length;ee++){let re=N[ee];if(re==="--"){p._.push(...N.slice(ee+1));break}if(re.startsWith("--")){let oe=re.indexOf("=");if(oe!==-1){let be=gs(re.slice(2,oe)),we=re.slice(oe+1);Z(be,we)}else{let be=re.slice(2);if(be.startsWith("no-")){let Ee=gs(be.slice(3));Z(Ee,!1);continue}let we=gs(be),Ce=j[we]||we;if(L.has(Ce)||L.has(we))Z(we,!0);else{let Ee=N[ee+1];if(Ee!==void 0&&!Ee.startsWith("-"))Z(we,Ee),ee++;else Z(we,!0)}}}else if(re.startsWith("-")&&re.length>1){let oe=re.slice(1);for(let be=0;be<oe.length;be++){let we=oe[be],Ce=j[we]||we;if(be===oe.length-1&&!L.has(Ce)&&!L.has(we)){let Ee=N[ee+1];if(Ee!==void 0&&!Ee.startsWith("-"))Z(we,Ee),ee++;else Z(we,!0)}else Z(we,!0)}}else p._.push(re)}return p}class dl{rawName;description;name;names;isBoolean;required;config;negated;constructor(N,t,p){if(this.rawName=N,this.description=t,this.config=Object.assign({},p),N=N.replace(/\.\*/g,""),this.negated=!1,this.names=ia(N).split(",").map((A)=>{let L=A.trim().replace(/^-{1,2}/,"");if(L.startsWith("no-"))this.negated=!0,L=L.replace(/^no-/,"");return Ko(L)}).sort((A,L)=>A.length>L.length?1:-1),this.name=this.names[this.names.length-1],this.negated&&this.config.default==null)this.config.default=!0;if(N.includes("<"))this.required=!0;else if(N.includes("["))this.required=!1;else this.isBoolean=!0}}var bg=oa.argv,Hd=`${oa.platform}-${oa.arch} bun-v${typeof Bun<"u"?Bun.version:"unknown"}`,sl=vs.argv,Kd=`${vs.platform}-${vs.arch} node-${vs.version}`;class sa{rawName;description;config;cli;options;aliasNames;name;namespace;args;commandAction;usageText;versionNumber;examples;helpCallback;globalCommand;beforeHooks;afterHooks;middleware;constructor(N,t,p,A){this.rawName=N,this.description=t,this.config=p,this.cli=A,this.options=[],this.aliasNames=[],this.name=ia(N);let L=ia(N),j=L.indexOf(":");if(j>0)this.namespace=L.substring(0,j),this.name=L.substring(j+1);if(this.args=Bd(N),this.examples=[],this.beforeHooks=[],this.afterHooks=[],this.middleware=[],!p)this.config={}}usage(N){return this.usageText=N,this}allowUnknownOptions(){return this.config.allowUnknownOptions=!0,this}ignoreOptionDefaultValue(){return this.config.ignoreOptionDefaultValue=!0,this}version(N,t="-v, --version"){return this.versionNumber=N,this.option(t,"Display version number"),this}example(N){return this.examples.push(N),this}option(N,t,p){let A=new dl(N,t,p);return this.options.push(A),this}alias(N){return this.aliasNames.push(N),this}action(N){return this.commandAction=N,this}before(N){return this.beforeHooks.push(N),this}after(N){return this.afterHooks.push(N),this}use(N){return this.middleware.push(N),this}isMatched(N){if(this.aliasNames.includes(N))return!0;if(this.namespace)return`${this.namespace}:${this.name}`===N;return this.name===N}get isDefaultCommand(){return this.name===""||this.aliasNames.includes("!")}get isGlobalCommand(){return this instanceof aa}get displayName(){return this.namespace?`${this.namespace}:${this.name}`:this.name}hasOption(N){let t=Ko(N.split(".")[0]);return!!this.options.find((p)=>p.names.some((A)=>Ko(A)===t))}outputHelp(){let{name:N,commands:t}=this.cli,{versionNumber:p,options:A,helpCallback:L}=this.cli.globalCommand,j=[{body:`${N}${p?`/${p}`:""}`}];if(j.push({title:"Usage",body:` $ ${N} ${this.usageText||this.rawName}`}),(this.isGlobalCommand||this.isDefaultCommand)&&t.length>0){let re=il(t.map((Ee)=>Ee.rawName)),oe=new Map,be=[];for(let Ee of t)if(Ee.namespace){if(!oe.has(Ee.namespace))oe.set(Ee.namespace,[]);oe.get(Ee.namespace).push(Ee)}else be.push(Ee);let we="";if(be.sort((Ee,Se)=>Ee.rawName<Se.rawName?-1:Ee.rawName>Se.rawName?1:0),be.length>0)we+=be.map((Ee)=>` ${ra(Ee.rawName,re.length)} ${Ee.description}`).join(`
6
- `);let Ce=Array.from(oe.keys()).sort();for(let Ee of Ce){let Se=oe.get(Ee);if(we.length>0)we+=`
5
+ ${this.stack}`;return this.message}}function eu(N){if(N instanceof Xo)return!0;return!!N&&typeof N==="object"&&N.name==="ClappError"&&typeof N.message==="string"}function tu(){let{env:N}=ml,{TERM:t,TERM_PROGRAM:m}=N;if(ml.platform!=="win32")return t!=="linux";return Boolean(N.WT_SESSION)||Boolean(N.TERMINUS_SUBLIME)||N.ConEmuTask==="{cmd::Cmder}"||m==="Terminus-Sublime"||m==="vscode"||t==="xterm-256color"||t==="alacritty"||t==="rxvt-unicode"||t==="rxvt-unicode-256color"||N.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var ks="\x1B",Wn=`${ks}[`;var gl={to(N,t){if(!t)return`${Wn}${N+1}G`;return`${Wn}${t+1};${N+1}H`},move(N,t){let m="";if(N<0)m+=`${Wn}${-N}D`;else if(N>0)m+=`${Wn}${N}C`;if(t<0)m+=`${Wn}${-t}A`;else if(t>0)m+=`${Wn}${t}B`;return m},up:(N=1)=>`${Wn}${N}A`,down:(N=1)=>`${Wn}${N}B`,forward:(N=1)=>`${Wn}${N}C`,backward:(N=1)=>`${Wn}${N}D`,nextLine:(N=1)=>`${Wn}E`.repeat(N),prevLine:(N=1)=>`${Wn}F`.repeat(N),left:`${Wn}G`,hide:`${Wn}?25l`,show:`${Wn}?25h`,save:`${ks}7`,restore:`${ks}8`};var Ig={screen:`${Wn}2J`,up:(N=1)=>`${Wn}1J`.repeat(N),down:(N=1)=>`${Wn}J`.repeat(N),line:`${Wn}2K`,lineEnd:`${Wn}K`,lineStart:`${Wn}1K`,lines(N){let t="";for(let m=0;m<N;m++)t+=this.line+(m<N-1?gl.up():"");if(N)t+=gl.left;return t}},Pg={screen:`${ks}c`};function nu(N,t){let m=[];for(let A=0;A<=t.length;A++)m[A]=[A];for(let A=0;A<=N.length;A++)m[0][A]=A;for(let A=1;A<=t.length;A++)for(let L=1;L<=N.length;L++)if(t.charAt(A-1)===N.charAt(L-1))m[A][L]=m[A-1][L-1];else m[A][L]=Math.min(m[A-1][L-1]+1,m[A][L-1]+1,m[A-1][L]+1);return m[t.length][N.length]}function bl(N,t,m=2,A=3){return t.map((L)=>({cmd:L,distance:nu(N,L)})).filter(({distance:L})=>L<=m).sort((L,j)=>L.distance-j.distance).slice(0,A).map(({cmd:L})=>L)}function ru(N,t={}){let m={_:[]},A=t.alias||{},L=new Set(t.boolean||[]),j={};for(let ee of Object.keys(A))for(let ne of A[ee])j[ne]=ee;for(let ee of L)if(A[ee])for(let ne of A[ee])L.add(ne);function G(ee,ne){if(ee===void 0)return ne;if(typeof ne==="boolean")return ne;return Array.isArray(ee)?[...ee,ne]:[ee,ne]}function Z(ee,ne){let oe=j[ee]||ee,be=G(m[oe],ne);if(m[oe]=be,A[oe])for(let we of A[oe])m[we]=be;if(j[ee]&&A[j[ee]])for(let we of A[j[ee]])m[we]=be;m[ee]=be}for(let ee=0;ee<N.length;ee++){let ne=N[ee];if(ne==="--"){m._.push(...N.slice(ee+1));break}if(ne.startsWith("--")){let oe=ne.indexOf("=");if(oe!==-1){let be=Ss(ne.slice(2,oe)),we=ne.slice(oe+1);Z(be,we)}else{let be=ne.slice(2);if(be.startsWith("no-")){let De=Ss(be.slice(3));Z(De,!1);continue}let we=Ss(be),Ce=j[we]||we;if(L.has(Ce)||L.has(we))Z(we,!0);else{let De=N[ee+1];if(De!==void 0&&!De.startsWith("-"))Z(we,De),ee++;else Z(we,!0)}}}else if(ne.startsWith("-")&&ne.length>1){let oe=ne.slice(1);for(let be=0;be<oe.length;be++){let we=oe[be],Ce=j[we]||we;if(be===oe.length-1&&!L.has(Ce)&&!L.has(we)){let De=N[ee+1];if(De!==void 0&&!De.startsWith("-"))Z(we,De),ee++;else Z(we,!0)}else Z(we,!0)}}else m._.push(ne)}return m}class wl{rawName;description;name;names;isBoolean;required;config;negated;constructor(N,t,m){if(this.rawName=N,this.description=t,this.config=Object.assign({},m),N=N.replace(/\.\*/g,""),this.negated=!1,this.names=fa(N).split(",").map((A)=>{let L=A.trim().replace(/^-{1,2}/,"");if(L.startsWith("no-"))this.negated=!0,L=L.replace(/^no-/,"");return Qo(L)}).sort((A,L)=>A.length>L.length?1:-1),this.name=this.names[this.names.length-1],this.negated&&this.config.default==null)this.config.default=!0;if(N.includes("<"))this.required=!0;else if(N.includes("["))this.required=!1;else this.isBoolean=!0}}var Dg=ga.argv,iu=`${ga.platform}-${ga.arch} bun-v${typeof Bun<"u"?Bun.version:"unknown"}`,hl=$s.argv,ou=`${$s.platform}-${$s.arch} node-${$s.version}`;class ha{rawName;description;config;cli;options;aliasNames;name;namespace;args;commandAction;usageText;versionNumber;examples;helpCallback;globalCommand;beforeHooks;afterHooks;middleware;constructor(N,t,m,A){this.rawName=N,this.description=t,this.config=m,this.cli=A,this.options=[],this.aliasNames=[],this.name=fa(N);let L=fa(N),j=L.indexOf(":");if(j>0)this.namespace=L.substring(0,j),this.name=L.substring(j+1);if(this.args=Jd(N),this.examples=[],this.beforeHooks=[],this.afterHooks=[],this.middleware=[],!m)this.config={}}usage(N){return this.usageText=N,this}allowUnknownOptions(){return this.config.allowUnknownOptions=!0,this}ignoreOptionDefaultValue(){return this.config.ignoreOptionDefaultValue=!0,this}version(N,t="-v, --version"){return this.versionNumber=N,this.option(t,"Display version number"),this}example(N){return this.examples.push(N),this}option(N,t,m){let A=new wl(N,t,m);return this.options.push(A),this}alias(N){return this.aliasNames.push(N),this}action(N){return this.commandAction=N,this}before(N){return this.beforeHooks.push(N),this}after(N){return this.afterHooks.push(N),this}use(N){return this.middleware.push(N),this}isMatched(N){if(this.aliasNames.includes(N))return!0;if(this.namespace)return`${this.namespace}:${this.name}`===N;return this.name===N}get isDefaultCommand(){return this.name===""||this.aliasNames.includes("!")}get isGlobalCommand(){return this instanceof ya}get displayName(){return this.namespace?`${this.namespace}:${this.name}`:this.name}hasOption(N){let t=Qo(N.split(".")[0]);return!!this.options.find((m)=>m.names.some((A)=>Qo(A)===t))}outputHelp(){let{name:N,commands:t}=this.cli,{versionNumber:m,options:A,helpCallback:L}=this.cli.globalCommand,j=[{body:`${N}${m?`/${m}`:""}`}];if(j.push({title:"Usage",body:` $ ${N} ${this.usageText||this.rawName}`}),(this.isGlobalCommand||this.isDefaultCommand)&&t.length>0){let ne=fl(t.map((De)=>De.rawName)),oe=new Map,be=[];for(let De of t)if(De.namespace){if(!oe.has(De.namespace))oe.set(De.namespace,[]);oe.get(De.namespace).push(De)}else be.push(De);let we="";if(be.sort((De,Se)=>De.rawName<Se.rawName?-1:De.rawName>Se.rawName?1:0),be.length>0)we+=be.map((De)=>` ${ma(De.rawName,ne.length)} ${De.description}`).join(`
6
+ `);let Ce=Array.from(oe.keys()).sort();for(let De of Ce){let Se=oe.get(De);if(we.length>0)we+=`
7
7
 
8
- `;we+=` ${Ee}:
9
- `,we+=Se.map((De)=>` ${ra(De.rawName,re.length-2)} ${De.description}`).join(`
10
- `)}j.push({title:"Commands",body:we}),j.push({body:`Run \`${N} <command> --help\` for command details.`})}let Z=new Set(this.options.flatMap((re)=>re.names)),ee=this.isGlobalCommand?A:[...this.options,...(A||[]).filter((re)=>!re.names.some((oe)=>Z.has(oe)))];if(!this.isGlobalCommand&&!this.isDefaultCommand)ee=ee.filter((re)=>re.name!=="version");if(ee.length>0){let re=il(ee.map((oe)=>oe.rawName));j.push({title:"Options",body:ee.map((oe)=>` ${ra(oe.rawName,re.length)} ${oe.description} ${oe.config.default===void 0?"":`(default: ${oe.config.default})`}`).join(`
11
- `)})}if(this.examples.length>0)j.push({title:"Examples",body:this.examples.map((re)=>{if(typeof re==="function")return re(N);return re}).join(`
12
- `)});if(L)j=L(j)||j;console.log(j.map((re)=>re.title?`${re.title}:
13
- ${re.body}`:re.body).join(`
8
+ `;we+=` ${De}:
9
+ `,we+=Se.map((xe)=>` ${ma(xe.rawName,ne.length-2)} ${xe.description}`).join(`
10
+ `)}j.push({title:"Commands",body:we}),j.push({body:`Run \`${N} <command> --help\` for command details.`})}let Z=new Set(this.options.flatMap((ne)=>ne.names)),ee=this.isGlobalCommand?A:[...this.options,...(A||[]).filter((ne)=>!ne.names.some((oe)=>Z.has(oe)))];if(!this.isGlobalCommand&&!this.isDefaultCommand)ee=ee.filter((ne)=>ne.name!=="version");if(ee.length>0){let ne=fl(ee.map((oe)=>oe.rawName));j.push({title:"Options",body:ee.map((oe)=>` ${ma(oe.rawName,ne.length)} ${oe.description} ${oe.config.default===void 0?"":`(default: ${oe.config.default})`}`).join(`
11
+ `)})}if(this.examples.length>0)j.push({title:"Examples",body:this.examples.map((ne)=>{if(typeof ne==="function")return ne(N);return ne}).join(`
12
+ `)});if(L)j=L(j)||j;console.log(j.map((ne)=>ne.title?`${ne.title}:
13
+ ${ne.body}`:ne.body).join(`
14
14
 
15
- `))}outputVersion(){let{name:N}=this.cli,{versionNumber:t}=this.cli.globalCommand;if(t)console.log(`${N}/${t} ${typeof Bun<"u"?Hd:Kd}`)}get usageLine(){return`$ ${this.cli.name} ${this.usageText||this.rawName}`}checkRequiredArgs(){let N=this.args.filter((t)=>t.required).length;if(this.cli.args.length<N){let p=this.args.filter((L)=>L.required).slice(this.cli.args.length),A=p.map((L)=>`<${L.value}>`).join(" ");throw new Jo(`Missing required argument${p.length>1?"s":""}: ${A}
15
+ `))}outputVersion(){let{name:N}=this.cli,{versionNumber:t}=this.cli.globalCommand;if(t)console.log(`${N}/${t} ${typeof Bun<"u"?iu:ou}`)}get usageLine(){return`$ ${this.cli.name} ${this.usageText||this.rawName}`}checkRequiredArgs(){let N=this.args.filter((t)=>t.required).length;if(this.cli.args.length<N){let m=this.args.filter((L)=>L.required).slice(this.cli.args.length),A=m.map((L)=>`<${L.value}>`).join(" ");throw new Xo(`Missing required argument${m.length>1?"s":""}: ${A}
16
16
 
17
- Run \`${this.cli.name} ${this.rawName} --help\` for usage information.`,this.usageLine)}}checkUnknownOptions(){let{options:N,globalCommand:t}=this.cli;if(!this.config.allowUnknownOptions){for(let p of Object.keys(N))if(p!=="--"&&!this.hasOption(p)&&!t.hasOption(p)){let L=[...t.options,...this.options].flatMap((re)=>re.names),j=Ko(p.split(".")[0]),G=p.length>1?`--${p}`:`-${p}`,Z=ll(j,L),ee=`Unknown option \`${G}\``;if(Z.length>0)ee+=`
17
+ Run \`${this.cli.name} ${this.rawName} --help\` for usage information.`,this.usageLine)}}checkUnknownOptions(){let{options:N,globalCommand:t}=this.cli;if(!this.config.allowUnknownOptions){for(let m of Object.keys(N))if(m!=="--"&&!this.hasOption(m)&&!t.hasOption(m)){let L=[...t.options,...this.options].flatMap((ne)=>ne.names),j=Qo(m.split(".")[0]),G=m.length>1?`--${m}`:`-${m}`,Z=bl(j,L),ee=`Unknown option \`${G}\``;if(Z.length>0)ee+=`
18
18
 
19
- Did you mean one of these?`,Z.forEach((re)=>{let oe=re.length>1?`--${re}`:`-${re}`;ee+=`
19
+ Did you mean one of these?`,Z.forEach((ne)=>{let oe=ne.length>1?`--${ne}`:`-${ne}`;ee+=`
20
20
  • ${oe}`});throw ee+=`
21
21
 
22
- Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`,new Jo(ee,this.usageLine)}}}checkOptionValue(){let{options:N,globalCommand:t}=this.cli,p=[...t.options,...this.options];for(let A of p){let L=N[A.name.split(".")[0]];if(A.required){let j=p.some((G)=>G.negated&&G.names.includes(A.name));if(L===!0||L===!1&&!j)throw new Jo(`Option \`${A.rawName}\` requires a value.
22
+ Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`,new Xo(ee,this.usageLine)}}}checkOptionValue(){let{options:N,globalCommand:t}=this.cli,m=[...t.options,...this.options];for(let A of m){let L=N[A.name.split(".")[0]];if(A.required){let j=m.some((G)=>G.negated&&G.names.includes(A.name));if(L===!0||L===!1&&!j)throw new Xo(`Option \`${A.rawName}\` requires a value.
23
23
 
24
- Example: ${this.cli.name} ${this.rawName} ${A.rawName} <value>`,this.usageLine)}}}}class aa extends sa{constructor(N){super("@@global@@","",{},N)}}var Jd=sa,Wi={red:["\x1B[31m","\x1B[39m"],green:["\x1B[32m","\x1B[39m"],blue:["\x1B[34m","\x1B[39m"],yellow:["\x1B[33m","\x1B[39m"],cyan:["\x1B[36m","\x1B[39m"],magenta:["\x1B[35m","\x1B[39m"],white:["\x1B[37m","\x1B[39m"],gray:["\x1B[90m","\x1B[39m"],bgRed:["\x1B[41m","\x1B[49m"],bgGreen:["\x1B[42m","\x1B[49m"],bgBlue:["\x1B[44m","\x1B[49m"],bgYellow:["\x1B[43m","\x1B[49m"],bgCyan:["\x1B[46m","\x1B[49m"],bgMagenta:["\x1B[45m","\x1B[49m"],bold:["\x1B[1m","\x1B[22m"],italic:["\x1B[3m","\x1B[23m"],underline:["\x1B[4m","\x1B[24m"],dim:["\x1B[2m","\x1B[22m"],inverse:["\x1B[7m","\x1B[27m"],hidden:["\x1B[8m","\x1B[28m"],strikethrough:["\x1B[9m","\x1B[29m"]};var No={primary:"blue",secondary:"cyan",success:"green",warning:"yellow",error:"red",info:"magenta",muted:"gray"};function Yd(){return!0}function Zd(){let N={};N.supportsColor=Yd();function t(A,L=[]){let j=A===""?[]:[...L,A],G=function(ee){if(!N.supportsColor)return ee;let re="",oe="";for(let be of j)if(be in No&&No[be]in Wi){let we=No[be];re+=Wi[we][0],oe=Wi[we][1]+oe}else if(be in Wi)re+=Wi[be][0],oe=Wi[be][1]+oe;return re+ee+oe},Z=[...Object.keys(Wi),...Object.keys(No)];for(let ee of Z)if(!(ee in G))Object.defineProperty(G,ee,{get(){return t(ee,j)}});return G}let p=[...Object.keys(Wi),...Object.keys(No)];for(let A of p)if(!(A in N))Object.defineProperty(N,A,{get(){return t(A)}});return N}var Wo=Zd();class ca extends Fd{name;commands;globalCommand;matchedCommand;matchedCommandName;rawArgs;args;options;showHelpOnExit;showVersionOnExit;enableDidYouMean=!0;signalHandlersSet=!1;isVerbose=!1;isQuiet=!1;isDebug=!1;isNoInteraction=!1;environment;isDryRun=!1;isForce=!1;useEmoji=!0;theme;isNoCache=!1;constructor(N=""){super();this.name=N,this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.globalCommand=new aa(this),this.globalCommand.usage("<command> [options]")}handleSignals(N){if(this.signalHandlersSet)return this;let t=async(p)=>{if(console.log(`
24
+ Example: ${this.cli.name} ${this.rawName} ${A.rawName} <value>`,this.usageLine)}}}}class ya extends ha{constructor(N){super("@@global@@","",{},N)}}var su=ha,Xi={red:["\x1B[31m","\x1B[39m"],green:["\x1B[32m","\x1B[39m"],blue:["\x1B[34m","\x1B[39m"],yellow:["\x1B[33m","\x1B[39m"],cyan:["\x1B[36m","\x1B[39m"],magenta:["\x1B[35m","\x1B[39m"],white:["\x1B[37m","\x1B[39m"],gray:["\x1B[90m","\x1B[39m"],bgRed:["\x1B[41m","\x1B[49m"],bgGreen:["\x1B[42m","\x1B[49m"],bgBlue:["\x1B[44m","\x1B[49m"],bgYellow:["\x1B[43m","\x1B[49m"],bgCyan:["\x1B[46m","\x1B[49m"],bgMagenta:["\x1B[45m","\x1B[49m"],bold:["\x1B[1m","\x1B[22m"],italic:["\x1B[3m","\x1B[23m"],underline:["\x1B[4m","\x1B[24m"],dim:["\x1B[2m","\x1B[22m"],inverse:["\x1B[7m","\x1B[27m"],hidden:["\x1B[8m","\x1B[28m"],strikethrough:["\x1B[9m","\x1B[29m"]};var Mo={primary:"blue",secondary:"cyan",success:"green",warning:"yellow",error:"red",info:"magenta",muted:"gray"};function au(){return!0}function cu(){let N={};N.supportsColor=au();function t(A,L=[]){let j=A===""?[]:[...L,A],G=function(ee){if(!N.supportsColor)return ee;let ne="",oe="";for(let be of j)if(be in Mo&&Mo[be]in Xi){let we=Mo[be];ne+=Xi[we][0],oe=Xi[we][1]+oe}else if(be in Xi)ne+=Xi[be][0],oe=Xi[be][1]+oe;return ne+ee+oe},Z=[...Object.keys(Xi),...Object.keys(Mo)];for(let ee of Z)if(!(ee in G))Object.defineProperty(G,ee,{get(){return t(ee,j)}});return G}let m=[...Object.keys(Xi),...Object.keys(Mo)];for(let A of m)if(!(A in N))Object.defineProperty(N,A,{get(){return t(A)}});return N}var _o=cu();class va extends Kd{name;commands;globalCommand;matchedCommand;matchedCommandName;rawArgs;args;options;showHelpOnExit;showVersionOnExit;enableDidYouMean=!0;signalHandlersSet=!1;isVerbose=!1;isQuiet=!1;isDebug=!1;isNoInteraction=!1;environment;isDryRun=!1;isForce=!1;useEmoji=!0;theme;isNoCache=!1;constructor(N=""){super();this.name=N,this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.globalCommand=new ya(this),this.globalCommand.usage("<command> [options]")}handleSignals(N){if(this.signalHandlersSet)return this;let t=async(m)=>{if(console.log(`
25
25
 
26
- Received ${p}, cleaning up...`),N)try{await N()}catch(A){console.error("Error during cleanup:",A)}Vi.exit(0)};return Vi.on("SIGINT",()=>t("SIGINT")),Vi.on("SIGTERM",()=>t("SIGTERM")),this.signalHandlersSet=!0,this}didYouMean(N=!0){return this.enableDidYouMean=N,this}verbose(){return this.globalCommand.option("-v, --verbose","Enable verbose output"),this}quiet(){return this.globalCommand.option("-q, --quiet","Suppress non-essential output"),this}debug(){return this.globalCommand.option("--debug","Enable debug mode with detailed error information"),this}noInteraction(){return this.globalCommand.option("-n, --no-interaction","Do not ask any interactive questions (for CI/CD)"),this}env(){return this.globalCommand.option("--env <environment>","Target environment (e.g., production, staging, local)"),this}dryRun(){return this.globalCommand.option("--dry-run","Preview actions without executing them"),this}force(){return this.globalCommand.option("-f, --force","Skip confirmation prompts"),this}emoji(){return this.globalCommand.option("--no-emoji","Disable emoji in output"),this}themes(){return this.globalCommand.option("--theme <theme>","Color theme (default, dracula, nord, solarized, monokai)"),this}cache(){return this.globalCommand.option("--no-cache","Disable caching"),this}usage(N){return this.globalCommand.usage(N),this}command(N,t,p){if(!p)p={};let A=new Jd(N,t||"",p,this);return A.globalCommand=this.globalCommand,this.commands.push(A),A}option(N,t,p){return this.globalCommand.option(N,t,p),this}help(N){return this.globalCommand.option("-h, --help","Display this message"),this.globalCommand.helpCallback=N,this.showHelpOnExit=!0,this}version(N,t="-v, --version"){return this.globalCommand.version(N,t),this.showVersionOnExit=!0,this}example(N){return this.globalCommand.example(N),this}outputHelp(){if(this.matchedCommand)this.matchedCommand.outputHelp();else this.globalCommand.outputHelp()}outputVersion(){this.globalCommand.outputVersion()}setParsedInfo({args:N,options:t},p,A){if(this.args=N,this.options=t,p)this.matchedCommand=p;if(A)this.matchedCommandName=A;return this}unsetMatchedCommand(){this.matchedCommand=void 0,this.matchedCommandName=void 0}showCommandNotFound(N){if(console.log(Wo.red(`
26
+ Received ${m}, cleaning up...`),N)try{await N()}catch(A){console.error("Error during cleanup:",A)}Ji.exit(0)};return Ji.on("SIGINT",()=>t("SIGINT")),Ji.on("SIGTERM",()=>t("SIGTERM")),this.signalHandlersSet=!0,this}didYouMean(N=!0){return this.enableDidYouMean=N,this}verbose(){return this.globalCommand.option("-v, --verbose","Enable verbose output"),this}quiet(){return this.globalCommand.option("-q, --quiet","Suppress non-essential output"),this}debug(){return this.globalCommand.option("--debug","Enable debug mode with detailed error information"),this}noInteraction(){return this.globalCommand.option("-n, --no-interaction","Do not ask any interactive questions (for CI/CD)"),this}env(){return this.globalCommand.option("--env <environment>","Target environment (e.g., production, staging, local)"),this}dryRun(){return this.globalCommand.option("--dry-run","Preview actions without executing them"),this}force(){return this.globalCommand.option("-f, --force","Skip confirmation prompts"),this}emoji(){return this.globalCommand.option("--no-emoji","Disable emoji in output"),this}themes(){return this.globalCommand.option("--theme <theme>","Color theme (default, dracula, nord, solarized, monokai)"),this}cache(){return this.globalCommand.option("--no-cache","Disable caching"),this}usage(N){return this.globalCommand.usage(N),this}command(N,t,m){if(!m)m={};let A=new su(N,t||"",m,this);return A.globalCommand=this.globalCommand,this.commands.push(A),A}option(N,t,m){return this.globalCommand.option(N,t,m),this}help(N){return this.globalCommand.option("-h, --help","Display this message"),this.globalCommand.helpCallback=N,this.showHelpOnExit=!0,this}version(N,t="-v, --version"){return this.globalCommand.version(N,t),this.showVersionOnExit=!0,this}example(N){return this.globalCommand.example(N),this}outputHelp(){if(this.matchedCommand)this.matchedCommand.outputHelp();else this.globalCommand.outputHelp()}outputVersion(){this.globalCommand.outputVersion()}setParsedInfo({args:N,options:t},m,A){if(this.args=N,this.options=t,m)this.matchedCommand=m;if(A)this.matchedCommandName=A;return this}unsetMatchedCommand(){this.matchedCommand=void 0,this.matchedCommandName=void 0}showCommandNotFound(N){if(console.log(_o.red(`
27
27
  ✗ Command "${N}" not found.
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 p=ll(N,t);if(p.length>0)console.log(Wo.yellow("Did you mean one of these?")),p.forEach((A)=>console.log(` ${Wo.dim("•")} ${this.name} ${A}`)),console.log("")}console.log(Wo.dim("Run"),`${this.name} --help`,Wo.dim("to see all available commands")),Vi.exit(1)}async parse(N=sl,t={}){let{run:p=!0,exitOnError:A=!1}=t;if(A)try{return await this.parse(N,{run:p})}catch(ee){throw this.handleUsageError(ee),ee}if(this.rawArgs=N,!this.name)this.name=N[1]?Ud(N[1]):"cli";let L=!0,j=N.slice(2),G=j[0];if(G&&!G.startsWith("-")){for(let ee of this.commands)if(ee.isMatched(G)){let re=this.mri(j,ee);L=!1;let oe={...re,args:re.args.slice(1)};this.setParsedInfo(oe,ee,G),this.emit(`command:${G}`,ee);break}if(L){let ee=j[1];if(ee&&!ee.startsWith("-")){let re=`${G}:${ee}`;for(let oe of this.commands)if(oe.isMatched(re)){let be=this.mri(j,oe);L=!1;let we={...be,args:be.args.slice(2)};this.setParsedInfo(we,oe,re),this.emit(`command:${re}`,oe);break}}}}if(L){for(let ee of this.commands)if(ee.name===""){L=!1;let re=this.mri(j,ee);this.setParsedInfo(re,ee),this.emit("command:!",ee);break}}if(L){let ee=this.mri(N.slice(2));this.setParsedInfo(ee)}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(),p=!1,this.unsetMatchedCommand();if(this.options.version&&this.showVersionOnExit&&this.matchedCommandName==null)this.outputVersion(),p=!1,this.unsetMatchedCommand();let Z={args:this.args,options:this.options};if(p)await this.runMatchedCommand();if(!this.matchedCommand&&this.args[0]){if(this.emit("command:*"),!(this.listenerCount("command:*")>0))this.showCommandNotFound(this.args[0])}return Z}mri(N,t){let p=[...this.globalCommand.options,...t?t.options:[]],A=Md(p),L=[],j=N.indexOf("--");if(j>-1)L=N.slice(j+1),N=N.slice(0,j);let G=Gd(N,A),Z={_:G._};for(let we of Object.keys(G))if(we!=="_")Z[Ko(we)]=G[we];let ee=Z._,re={"--":L},oe=t&&t.config.ignoreOptionDefaultValue?t.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,be=Object.create(null);for(let we of p){if(!oe&&we.config.default!==void 0)for(let Ce of we.names)re[Ce]=we.config.default;if(Array.isArray(we.config.type)){if(be[we.name]===void 0)be[we.name]={shouldTransform:!0,transformFunction:we.config.type[0]}}}for(let we of Object.keys(Z))if(we!=="_"){let Ce=we.split(".");_d(re,Ce,Z[we]),zd(re,be)}return{args:ee,options:re}}async run(N=sl){return this.parse(N,{run:!0,exitOnError:!0})}handleUsageError(N){if(!Vd(N)||N.isUsageError===!1)return;let t=N.message||"command-line error",p=this.name?`${this.name}: `:"",A=/--help/.test(t)?"":N.usage?`
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=bl(N,t);if(m.length>0)console.log(_o.yellow("Did you mean one of these?")),m.forEach((A)=>console.log(` ${_o.dim("•")} ${this.name} ${A}`)),console.log("")}console.log(_o.dim("Run"),`${this.name} --help`,_o.dim("to see all available commands")),Ji.exit(1)}async parse(N=hl,t={}){let{run:m=!0,exitOnError:A=!1}=t;if(A)try{return await this.parse(N,{run:m})}catch(ee){throw this.handleUsageError(ee),ee}if(this.rawArgs=N,!this.name)this.name=N[1]?Xd(N[1]):"cli";let L=!0,j=N.slice(2),G=j[0];if(G&&!G.startsWith("-")){for(let ee of this.commands)if(ee.isMatched(G)){let ne=this.mri(j,ee);L=!1;let oe={...ne,args:ne.args.slice(1)};this.setParsedInfo(oe,ee,G),this.emit(`command:${G}`,ee);break}if(L){let ee=j[1];if(ee&&!ee.startsWith("-")){let ne=`${G}:${ee}`;for(let oe of this.commands)if(oe.isMatched(ne)){let be=this.mri(j,oe);L=!1;let we={...be,args:be.args.slice(2)};this.setParsedInfo(we,oe,ne),this.emit(`command:${ne}`,oe);break}}}}if(L){for(let ee of this.commands)if(ee.name===""){L=!1;let ne=this.mri(j,ee);this.setParsedInfo(ne,ee),this.emit("command:!",ee);break}}if(L){let ee=this.mri(N.slice(2));this.setParsedInfo(ee)}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 Z={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 Z}mri(N,t){let m=[...this.globalCommand.options,...t?t.options:[]],A=Yd(m),L=[],j=N.indexOf("--");if(j>-1)L=N.slice(j+1),N=N.slice(0,j);let G=ru(N,A),Z={_:G._};for(let we of Object.keys(G))if(we!=="_")Z[Qo(we)]=G[we];let ee=Z._,ne={"--":L},oe=t&&t.config.ignoreOptionDefaultValue?t.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,be=Object.create(null);for(let we of m){if(!oe&&we.config.default!==void 0)for(let Ce of we.names)ne[Ce]=we.config.default;if(Array.isArray(we.config.type)){if(be[we.name]===void 0)be[we.name]={shouldTransform:!0,transformFunction:we.config.type[0]}}}for(let we of Object.keys(Z))if(we!=="_"){let Ce=we.split(".");Zd(ne,Ce,Z[we]),Qd(ne,be)}return{args:ee,options:ne}}async run(N=hl){return this.parse(N,{run:!0,exitOnError:!0})}handleUsageError(N){if(!eu(N)||N.isUsageError===!1)return;let t=N.message||"command-line error",m=this.name?`${this.name}: `:"",A=/--help/.test(t)?"":N.usage?`
29
29
  Usage: ${N.usage}`:`
30
- Run \`${this.name??"cli"} --help\` for usage.`;Vi.stderr.write(`${p}${t}${A}
31
- `),Vi.exit(N.exitCode??2)}async runMatchedCommand(){let{args:N,options:t,matchedCommand:p}=this;if(!p||!p.commandAction)return;p.checkUnknownOptions(),p.checkOptionValue(),p.checkRequiredArgs();let A=[];p.args.forEach((Z,ee)=>{if(Z.variadic)A.push(N.slice(ee));else A.push(N[ee])}),A.push(t);let L={command:p,args:A,options:t};for(let Z of p.beforeHooks)await Z(L);let j,G=async()=>{let Z=p.commandAction.apply(this,A);if(Z instanceof Promise)j=await Z;else j=Z;return j};if(p.middleware.length>0){let Z=0,ee=async()=>{if(Z<p.middleware.length){let re=p.middleware[Z++];await re({...L,next:ee})}else await G()};await ee()}else await G();for(let Z of p.afterHooks)await Z(L);return j}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return Vi.removeAllListeners("SIGINT"),Vi.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 ul{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(N){if(!this.enabled){this.misses++;return}let t=this.cache.get(N);if(!t){this.misses++;return}if(Date.now()-t.timestamp>t.ttl){this.cache.delete(N),this.misses++;return}return this.hits++,t.value}set(N,t,p=5000){if(!this.enabled)return;this.cache.set(N,{value:t,timestamp:Date.now(),ttl:p})}has(N){if(!this.enabled)return!1;let t=this.cache.get(N);if(!t)return!1;if(Date.now()-t.timestamp>t.ttl)return this.cache.delete(N),!1;return!0}delete(N){this.cache.delete(N)}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 N=Date.now();for(let[t,p]of this.cache.entries())if(N-p.timestamp>p.ttl)this.cache.delete(t)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var Sg=new ul;function Qd(){if("FORCE_COLOR"in Ho.env)return Ho.env.FORCE_COLOR!=="0";if("NO_COLOR"in Ho.env||Ho.env.TERM==="dumb")return!1;if(Ho.platform==="win32")return!0;return al.isatty(1)&&al.isatty(2)}var ml=Qd();function Fn(N,t){if(!ml)return(p)=>p;return(p)=>N+p+t}var Xd=Fn("\x1B[0m","\x1B[0m"),eu=Fn("\x1B[31m","\x1B[39m"),tu=Fn("\x1B[32m","\x1B[39m"),nu=Fn("\x1B[33m","\x1B[39m"),ru=Fn("\x1B[34m","\x1B[39m"),iu=Fn("\x1B[35m","\x1B[39m"),ou=Fn("\x1B[36m","\x1B[39m"),su=Fn("\x1B[37m","\x1B[39m"),au=Fn("\x1B[90m","\x1B[39m"),cu=Fn("\x1B[1m","\x1B[22m"),lu=Fn("\x1B[3m","\x1B[23m"),du=Fn("\x1B[4m","\x1B[24m"),uu=Fn("\x1B[2m","\x1B[22m"),mu=Fn("\x1B[7m","\x1B[27m"),pu=Fn("\x1B[8m","\x1B[28m"),fu=Fn("\x1B[9m","\x1B[29m"),gu=Fn("\x1B[41m","\x1B[49m"),hu=Fn("\x1B[42m","\x1B[49m"),yu=Fn("\x1B[43m","\x1B[49m"),vu=Fn("\x1B[44m","\x1B[49m"),bu=Fn("\x1B[45m","\x1B[49m"),wu=Fn("\x1B[46m","\x1B[49m"),Su=Fn("\x1B[47m","\x1B[49m"),$u=ml,Cu={reset:Xd,red:eu,green:tu,yellow:nu,blue:ru,magenta:iu,cyan:ou,white:su,gray:au,bold:cu,italic:lu,underline:du,dim:uu,inverse:mu,hidden:pu,strikethrough:fu,bgRed:gu,bgGreen:hu,bgYellow:yu,bgBlue:vu,bgMagenta:bu,bgCyan:wu,bgWhite:Su,isColorSupported:$u},Au=Cu,Ru=["up","down","left","right","space","enter","cancel"],Ig={actions:new Set(Ru),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};var Pg=ku.platform.startsWith("win"),xg=Symbol("clapp:cancel");var Iu=Wd();var on=(N,t)=>Iu?N:t,Dg=on("◆","*"),Eg=on("■","x"),Tg=on("▲","x"),Ng=on("◇","o"),Og=on("┌","T"),Pu=on("│","|"),Lg=on("└","—"),jg=on("●",">"),Fg=on("○"," "),Bg=on("◻","[•]"),Mg=on("◼","[+]"),_g=on("◻","[ ]"),zg=on("▪","•"),Ug=on("─","-"),Vg=on("╮","+"),Wg=on("├","+"),qg=on("╯","+"),Gg=on("●","•"),Hg=on("◆","*"),Kg=on("▲","!"),Jg=on("■","x");var Yg={light:on("─","-"),heavy:on("━","="),block:on("█","#")};function xu(){return`${Au.gray(Pu)} `}var Zg=xu();var la="0.16.16";import{existsSync as fa}from"node:fs";import{mkdir as Eu,writeFile as pl}from"node:fs/promises";function ga(N){N.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"),fa("cloud.config.ts")){if(!await d("cloud.config.ts already exists. Overwrite?",!1)){e("Initialization cancelled");return}}let p=t?.name||await I("Project name","my-app"),A=t?.mode||await ho("Select deployment mode",["serverless","server"]),L=t?.region||await ho("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),j=new o("Creating configuration file...");j.start();let G=`import { defineConfig } from '@ts-cloud/core'
30
+ Run \`${this.name??"cli"} --help\` for usage.`;Ji.stderr.write(`${m}${t}${A}
31
+ `),Ji.exit(N.exitCode??2)}async runMatchedCommand(){let{args:N,options:t,matchedCommand:m}=this;if(!m||!m.commandAction)return;m.checkUnknownOptions(),m.checkOptionValue(),m.checkRequiredArgs();let A=[];m.args.forEach((Z,ee)=>{if(Z.variadic)A.push(N.slice(ee));else A.push(N[ee])}),A.push(t);let L={command:m,args:A,options:t};for(let Z of m.beforeHooks)await Z(L);let j,G=async()=>{let Z=m.commandAction.apply(this,A);if(Z instanceof Promise)j=await Z;else j=Z;return j};if(m.middleware.length>0){let Z=0,ee=async()=>{if(Z<m.middleware.length){let ne=m.middleware[Z++];await ne({...L,next:ee})}else await G()};await ee()}else await G();for(let Z of m.afterHooks)await Z(L);return j}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return Ji.removeAllListeners("SIGINT"),Ji.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 Sl{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(N){if(!this.enabled){this.misses++;return}let t=this.cache.get(N);if(!t){this.misses++;return}if(Date.now()-t.timestamp>t.ttl){this.cache.delete(N),this.misses++;return}return this.hits++,t.value}set(N,t,m=5000){if(!this.enabled)return;this.cache.set(N,{value:t,timestamp:Date.now(),ttl:m})}has(N){if(!this.enabled)return!1;let t=this.cache.get(N);if(!t)return!1;if(Date.now()-t.timestamp>t.ttl)return this.cache.delete(N),!1;return!0}delete(N){this.cache.delete(N)}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 N=Date.now();for(let[t,m]of this.cache.entries())if(N-m.timestamp>m.ttl)this.cache.delete(t)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var Tg=new Sl;function lu(){if("FORCE_COLOR"in Zo.env)return Zo.env.FORCE_COLOR!=="0";if("NO_COLOR"in Zo.env||Zo.env.TERM==="dumb")return!1;if(Zo.platform==="win32")return!0;return yl.isatty(1)&&yl.isatty(2)}var $l=lu();function Vn(N,t){if(!$l)return(m)=>m;return(m)=>N+m+t}var du=Vn("\x1B[0m","\x1B[0m"),uu=Vn("\x1B[31m","\x1B[39m"),pu=Vn("\x1B[32m","\x1B[39m"),mu=Vn("\x1B[33m","\x1B[39m"),fu=Vn("\x1B[34m","\x1B[39m"),gu=Vn("\x1B[35m","\x1B[39m"),hu=Vn("\x1B[36m","\x1B[39m"),yu=Vn("\x1B[37m","\x1B[39m"),vu=Vn("\x1B[90m","\x1B[39m"),bu=Vn("\x1B[1m","\x1B[22m"),wu=Vn("\x1B[3m","\x1B[23m"),Su=Vn("\x1B[4m","\x1B[24m"),$u=Vn("\x1B[2m","\x1B[22m"),Cu=Vn("\x1B[7m","\x1B[27m"),Au=Vn("\x1B[8m","\x1B[28m"),ku=Vn("\x1B[9m","\x1B[29m"),Ru=Vn("\x1B[41m","\x1B[49m"),Iu=Vn("\x1B[42m","\x1B[49m"),Pu=Vn("\x1B[43m","\x1B[49m"),xu=Vn("\x1B[44m","\x1B[49m"),Du=Vn("\x1B[45m","\x1B[49m"),Eu=Vn("\x1B[46m","\x1B[49m"),Tu=Vn("\x1B[47m","\x1B[49m"),Nu=$l,Ou={reset:du,red:uu,green:pu,yellow:mu,blue:fu,magenta:gu,cyan:hu,white:yu,gray:vu,bold:bu,italic:wu,underline:Su,dim:$u,inverse:Cu,hidden:Au,strikethrough:ku,bgRed:Ru,bgGreen:Iu,bgYellow:Pu,bgBlue:xu,bgMagenta:Du,bgCyan:Eu,bgWhite:Tu,isColorSupported:Nu},Lu=Ou,Fu=["up","down","left","right","space","enter","cancel"],Bg={actions:new Set(Fu),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};var Mg=ju.platform.startsWith("win"),_g=Symbol("clapp:cancel");var Bu=tu();var Fn=(N,t)=>Bu?N:t,zg=Fn("◆","*"),Ug=Fn("■","x"),Vg=Fn("▲","x"),Wg=Fn("◇","o"),qg=Fn("┌","T"),Mu=Fn("│","|"),Gg=Fn("└","—"),Hg=Fn("●",">"),Kg=Fn("○"," "),Jg=Fn("◻","[•]"),Yg=Fn("◼","[+]"),Zg=Fn("◻","[ ]"),Qg=Fn("▪","•"),Xg=Fn("─","-"),eh=Fn("╮","+"),th=Fn("├","+"),nh=Fn("╯","+"),rh=Fn("●","•"),ih=Fn("◆","*"),oh=Fn("▲","!"),sh=Fn("■","x");var ah={light:Fn("─","-"),heavy:Fn("━","="),block:Fn("█","#")};function _u(){return`${Lu.gray(Mu)} `}var ch=_u();var ba="0.16.18";import{existsSync as Sa}from"node:fs";import{mkdir as Uu,writeFile as Cl}from"node:fs/promises";function $a(N){N.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"),Sa("cloud.config.ts")){if(!await d("cloud.config.ts already exists. Overwrite?",!1)){e("Initialization cancelled");return}}let m=t?.name||await I("Project name","my-app"),A=t?.mode||await lo("Select deployment mode",["serverless","server"]),L=t?.region||await lo("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),j=new o("Creating configuration file...");j.start();let G=`import { defineConfig } from '@ts-cloud/core'
32
32
 
33
33
  export default defineConfig({
34
34
  project: {
35
- name: '${p}',
36
- slug: '${p.toLowerCase().replace(/[^a-z0-9-]/g,"-")}',
35
+ name: '${m}',
36
+ slug: '${m.toLowerCase().replace(/[^a-z0-9-]/g,"-")}',
37
37
  region: '${L}',
38
38
  },
39
39
  mode: '${A}',
@@ -52,39 +52,39 @@ export default defineConfig({
52
52
  // Add your infrastructure configuration here
53
53
  },
54
54
  })
55
- `;if(await pl("cloud.config.ts",G),j.succeed("Created cloud.config.ts"),!fa(".gitignore"))await pl(".gitignore",`.env
55
+ `;if(await Cl("cloud.config.ts",G),j.succeed("Created cloud.config.ts"),!Sa(".gitignore"))await Cl(".gitignore",`.env
56
56
  .env.*
57
57
  node_modules/
58
58
  dist/
59
59
  cloudformation/
60
60
  *.log
61
61
  .DS_Store
62
- `),n("Created .gitignore");if(!fa("cloudformation"))await Eu("cloudformation",{recursive:!0}),n("Created cloudformation/ directory");he(`ts-cloud project initialized!
62
+ `),n("Created .gitignore");if(!Sa("cloudformation"))await Uu("cloudformation",{recursive:!0}),n("Created cloudformation/ directory");he(`ts-cloud project initialized!
63
63
 
64
64
  Next steps:
65
65
  1. Edit cloud.config.ts to configure your infrastructure
66
66
  2. Run 'cloud generate' to create CloudFormation templates
67
- 3. Run 'cloud deploy' to deploy your infrastructure`,"green")}),N.command("init:server","Initialize server-based (EC2) project").action(async()=>{i("Initializing Server-Based Project"),await N.parse(["init","--mode","server"])}),N.command("init:serverless","Initialize serverless (Fargate/Lambda) project").action(async()=>{i("Initializing Serverless Project"),await N.parse(["init","--mode","serverless"])}),N.command("init:hybrid","Initialize hybrid project").action(async()=>{i("Initializing Hybrid Project"),await N.parse(["init","--mode","hybrid"])})}import{readFile as fl,writeFile as Tu}from"node:fs/promises";import{resolve as ws}from"node:path";async function Me(){let N=await Ei();if(!N.project)throw Error("Missing required project configuration in cloud.config.ts");return N}function Xn(N){if(N)switch(N.toLowerCase()){case"porkbun":{let A=process.env.PORKBUN_API_KEY,L=process.env.PORKBUN_SECRET_KEY;if(!A||!L)throw Error("PORKBUN_API_KEY and PORKBUN_SECRET_KEY environment variables are required for Porkbun provider");return{provider:"porkbun",apiKey:A,secretKey:L}}case"godaddy":{let A=process.env.GODADDY_API_KEY,L=process.env.GODADDY_API_SECRET;if(!A||!L)throw Error("GODADDY_API_KEY and GODADDY_API_SECRET environment variables are required for GoDaddy provider");let j=process.env.GODADDY_ENVIRONMENT||"production";return{provider:"godaddy",apiKey:A,apiSecret:L,environment:j}}case"route53":{let A=process.env.AWS_REGION||"us-east-1",L=process.env.AWS_HOSTED_ZONE_ID;return{provider:"route53",region:A,hostedZoneId:L}}case"cloudflare":{let A=process.env.CLOUDFLARE_API_TOKEN;if(!A)throw Error("CLOUDFLARE_API_TOKEN environment variable is required for Cloudflare provider");return{provider:"cloudflare",apiToken:A,zoneId:process.env.CLOUDFLARE_ZONE_ID,accountId:process.env.CLOUDFLARE_ACCOUNT_ID}}default:throw Error(`Unknown DNS provider: ${N}. Supported: porkbun, godaddy, route53, cloudflare`)}if(new ti().loadFromEnv().getAllProviders().length===0)return null;if(process.env.PORKBUN_API_KEY&&process.env.PORKBUN_SECRET_KEY)return{provider:"porkbun",apiKey:process.env.PORKBUN_API_KEY,secretKey:process.env.PORKBUN_SECRET_KEY};if(process.env.GODADDY_API_KEY&&process.env.GODADDY_API_SECRET)return{provider:"godaddy",apiKey:process.env.GODADDY_API_KEY,apiSecret:process.env.GODADDY_API_SECRET,environment:process.env.GODADDY_ENVIRONMENT||"production"};if(process.env.AWS_ACCESS_KEY_ID||process.env.AWS_REGION)return{provider:"route53",region:process.env.AWS_REGION||"us-east-1",hostedZoneId:process.env.AWS_HOSTED_ZONE_ID};if(process.env.CLOUDFLARE_API_TOKEN)return{provider:"cloudflare",apiToken:process.env.CLOUDFLARE_API_TOKEN,zoneId:process.env.CLOUDFLARE_ZONE_ID,accountId:process.env.CLOUDFLARE_ACCOUNT_ID};return null}function pi(N){let t=Xn(N);if(!t)throw Error("No DNS provider configured. Set environment variables for Porkbun (PORKBUN_API_KEY, PORKBUN_SECRET_KEY), GoDaddy (GODADDY_API_KEY, GODADDY_API_SECRET), Cloudflare (CLOUDFLARE_API_TOKEN), or Route53 (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)");return O(t)}async function Nu(N){let t=await Me(),p=y(process.cwd(),t),A=N??Object.keys(t.environments??{})[0]??"production",L=p.environments.get(A);if(!L)throw p.store.close(),Error(`Environment ${A} was not found.`);let j=t.project?.region??"us-east-1",G=k(process.cwd()),Z=new yr(p.store),ee=new Tr(Z,{encryptionKey:G,backends:[new bt(p.store,G),new Sr(new W(j),j),new br(new Fe(j),j),new vr]}),re=p.store.getActorByExternalId("system","cli")??p.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return await Zr(ee,p,t,re.id),{config:t,controlPlane:p,env:A,environmentRecord:L,store:Z,service:ee,actor:re}}async function fi(N,t){let p=await Nu(N);try{return await t(p)}finally{p.controlPlane.store.close()}}async function Cr(N){try{await N()}catch(t){r(t instanceof Error?t.message:String(t))}}function Ou(N){return N.map((t)=>[t.key,t.kind,t.scope.type,t.inherited?"inherited":t.overridden?"override":"direct",t.kind==="secret"?t.backend??"restricted":t.value??"",t.backendVersion??"—",t.required?"required":"optional",t.updatedAt])}function gl(N,t=process.env){if(N.value!=null)throw Error("Secret values are not accepted in command arguments; use --from-env or the interactive prompt.");if(!N.fromEnv)return;let p=t[N.fromEnv];if(!p)throw Error(`${N.fromEnv} is empty or unavailable.`);return p}function Qr(N,t){let p=t.scope??"environment";if(p==="project")return{type:p,id:N.controlPlane.project.id};if(p==="environment"){let A=t.target?N.controlPlane.store.listEnvironments(N.controlPlane.project.id).find((L)=>L.id===t.target||L.slug===t.target):N.environmentRecord;if(!A)throw Error(`Environment ${t.target} was not found.`);return{type:p,id:A.id,environmentId:A.id}}if(p==="service"){let A=N.controlPlane.store.listResources(N.controlPlane.project.id),L=A.find((j)=>j.id===t.target||j.slug===t.target);if(!L)throw Error("Service scope requires --target with a resource ID or slug.");return{type:p,id:L.id,environmentId:L.environmentId,resourceId:L.id}}if(p==="function"){let A=N.controlPlane.store.listResources(N.controlPlane.project.id),L=A.find((j)=>j.kind==="function"&&(j.id===t.target||j.slug===t.target));if(!L)throw Error("Function scope requires --target with a function resource ID or slug.");return{type:p,id:L.id,environmentId:L.environmentId,resourceId:L.id}}if(p==="preview"){let A=new $e(N.controlPlane.store).listInstances({projectId:N.controlPlane.project.id}).find((L)=>L.id===t.target||L.name===t.target);if(!A)throw Error("Preview scope requires --target with a preview ID or name.");return{type:p,id:A.id,environmentId:A.baseEnvironmentId,resourceId:A.resourceId,previewId:A.id}}throw Error("--scope must be project, environment, service, function, or preview.")}function va(N){let t=N??"local_encrypted";if(!["local_encrypted","aws_secrets_manager","aws_ssm","external"].includes(t))throw Error("--backend must be local_encrypted, aws_secrets_manager, aws_ssm, or external.");return t}function yo(N,t,p){let A=N.store.find(N.controlPlane.project.id,p,t);if(!A)throw Error(`${t} was not found in the selected scope.`);return A}async function Lu(N,t){if(N==="secret")return gl(t)??await I("Enter secret value (input is write-only)","");if(t.fromEnv){let p=process.env[t.fromEnv];if(p==null)throw Error(`${t.fromEnv} is unavailable.`);return p}if(t.value!=null)return t.value;return await I("Enter variable value","")}function hi(N){return N.option("--env <environment>","Dashboard environment").option("--scope <scope>","project, environment, service, function, or preview",{default:"environment"}).option("--target <id>","Environment, resource, function, or preview ID/name")}function ba(N){N.command("config","Show validated file configuration").action(async()=>Cr(async()=>{let t=await Me();console.log(JSON.stringify(t,null,2))})),N.command("config:validate","Validate configuration file").action(async()=>Cr(async()=>{let t=await Me();if(!t.project?.name||!t.project?.slug||!t.mode)throw Error("project.name, project.slug, and mode are required.");n(`Configuration is valid: ${t.project.name} · ${t.mode} · ${t.project.region||"us-east-1"}`)})),hi(N.command("config:list","List scoped variables and write-only secret metadata")).option("--kind <kind>","variable or secret").option("--search <text>","Filter key names").option("--json","Print structured JSON").action(async(t)=>Cr(async()=>fi(t.env,async(p)=>{let A=Qr(p,t),L=["variable","secret"].includes(t.kind??"")?t.kind:void 0,j=p.service.list({projectId:p.controlPlane.project.id,scope:A,kind:L,search:t.search,canReadSecretMetadata:!0});if(t.json)console.log(JSON.stringify(j,null,2));else c(["Key","Kind","Scope","Precedence","Value/backend","Backend version","Requirement","Updated"],Ou(j))}))),hi(N.command("config:set <key>","Create or replace a scoped variable or secret")).option("--value <value>","Plaintext variable value only").option("--from-env <name>","Read the value from an environment variable").option("--secret","Store a write-only secret").option("--backend <backend>","local_encrypted, aws_secrets_manager, aws_ssm, or external").option("--reference <uri>","External provider reference").option("--required","Fail deployments when unavailable").option("--confirm <key>","Exact key confirmation for production replacements").option("--json","Print structured JSON").action(async(t,p)=>Cr(async()=>fi(p.env,async(A)=>{let L=Qr(A,p),j=p.secret?"secret":"variable",G=j==="secret"?va(p.backend):void 0,Z=G==="external"?void 0:await Lu(j,p),ee=await A.service.set({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,scope:L,key:t,kind:j,value:Z,reference:p.reference,backend:G,required:!!p.required,confirmed:p.confirm===t,actorId:A.actor.id,idempotencyKey:`cli:${crypto.randomUUID()}`});if(p.json)console.log(JSON.stringify({entry:A.service.list({projectId:A.controlPlane.project.id,scope:L,canReadSecretMetadata:!0}).find((re)=>re.id===ee.entry.id),mutation:ee.mutation,warnings:ee.warnings},null,2));else n(`${t}: ${ee.mutation.added.length?"created":ee.mutation.changed.length?"updated":"unchanged"}; ${ee.mutation.affectedResourceIds.length} affected service(s).`)}))),hi(N.command("config:delete <key>","Delete a scoped variable or secret")).option("--confirm <key>","Exact key confirmation").action(async(t,p)=>Cr(async()=>fi(p.env,async(A)=>{let L=yo(A,t,Qr(A,p));await A.service.remove({entryId:L.id,expectedVersion:L.version,confirmed:p.confirm===t,actorId:A.actor.id}),n(`Deleted ${t}.`)}))),hi(N.command("config:import <file>","Validate and import a dotenv file")).option("--secret-keys <keys>","Comma-separated secret keys").option("--backend <backend>","Secret backend").option("--confirm <word>","Use import for production replacement confirmation").option("--json","Print structured JSON").action(async(t,p)=>Cr(async()=>fi(p.env,async(A)=>{let L=await A.service.importDotenv({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,scope:Qr(A,p),source:await fl(ws(t),"utf8"),secretKeys:p.secretKeys?.split(",").map((j)=>j.trim()).filter(Boolean),backend:va(p.backend),confirmed:p.confirm==="import",actorId:A.actor.id,idempotencyKey:`cli-import:${crypto.randomUUID()}`});if(!L.document.valid)throw Error(L.document.diagnostics.map((j)=>`line ${j.line}: ${j.message}`).join(`
68
- `));if(p.json)console.log(JSON.stringify({mutation:L.mutation,diagnostics:L.document.diagnostics,warnings:L.warnings},null,2));else n(`Imported ${L.mutation?.added.length??0} new, ${L.mutation?.changed.length??0} changed, and ${L.mutation?.unchanged.length??0} unchanged entries.`)}))),hi(N.command("config:plan <file>","Preview dotenv drift using key names only")).option("--remove-missing","Include managed keys absent from the file").option("--json","Print structured JSON").action(async(t,p)=>Cr(async()=>fi(p.env,async(A)=>{let{parseDotenv:L}=await import("./chunk-2w591cvg.js"),j=L(await fl(ws(t),"utf8"));if(!j.valid)throw Error(j.diagnostics.map((Z)=>`line ${Z.line}: ${Z.message}`).join(`
69
- `));let G=A.service.plan({projectId:A.controlPlane.project.id,scope:Qr(A,p),values:j.values,removeMissing:!!p.removeMissing});if(p.json)console.log(JSON.stringify(G,null,2));else c(["Action","Keys"],[["Add",G.added.join(", ")||"—"],["Change",G.changed.join(", ")||"—"],["Remove",G.removed.join(", ")||"—"],["Unchanged",G.unchanged.join(", ")||"—"]]),e(`${G.affectedResourceIds.length} affected service(s); no secret values were printed.`)}))),hi(N.command("config:export [file]","Export plaintext variables only as dotenv")).action(async(t,p)=>Cr(async()=>fi(p.env,async(A)=>{let L=A.service.exportVariables({projectId:A.controlPlane.project.id,scope:Qr(A,p)});if(t)await Tu(ws(t),L,{mode:384}),n(`Exported variables to ${ws(t)}; secrets were excluded.`);else process.stdout.write(L)}))),hi(N.command("config:transfer <key>","Copy or move an entry between scopes")).option("--to-scope <scope>","Destination scope").option("--to-target <id>","Destination ID or slug").option("--move","Remove the source after copying").option("--confirm <key>","Exact key confirmation").action(async(t,p)=>Cr(async()=>fi(p.env,async(A)=>{let L=Qr(A,p),j=yo(A,t,L),G=Qr(A,{env:p.env,scope:p.toScope,target:p.toTarget}),Z=await A.service.transfer({entryId:j.id,targetScope:G,mode:p.move?"move":"copy",confirmed:p.confirm===t,actorId:A.actor.id,idempotencyKey:`cli-transfer:${crypto.randomUUID()}`});n(`${t} ${p.move?"moved":"copied"}; ${Z.mutation.affectedResourceIds.length} affected service(s).`)}))),hi(N.command("config:reveal <key>","Reveal a secret after exact confirmation")).option("--confirm <key>","Exact key confirmation").action(async(t,p)=>Cr(async()=>fi(p.env,async(A)=>{if(p.confirm!==t)throw Error(`Use --confirm ${t} to reveal this secret.`);let L=yo(A,t,Qr(A,p));console.log(await A.service.reveal({entryId:L.id,canRevealSecrets:!0,recentlyAuthenticated:!0,actorId:A.actor.id}))}))),hi(N.command("config:env","Backward-compatible variable management")).option("--list","List variables").option("--set <key=value>","Set a variable").option("--unset <key>","Delete a variable").option("--environment <environment>","Target environment").action(async(t)=>Cr(async()=>fi(t.environment??t.env,async(p)=>{let A=Qr(p,{env:t.environment,scope:"environment",target:t.environment});if(t.list)c(["Key","Value","Updated"],p.service.list({projectId:p.controlPlane.project.id,scope:A,kind:"variable"}).map((L)=>[L.key,L.value??"",L.updatedAt]));else if(t.set){let L=t.set.indexOf("=");if(L<1)throw Error("Use --set KEY=VALUE.");let j=t.set.slice(0,L),G=t.set.slice(L+1);await p.service.set({organizationId:p.controlPlane.organization.id,projectId:p.controlPlane.project.id,scope:A,key:j,kind:"variable",value:G,confirmed:!1,actorId:p.actor.id}),n(`Set ${j}.`)}else if(t.unset){let L=yo(p,t.unset,A);await p.service.remove({entryId:L.id,expectedVersion:L.version,confirmed:await d(`Delete ${L.key}?`,!1),actorId:p.actor.id}),n(`Deleted ${L.key}.`)}else e("Use --list, --set KEY=VALUE, or --unset KEY.")}))),hi(N.command("config:secrets","Backward-compatible write-only secret management")).option("--list","List secret metadata").option("--create <key>","Create a secret").option("--update <key>","Rotate a secret").option("--get <key>","Reveal a secret").option("--delete <key>","Delete a secret").option("--from-env <name>","Read new value from an environment variable").option("--backend <backend>","Secret backend").option("--confirm <key>","Exact key confirmation").action(async(t)=>Cr(async()=>fi(t.env,async(p)=>{let A=Qr(p,t);if(t.list)c(["Key","Backend","Version","Rotated"],p.service.list({projectId:p.controlPlane.project.id,scope:A,kind:"secret",canReadSecretMetadata:!0}).map((L)=>[L.key,L.backend??"restricted",L.backendVersion??"—",L.rotatedAt??"never"]));else if(t.create||t.update){let L=t.create??t.update,j=gl(t)??await I("Enter secret value (write-only)","");await p.service.set({organizationId:p.controlPlane.organization.id,projectId:p.controlPlane.project.id,scope:A,key:L,kind:"secret",value:j,backend:va(t.backend),confirmed:t.confirm===L,actorId:p.actor.id}),n(`${L} ${t.update?"rotated":"created"}.`)}else if(t.get){if(t.confirm!==t.get)throw Error(`Use --confirm ${t.get} to reveal.`);let L=yo(p,t.get,A);console.log(await p.service.reveal({entryId:L.id,canRevealSecrets:!0,recentlyAuthenticated:!0,actorId:p.actor.id}))}else if(t.delete){let L=yo(p,t.delete,A);await p.service.remove({entryId:L.id,expectedVersion:L.version,confirmed:t.confirm===L.key,actorId:p.actor.id}),n(`Deleted ${L.key}.`)}else e("Use --list, --create, --update, --get, or --delete.")})))}import{existsSync as Ku}from"node:fs";import{readFile as Ju,writeFile as Yu}from"node:fs/promises";async function Ss(N){let t=[];for(let p of N.steps)try{t.push({step:p,state:await p.satisfied()?"satisfied":"pending"})}catch(A){t.push({step:p,state:"unknown",reason:A instanceof Error?A.message:String(A)})}return t}function hl(N){return N.filter((t)=>t.state!=="satisfied")}function ju(N){return hl(N).some((t)=>t.step.destructive===!0)}function $s(N,t){let p=hl(t),A=[`${N.operation} ${N.target}`];if(p.length===0)return A.push(" Nothing to do — every step is already satisfied."),A;for(let{step:j,state:G,reason:Z}of t){let ee=G==="satisfied"?"ok ":G==="unknown"?"? ":"→ ",re=[j.destructive?"DESTRUCTIVE":"",G==="satisfied"?"already done":""].filter(Boolean);if(A.push(` ${ee}${j.title}${re.length>0?` [${re.join(", ")}]`:""}`),j.change)A.push(` ${j.change.from} → ${j.change.to}`);if(Z)A.push(` could not check: ${Z}`)}let L=p.filter((j)=>j.step.destructive).length;return A.push(` ${p.length} step(s) would run`+(L>0?`, ${L} irreversible — re-run with --confirm ${N.target}`:"")),A}async function Cs(N,t,p={}){let A={operation:N.operation,target:N.target,steps:[],success:!0};if(ju(t)&&p.confirm!==N.target)throw Error(`${N.operation} includes an irreversible step. Re-run with --confirm ${N.target} to authorize it.`);for(let{step:L,state:j}of t){if(j==="satisfied"){A.steps.push({id:L.id,title:L.title,state:"skipped"}),p.log?.(`skip ${L.title} (already done)`);continue}p.audit?.({operation:N.operation,target:N.target,step:L.id,state:"started"});try{await L.apply(),A.steps.push({id:L.id,title:L.title,state:"applied"}),p.log?.(`done ${L.title}`),p.audit?.({operation:N.operation,target:N.target,step:L.id,state:"applied"})}catch(G){let Z=G instanceof Error?G.message:String(G);return A.steps.push({id:L.id,title:L.title,state:"failed",error:Z}),A.success=!1,p.log?.(`FAIL ${L.title}: ${Z}`),p.audit?.({operation:N.operation,target:N.target,step:L.id,state:"failed",error:Z}),A}}return A}function bo(N,t){return`/tmp/ts-cloud-move-${N}-${t}.tar.gz`}function yl(N,t){return`${N}-${t}*.service`}function en(N){return`'${N.split("'").join("'\\''")}'`}function Fu(N,t){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${en(vl(N,t))} || true); do`,' systemctl stop "$unit" 2>/dev/null || true',"done"].join(`
70
- `)}function vl(N,t){return`^${N}-${t}-((queue|daemon)-.*|scheduler)\\.service$`}function Bu(N,t){return bl(vl(N,t))}function bl(N){return[`ls /etc/systemd/system/ 2>/dev/null | grep -E ${en(N)} | while read -r unit; do`,' systemctl is-active "$unit" >/dev/null 2>&1 && echo "active:$unit" || true',"done"].join(`
71
- `)}function Mu(N){return!N.split(`
72
- `).some((t)=>t.trim().startsWith("active:"))}function _u(N,t,p){let A=bo(N,t);return["set -eu",`test -d ${en(p)} || { echo "no site tree at ${p}" >&2; exit 1; }`,`rm -f ${en(A)}`,`tar czf ${en(A)} -C "$(dirname ${en(p)})" "$(basename ${en(p)})" -C /etc/systemd/system $(cd /etc/systemd/system && ls ${yl(N,t)} 2>/dev/null | tr '\\n' ' ')`,`sha256sum ${en(A)} | cut -d' ' -f1`].join(`
73
- `)}function zu(N,t,p){let A=bo(N,t);return["set -eu",`test -f ${en(A)} || { echo "archive not staged at ${A}" >&2; exit 1; }`,`mkdir -p "$(dirname ${en(p)})"`,`tar xzf ${en(A)} -C "$(dirname ${en(p)})" "$(basename ${en(p)})"`,`tar xzf ${en(A)} -C /etc/systemd/system --wildcards ${en(yl(N,t))} 2>/dev/null || true`,"systemctl daemon-reload",`systemctl enable ${N}-${t}.service 2>/dev/null || true`,`systemctl restart ${N}-${t}.service`,`rm -f ${en(A)}`].join(`
74
- `)}function Uu(N,t,p){return[`test -d ${en(p)} && echo tree:present || echo tree:absent`,`systemctl is-active ${N}-${t}.service >/dev/null 2>&1 && echo unit:active || echo unit:inactive`].join(`
75
- `)}function Vu(N){return N.includes("tree:present")&&N.includes("unit:active")}function Wu(N,t="/",p=10){let A=`http://127.0.0.1:${N}${t.startsWith("/")?t:`/${t}`}`;return["set -eu",`for i in $(seq 1 ${p}); do`,` if curl -fsS -o /dev/null --max-time 5 ${en(A)}; then echo healthy; exit 0; fi`," sleep 3","done",`echo "no healthy response from ${A}" >&2`,"exit 1"].join(`
76
- `)}function qu(N,t){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${en(wl(N,t))} || true); do`,' systemctl disable --now "$unit" 2>/dev/null || true',"done",`rm -f /etc/rpx/sites.d/${N}.json 2>/dev/null || true`,"systemctl reload rpx-gateway.service 2>/dev/null || systemctl restart rpx-gateway.service 2>/dev/null || true"].join(`
77
- `)}function wl(N,t){return`^${N}-${t}[-@.]`}function Gu(N,t){return bl(wl(N,t))}function Hu(N){return!N.split(`
78
- `).some((t)=>t.trim().startsWith("active:"))}function Sl(N,t){return`/tmp/ts-cloud-move-${N}-${t}-certs.tar.gz`}function $l(N,t){return["set -u",...t.map((p)=>`if [ -s ${en(`${N}/${p}.crt`)} ]; then echo "cert:${p}:$(sha256sum ${en(`${N}/${p}.crt`)} | cut -d' ' -f1)"; else echo "cert:${p}:absent"; fi`)].join(`
79
- `)}function wa(N){let t=new Map;for(let p of N.split(`
80
- `)){let A=p.trim();if(!A.startsWith("cert:"))continue;let[,L,j]=A.split(":");if(L&&j)t.set(L,j)}return t}function Cl(N,t){for(let[p,A]of N){if(A==="absent")continue;if(t.get(p)!==A)return!1}return!0}function Al(N,t,p){let A=t.flatMap((L)=>[`${L}.crt`,`${L}.key`]);return["set -eu",`rm -f ${en(p)}`,`cd ${en(N)} 2>/dev/null || { echo "no certs dir at ${N}" >&2; exit 0; }`,`TS_CLOUD_CERTS="$(ls ${A.map(en).join(" ")} 2>/dev/null | tr '\\n' ' ' || true)"`,'[ -n "$TS_CLOUD_CERTS" ] || { echo "no certificates to carry"; exit 0; }',`tar czf ${en(p)} $TS_CLOUD_CERTS`].join(`
81
- `)}function kl(N,t){return["set -eu",`[ -s ${en(t)} ] || { echo "no certificate archive staged"; exit 0; }`,`mkdir -p ${en(N)}`,`tar xzf ${en(t)} -C ${en(N)}`,`chmod 600 ${en(N)}/*.key 2>/dev/null || true`,`rm -f ${en(t)}`].join(`
82
- `)}async function Rl(N,t){let{slug:p,siteName:A,appBase:L,from:j,to:G,targetAddress:Z}=N;if(j===G)throw Error(`'${A}' is already on ${G}.`);if(N.database&&!t.database)throw Error(`'${A}' uses the on-box database '${N.database.name}', which lives in the engine's own data directory rather than in the site tree, and this move has no way to carry it. Moving the site without it would cut traffic over to an empty database.`);if(N.database&&t.database&&await t.database.targetHasData())throw Error(`${G} already has a database named '${N.database.name}' with tables in it. Restoring this site's dump over it would destroy that data. Rename or drop it on the target first, or point this project at a different database name.`);let ee=[{id:"pause-workers",title:`Stop background work on ${j} so the snapshot is consistent`,satisfied:async()=>Mu(await t.runOnSource(Bu(p,A))),apply:async()=>{await t.runOnSource(Fu(p,A))}},...t.database?[{id:"database-dump",title:`Dump the '${N.database?.name}' database on ${j}`,satisfied:async()=>!1,apply:()=>t.database.dump()}]:[],{id:"snapshot",title:`Archive the site's tree and units on ${j}`,change:{from:`${j}:${L}`,to:bo(p,A)},satisfied:async()=>!1,apply:async()=>{await t.runOnSource(_u(p,A,L))}},{id:"transfer",title:`Carry the archive to ${G}`,change:{from:j,to:G},satisfied:()=>t.archiveStaged(),apply:()=>t.transferArchive()},...t.database?[{id:"database-transfer",title:`Carry the database dump to ${G}`,change:{from:j,to:G},satisfied:()=>t.database.dumpStaged(),apply:()=>t.database.transferDump()},{id:"database-restore",title:`Create and load '${N.database?.name}' on ${G}`,satisfied:async()=>!1,apply:()=>t.database.restore()}]:[],{id:"restore",title:`Unpack the site on ${G} and start it`,change:{from:bo(p,A),to:`${G}:${L}`},satisfied:async()=>Vu(await t.runOnTarget(Uu(p,A,L))),apply:async()=>{await t.runOnTarget(zu(p,A,L))}}];if(N.port!=null){let{port:re}=N;ee.push({id:"health",title:`Wait for the site to answer on ${G} (127.0.0.1:${re})`,satisfied:async()=>!1,apply:async()=>{await t.runOnTarget(Wu(re,N.healthCheckPath??"/"))}})}return ee.push(...t.certificates?[{id:"certificates",title:`Carry the site's TLS material to ${G}`,satisfied:()=>t.certificates.inPlace(),apply:()=>t.certificates.carry()}]:[],{id:"gateway",title:`Route the site on ${G}`,satisfied:()=>t.targetRoutesSite(),apply:()=>t.refreshTargetGateway()},{id:"dns",title:`Point DNS at ${G}`,change:{from:`${j}`,to:Z},satisfied:async()=>await t.publishedAddress()===Z,apply:async()=>{let re=await t.cutoverDns();if(re.length>0)throw Error(re.join("; "))}},{id:"drain-source",title:`Stop the site on ${j}, leaving its files in place`,satisfied:async()=>Hu(await t.runOnSource(Gu(p,A))),apply:async()=>{await t.runOnSource(qu(p,A))}}),{operation:"site:move",target:A,steps:ee}}async function so(N,t,p){let A=await N.exec(t,p);if(A.code!==0)throw Error(`${t.name}: ${A.stderr.trim()||A.stdout.trim()||`exited ${A.code}`}`);return A.stdout}async function Co(N,t,p){let A=Bun.spawn(["scp","-P",String(N.sshPort),"-o","BatchMode=yes","-o","StrictHostKeyChecking=yes",t,p],{stdout:"pipe",stderr:"pipe"}),[L,j]=await Promise.all([A.exited,new Response(A.stderr).text()]);if(L!==0)throw Error(`scp failed: ${j.trim()||`exited ${L}`}`)}async function Zu(N,t){if(!t.to)throw Error("Name the target server with --to <server>.");let p=await Me(),A=p.sites?.[N];if(!A)throw Error(`Site '${N}' is not in cloud.config.ts.`);let L=p.project.slug,j=y(process.cwd(),p);try{let Z=new Ie(j.store).list(j.project.id),ee=(ft)=>{let Vt=Z.find((vt)=>vt.id===ft||vt.name===ft);if(!Vt)throw Error(`Server ${ft} was not found. Run \`cloud server:list\`.`);if(Vt.trustState!=="pinned")throw Error(`${Vt.name} has no pinned host key. Run \`cloud server:validate ${Vt.name}\` first.`);return Vt},re=ee(t.to),oe=Z.filter((ft)=>ft.id!==re.id);if(!t.from&&oe.length!==1)throw Error(`Name the source server with --from <server> — ${oe.length} others are enrolled.`);let be=ee(t.from??oe[0].name),we=new Ir,Ce=H(L,N),Ee=bo(L,N),Se=A.domain,De=p.infrastructure?.dns?.provider,_e=p.infrastructure?.compute?.proxy?.engine==="rpx"?p.infrastructure.compute.proxy:void 0,Ae=p.infrastructure?.dns?.domain,Te=(ft)=>Ae&&ft.endsWith(Ae)?Ae:ft.split(".").slice(-2).join("."),Ne=C(p),Xe=Ne?.name&&ii(Ne)?{name:Ne.name}:void 0,Ge=`/tmp/ts-cloud-move-${L}-${N}.sql.gz`,Ye=Sl(L,N),et=_e?.certsDir??Lr,Pt=[Se,...A.aliases??[]].filter((ft)=>!!ft),wt=Ne?.engine??"mysql",Ot={runOnSource:(ft)=>so(we,be,ft),runOnTarget:(ft)=>so(we,re,ft),archiveStaged:async()=>(await we.exec(re,`test -f ${Ee} && echo staged || true`)).stdout.includes("staged"),transferArchive:async()=>{let ft=`${process.cwd()}/.ts-cloud-move-${L}-${N}.tar.gz`;await Co(be,`${be.sshUser}@${be.endpoint}:${Ee}`,ft),await Co(re,ft,`${re.sshUser}@${re.endpoint}:${Ee}`),await Bun.file(ft).delete().catch(()=>{})},targetRoutesSite:async()=>{if(!_e||!Se)return!0;return(await we.exec(re,`grep -qF ${JSON.stringify(Se)} /etc/rpx/sites.d/${L}.json 2>/dev/null && echo routed || true`)).stdout.includes("routed")},refreshTargetGateway:async()=>{if(!_e)throw Error(`This project does not run the rpx gateway, so the move cannot publish a route. Point ${re.name}'s web server at ${Ce}/current yourself, then re-run to continue.`);await so(we,re,je({config:fe(p.sites??{},{proxy:_e,slug:L,lan:Ni(p)}),slug:L,preserveManagementDashboardRoutes:!0}).join(`
83
- `))},publishedAddress:async()=>{if(!Se||!De)return re.endpoint;let ft=Xn(De);if(!ft)return;return await Ii(O(ft),Te(Se),Se,re.endpoint,"A")?re.endpoint:void 0},...Xe?{database:{dump:async()=>{await so(we,be,["set -euo pipefail",'eval "$(cd /opt/pantry && pantry env 2>/dev/null)" || true',...$o(wt,Xe.name,"/tmp",Ne),`mv -f "$(ls -1t /tmp/${Xe.name}-*.sql.gz | head -1)" ${Ge}`].join(`
84
- `))},dumpStaged:async()=>(await we.exec(re,`test -s ${Ge} && echo staged || true`)).stdout.includes("staged"),transferDump:async()=>{let ft=`${process.cwd()}/.ts-cloud-move-${L}-${N}.sql.gz`;await Co(be,`${be.sshUser}@${be.endpoint}:${Ge}`,ft),await Co(re,ft,`${re.sshUser}@${re.endpoint}:${Ge}`),await Bun.file(ft).delete().catch(()=>{})},restore:async()=>{await so(we,re,[...ht(Ne,p.infrastructure?.compute?.managedServices??{}),...oi(Ne,{from:Ge}),`rm -f ${Ge}`].join(`
85
- `))},targetHasData:async()=>{let ft=wt==="postgres"?`psql -tAc "select count(*) from information_schema.tables where table_schema='public'" -d ${Xe.name} 2>/dev/null || echo 0`:`mysql -N -B -e "select count(*) from information_schema.tables where table_schema='${Xe.name}'" 2>/dev/null || echo 0`,Vt=await we.exec(re,`eval "$(cd /opt/pantry && pantry env 2>/dev/null)" || true
86
- ${ft}`);return Number.parseInt(Vt.stdout.trim().split(`
87
- `).pop()??"0",10)>0}}}:{},..._e?{certificates:{inPlace:async()=>{let ft=$l(et,Pt),[Vt,vt]=await Promise.all([we.exec(be,ft),we.exec(re,ft)]);return Cl(wa(Vt.stdout),wa(vt.stdout))},carry:async()=>{await so(we,be,Al(et,Pt,Ye));let ft=`${process.cwd()}/.ts-cloud-move-${L}-${N}-certs.tar.gz`;if(!(await we.exec(be,`test -s ${Ye} && echo staged || true`)).stdout.includes("staged"))return;await Co(be,`${be.sshUser}@${be.endpoint}:${Ye}`,ft),await Co(re,ft,`${re.sshUser}@${re.endpoint}:${Ye}`),await Bun.file(ft).delete().catch(()=>{}),await so(we,re,kl(et,Ye)),await we.exec(be,`rm -f ${Ye}`)}}}:{},cutoverDns:async()=>{if(!Se)return[];if(!De)return[`No DNS provider configured — point ${Se} at ${re.endpoint} manually.`];let ft=Xn(De);if(!ft)return[`DNS provider '${De}' is not configured.`];return(await jn({provider:O(ft),zone:Te(Se),fqdn:Se,ipv4:re.endpoint,ipv6:le(void 0)})).warnings}},Ut=await Rl({slug:L,siteName:N,appBase:Ce,from:be.name,to:re.name,targetAddress:re.endpoint,port:A.port,healthCheckPath:A.healthCheck?.path,database:Xe},Ot),sn=await Ss(Ut);if(!t.apply){if(t.json){console.log(JSON.stringify({schemaVersion:1,operation:Ut.operation,target:Ut.target,move:{site:N,from:be.name,to:re.name,address:re.endpoint},steps:sn.map((ft)=>({id:ft.step.id,title:ft.step.title,state:ft.state,change:ft.step.change,reason:ft.reason}))},null,2));return}for(let ft of $s(Ut,sn))console.log(ft);console.log(` The source keeps its files either way — reversible until ${be.name} is destroyed.`),console.log(" Re-run with --apply to perform it.");return}let jt=await Cs(Ut,sn,{log:(ft)=>e(ft),audit:(ft)=>j.store.appendEvent({projectId:j.project.id,type:`${ft.operation}.${ft.step}.${ft.state}`,level:ft.state==="failed"?"error":"info",payload:{site:N,from:be.name,to:re.name,...ft.error?{error:ft.error}:{}}})});if(t.json)console.log(JSON.stringify({schemaVersion:1,outcome:jt},null,2));if(!jt.success){let ft=jt.steps.find((Vt)=>Vt.state==="failed");throw Error(`${Ut.operation} stopped at '${ft?.title}': ${ft?.error}. ${be.name} still has the site and its files; re-run the same command to continue from here.`)}if(!t.json)n(`Moved ${N} from ${be.name} to ${re.name}. ${be.name} keeps its copy until you destroy it.`)}finally{j.store.close()}}function $a(N){N.command("site:move <name>","Move a deployed site to another server").option("--to <server>","Target server (enrolled name or id)").option("--from <server>","Source server; inferred when only one other is enrolled").option("--apply","Perform the reviewed move plan").option("--json","Print structured JSON").action(async(t,p)=>{try{await Zu(t,p)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("site:add <name>","Add a site entry to cloud.config.ts").option("--config <path>","Path to cloud config file").option("--root <path>","Directory to deploy").option("--domain <domain>","Domain for the site").option("--path <path>","Path prefix for the site").option("--deploy <target>","Deployment target: server or bucket").option("--build <command>","Build command to run before deployment").option("--start <command>","Start command for server apps").option("--port <port>","Port for server apps").option("--type <type>","Site type, e.g. static, laravel, php").option("--path-rewrite-style <style>","Static rewrite style: directory or flat").option("--dry-run","Print the updated config without writing").action(async(t,p)=>{let A=p?.config||"cloud.config.ts",L=p?.root;if(!L){r("Missing --root <path>");return}if(!Ku(A)){r(`Config file not found: ${A}`);return}try{let j=un({configText:await Ju(A,"utf8"),name:t,root:L,domain:p?.domain,path:p?.path,deploy:Qu(p?.deploy),build:p?.build,start:p?.start,port:em(p?.port),type:p?.type,pathRewriteStyle:Xu(p?.pathRewriteStyle)});if(p?.dryRun){console.log(j);return}await Yu(A,j),n(`Added site '${t}' to ${A}`)}catch(j){r(j instanceof Error?j.message:String(j))}})}function Qu(N){if(!N)return;if(N==="bucket"||N==="server")return N;throw Error(`Invalid --deploy '${N}'. Expected 'server' or 'bucket'.`)}function Xu(N){if(!N)return;if(N==="directory"||N==="flat")return N;throw Error(`Invalid --path-rewrite-style '${N}'. Expected 'directory' or 'flat'.`)}function em(N){if(!N)return;let t=Number.parseInt(N,10);if(!Number.isInteger(t)||t<1||t>65535)throw Error(`Invalid --port '${N}'. Expected a TCP port between 1 and 65535.`);return t}import{existsSync as om}from"node:fs";import{mkdir as sm,writeFile as am}from"node:fs/promises";import{join as cm}from"node:path";class Ao{builder;config;environment;mergedConfig;serverEipLogicalIds=new Map;constructor(N){this.config=N.config,this.environment=N.environment,this.builder=new So(`${this.config.project.name} - ${this.environment}`),this.mergedConfig=this.mergeEnvironmentConfig()}mergeEnvironmentConfig(){let t=this.config.environments[this.environment]?.infrastructure;if(!t)return this.config;return{...this.config,infrastructure:{...this.config.infrastructure,...t,compute:{...this.config.infrastructure?.compute,...t.compute},storage:{...this.config.infrastructure?.storage,...t.storage},functions:{...this.config.infrastructure?.functions,...t.functions},servers:{...this.config.infrastructure?.servers,...t.servers},databases:{...this.config.infrastructure?.databases,...t.databases},cdn:{...this.config.infrastructure?.cdn,...t.cdn},sftp:t.sftp??this.config.infrastructure?.sftp,queues:{...this.config.infrastructure?.queues,...t.queues},redirects:{...this.config.infrastructure?.redirects,...t.redirects},realtime:{...this.config.infrastructure?.realtime,...t.realtime},cache:{...this.config.infrastructure?.cache,...t.cache},fileSystem:{...this.config.infrastructure?.fileSystem,...t.fileSystem},email:{...this.config.infrastructure?.email,...t.email},search:{...this.config.infrastructure?.search,...t.search},ai:{...this.config.infrastructure?.ai,...t.ai}}}}shouldDeploy(N){if(N.environments&&!N.environments.includes(this.environment))return!1;if(N.requiresFeatures){let t=this.config.features||{};if(!N.requiresFeatures.every((A)=>t[A]===!0))return!1}if(N.regions){let t=this.config.environments[this.environment]?.region||this.config.project.region;if(!N.regions.includes(t))return!1}if(N.condition&&typeof N.condition==="function")return N.condition(this.config,this.environment);return!0}resolveApiOriginPort(){let N=this.mergedConfig.infrastructure?.api?.port??this.mergedConfig.ports?.api,t=Number(N||3008);return Number.isFinite(t)&&t>0?t:3008}defaultComputeCachePathPatterns(){return["/api/*","/auth/*","/publisher/api/*","/publisher/*","/publish","/publish/*","/analytics/*","/packages/*","/npm/*","/zig/*","/php/*","/commits/*","/health","/dashboard/*","/search","/login","/logout","/signup","/account","/account/*"]}shouldRouteStorageBucketToCompute(N,t){return N==="public"||t.routeCompute===!0}storageBucketName(N,t,p){return this.mergedConfig.infrastructure?.storage?.[p]?.bucket??`${N}-${t}-${p}`}fileSystemLogicalId(N,t,p){return s(l({slug:`${N}-${p}`,environment:t,resourceType:"efs"}))}resolveSftpStorage(N,t,p){let A=N.storage;if(A?.type==="efs"&&A.fileSystem){if(!this.mergedConfig.infrastructure?.fileSystem?.[A.fileSystem])throw Error(`sftp: storage.fileSystem "${A.fileSystem}" is not defined in infrastructure.fileSystem`);return{type:"efs",fileSystemId:{Ref:this.fileSystemLogicalId(t,p,A.fileSystem)},posixProfile:A.posixProfile}}if(A?.type==="s3"&&A.storageBucket){if(!this.mergedConfig.infrastructure?.storage?.[A.storageBucket])throw Error(`sftp: storage.storageBucket "${A.storageBucket}" is not defined in infrastructure.storage`);return{type:"s3",bucket:this.storageBucketName(t,p,A.storageBucket)}}return}resolveComputeCachePathPatterns(N){if(N&&N.length>0)return N;return this.defaultComputeCachePathPatterns()}createComputeCacheBehavior(N,t){return{PathPattern:N,TargetOriginId:t,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS","PUT","POST","PATCH","DELETE"],CachedMethods:["GET","HEAD"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",OriginRequestPolicyId:"b689b0a8-53d0-40ab-baf2-68738e2966ac"}}appendComputeAppOrigin(N,t,p,A,L,j){let G=this.serverEipLogicalIds.get("app"),Z=this.serverEipLogicalIds.get("appInstance");if(!G)return;let ee=`EC2-${A}-${L}-api`,re=this.mergedConfig.infrastructure?.servers?.app?.region||this.mergedConfig.project?.region||"us-east-1",oe=re==="us-east-1"?".compute-1.amazonaws.com":`.${re}.compute.amazonaws.com`,be={"Fn::Join":["",["ec2-",{"Fn::Join":["-",{"Fn::Split":[".",{Ref:G}]}]},oe]]},we=this.resolveApiOriginPort();if(N.push({Id:ee,DomainName:be,CustomOriginConfig:{HTTPPort:we,HTTPSPort:443,OriginProtocolPolicy:"http-only",OriginSSLProtocols:["TLSv1.2"]}}),p.push(G),Z)p.push(Z);for(let Ce of j)t.push(this.createComputeCacheBehavior(Ce,ee))}buildCaddyfile(N){let t=N.filter(([,L])=>typeof L.domain==="string"&&L.domain&&typeof L.port==="number");if(t.length===0)return;let p=new Map;for(let[,L]of t){let j=p.get(L.domain)??[];j.push({port:L.port,path:L.path}),p.set(L.domain,j)}let A=[];for(let[L,j]of p){let Z=[...j].sort((ee,re)=>{let oe=!ee.path||ee.path==="/",be=!re.path||re.path==="/";if(oe&&!be)return 1;if(!oe&&be)return-1;return(re.path?.length??0)-(ee.path?.length??0)}).map((ee)=>{let re=!ee.path||ee.path==="/",oe=`reverse_proxy localhost:${ee.port}`;return re?` handle {
67
+ 3. Run 'cloud deploy' to deploy your infrastructure`,"green")}),N.command("init:server","Initialize server-based (EC2) project").action(async()=>{i("Initializing Server-Based Project"),await N.parse(["init","--mode","server"])}),N.command("init:serverless","Initialize serverless (Fargate/Lambda) project").action(async()=>{i("Initializing Serverless Project"),await N.parse(["init","--mode","serverless"])}),N.command("init:hybrid","Initialize hybrid project").action(async()=>{i("Initializing Hybrid Project"),await N.parse(["init","--mode","hybrid"])})}import{readFile as Al,writeFile as Vu}from"node:fs/promises";import{resolve as Rs}from"node:path";async function Ne(){let N=await Ai();if(!N.project)throw Error("Missing required project configuration in cloud.config.ts");return N}function ir(N){if(N)switch(N.toLowerCase()){case"porkbun":{let A=process.env.PORKBUN_API_KEY,L=process.env.PORKBUN_SECRET_KEY;if(!A||!L)throw Error("PORKBUN_API_KEY and PORKBUN_SECRET_KEY environment variables are required for Porkbun provider");return{provider:"porkbun",apiKey:A,secretKey:L}}case"godaddy":{let A=process.env.GODADDY_API_KEY,L=process.env.GODADDY_API_SECRET;if(!A||!L)throw Error("GODADDY_API_KEY and GODADDY_API_SECRET environment variables are required for GoDaddy provider");let j=process.env.GODADDY_ENVIRONMENT||"production";return{provider:"godaddy",apiKey:A,apiSecret:L,environment:j}}case"route53":{let A=process.env.AWS_REGION||"us-east-1",L=process.env.AWS_HOSTED_ZONE_ID;return{provider:"route53",region:A,hostedZoneId:L}}case"cloudflare":{let A=process.env.CLOUDFLARE_API_TOKEN;if(!A)throw Error("CLOUDFLARE_API_TOKEN environment variable is required for Cloudflare provider");return{provider:"cloudflare",apiToken:A,zoneId:process.env.CLOUDFLARE_ZONE_ID,accountId:process.env.CLOUDFLARE_ACCOUNT_ID}}default:throw Error(`Unknown DNS provider: ${N}. Supported: porkbun, godaddy, route53, cloudflare`)}if(new ni().loadFromEnv().getAllProviders().length===0)return null;if(process.env.PORKBUN_API_KEY&&process.env.PORKBUN_SECRET_KEY)return{provider:"porkbun",apiKey:process.env.PORKBUN_API_KEY,secretKey:process.env.PORKBUN_SECRET_KEY};if(process.env.GODADDY_API_KEY&&process.env.GODADDY_API_SECRET)return{provider:"godaddy",apiKey:process.env.GODADDY_API_KEY,apiSecret:process.env.GODADDY_API_SECRET,environment:process.env.GODADDY_ENVIRONMENT||"production"};if(process.env.AWS_ACCESS_KEY_ID||process.env.AWS_REGION)return{provider:"route53",region:process.env.AWS_REGION||"us-east-1",hostedZoneId:process.env.AWS_HOSTED_ZONE_ID};if(process.env.CLOUDFLARE_API_TOKEN)return{provider:"cloudflare",apiToken:process.env.CLOUDFLARE_API_TOKEN,zoneId:process.env.CLOUDFLARE_ZONE_ID,accountId:process.env.CLOUDFLARE_ACCOUNT_ID};return null}function vi(N){let t=ir(N);if(!t)throw Error("No DNS provider configured. Set environment variables for Porkbun (PORKBUN_API_KEY, PORKBUN_SECRET_KEY), GoDaddy (GODADDY_API_KEY, GODADDY_API_SECRET), Cloudflare (CLOUDFLARE_API_TOKEN), or Route53 (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)");return O(t)}async function Wu(N){let t=await Ne(),m=y(process.cwd(),t),A=N??Object.keys(t.environments??{})[0]??"production",L=m.environments.get(A);if(!L)throw m.store.close(),Error(`Environment ${A} was not found.`);let j=t.project?.region??"us-east-1",G=k(process.cwd()),Z=new Sr(m.store),ee=new Er(Z,{encryptionKey:G,backends:[new bt(m.store,G),new br(new q(j),j),new vr(new je(j),j),new Tr]}),ne=m.store.getActorByExternalId("system","cli")??m.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return await en(ee,m,t,ne.id),{config:t,controlPlane:m,env:A,environmentRecord:L,store:Z,service:ee,actor:ne}}async function bi(N,t){let m=await Wu(N);try{return await t(m)}finally{m.controlPlane.store.close()}}async function zr(N){try{await N()}catch(t){r(t instanceof Error?t.message:String(t))}}function qu(N){return N.map((t)=>[t.key,t.kind,t.scope.type,t.inherited?"inherited":t.overridden?"override":"direct",t.kind==="secret"?t.backend??"restricted":t.value??"",t.backendVersion??"—",t.required?"required":"optional",t.updatedAt])}function kl(N,t=process.env){if(N.value!=null)throw Error("Secret values are not accepted in command arguments; use --from-env or the interactive prompt.");if(!N.fromEnv)return;let m=t[N.fromEnv];if(!m)throw Error(`${N.fromEnv} is empty or unavailable.`);return m}function ii(N,t){let m=t.scope??"environment";if(m==="project")return{type:m,id:N.controlPlane.project.id};if(m==="environment"){let A=t.target?N.controlPlane.store.listEnvironments(N.controlPlane.project.id).find((L)=>L.id===t.target||L.slug===t.target):N.environmentRecord;if(!A)throw Error(`Environment ${t.target} was not found.`);return{type:m,id:A.id,environmentId:A.id}}if(m==="service"){let A=N.controlPlane.store.listResources(N.controlPlane.project.id),L=A.find((j)=>j.id===t.target||j.slug===t.target);if(!L)throw Error("Service scope requires --target with a resource ID or slug.");return{type:m,id:L.id,environmentId:L.environmentId,resourceId:L.id}}if(m==="function"){let A=N.controlPlane.store.listResources(N.controlPlane.project.id),L=A.find((j)=>j.kind==="function"&&(j.id===t.target||j.slug===t.target));if(!L)throw Error("Function scope requires --target with a function resource ID or slug.");return{type:m,id:L.id,environmentId:L.environmentId,resourceId:L.id}}if(m==="preview"){let A=new $e(N.controlPlane.store).listInstances({projectId:N.controlPlane.project.id}).find((L)=>L.id===t.target||L.name===t.target);if(!A)throw Error("Preview scope requires --target with a preview ID or name.");return{type:m,id:A.id,environmentId:A.baseEnvironmentId,resourceId:A.resourceId,previewId:A.id}}throw Error("--scope must be project, environment, service, function, or preview.")}function Ra(N){let t=N??"local_encrypted";if(!["local_encrypted","aws_secrets_manager","aws_ssm","external"].includes(t))throw Error("--backend must be local_encrypted, aws_secrets_manager, aws_ssm, or external.");return t}function So(N,t,m){let A=N.store.find(N.controlPlane.project.id,m,t);if(!A)throw Error(`${t} was not found in the selected scope.`);return A}async function Gu(N,t){if(N==="secret")return kl(t)??await I("Enter secret value (input is write-only)","");if(t.fromEnv){let m=process.env[t.fromEnv];if(m==null)throw Error(`${t.fromEnv} is unavailable.`);return m}if(t.value!=null)return t.value;return await I("Enter variable value","")}function Si(N){return N.option("--env <environment>","Dashboard environment").option("--scope <scope>","project, environment, service, function, or preview",{default:"environment"}).option("--target <id>","Environment, resource, function, or preview ID/name")}function Pa(N){N.command("config","Show validated file configuration").action(async()=>zr(async()=>{let t=await Ne();console.log(JSON.stringify(t,null,2))})),N.command("config:validate","Validate configuration file").action(async()=>zr(async()=>{let t=await Ne();if(!t.project?.name||!t.project?.slug||!t.mode)throw Error("project.name, project.slug, and mode are required.");n(`Configuration is valid: ${t.project.name} · ${t.mode} · ${t.project.region||"us-east-1"}`)})),Si(N.command("config:list","List scoped variables and write-only secret metadata")).option("--kind <kind>","variable or secret").option("--search <text>","Filter key names").option("--json","Print structured JSON").action(async(t)=>zr(async()=>bi(t.env,async(m)=>{let A=ii(m,t),L=["variable","secret"].includes(t.kind??"")?t.kind:void 0,j=m.service.list({projectId:m.controlPlane.project.id,scope:A,kind:L,search:t.search,canReadSecretMetadata:!0});if(t.json)console.log(JSON.stringify(j,null,2));else c(["Key","Kind","Scope","Precedence","Value/backend","Backend version","Requirement","Updated"],qu(j))}))),Si(N.command("config:set <key>","Create or replace a scoped variable or secret")).option("--value <value>","Plaintext variable value only").option("--from-env <name>","Read the value from an environment variable").option("--secret","Store a write-only secret").option("--backend <backend>","local_encrypted, aws_secrets_manager, aws_ssm, or external").option("--reference <uri>","External provider reference").option("--required","Fail deployments when unavailable").option("--confirm <key>","Exact key confirmation for production replacements").option("--json","Print structured JSON").action(async(t,m)=>zr(async()=>bi(m.env,async(A)=>{let L=ii(A,m),j=m.secret?"secret":"variable",G=j==="secret"?Ra(m.backend):void 0,Z=G==="external"?void 0:await Gu(j,m),ee=await A.service.set({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,scope:L,key:t,kind:j,value:Z,reference:m.reference,backend:G,required:!!m.required,confirmed:m.confirm===t,actorId:A.actor.id,idempotencyKey:`cli:${crypto.randomUUID()}`});if(m.json)console.log(JSON.stringify({entry:A.service.list({projectId:A.controlPlane.project.id,scope:L,canReadSecretMetadata:!0}).find((ne)=>ne.id===ee.entry.id),mutation:ee.mutation,warnings:ee.warnings},null,2));else n(`${t}: ${ee.mutation.added.length?"created":ee.mutation.changed.length?"updated":"unchanged"}; ${ee.mutation.affectedResourceIds.length} affected service(s).`)}))),Si(N.command("config:delete <key>","Delete a scoped variable or secret")).option("--confirm <key>","Exact key confirmation").action(async(t,m)=>zr(async()=>bi(m.env,async(A)=>{let L=So(A,t,ii(A,m));await A.service.remove({entryId:L.id,expectedVersion:L.version,confirmed:m.confirm===t,actorId:A.actor.id}),n(`Deleted ${t}.`)}))),Si(N.command("config:import <file>","Validate and import a dotenv file")).option("--secret-keys <keys>","Comma-separated secret keys").option("--backend <backend>","Secret backend").option("--confirm <word>","Use import for production replacement confirmation").option("--json","Print structured JSON").action(async(t,m)=>zr(async()=>bi(m.env,async(A)=>{let L=await A.service.importDotenv({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,scope:ii(A,m),source:await Al(Rs(t),"utf8"),secretKeys:m.secretKeys?.split(",").map((j)=>j.trim()).filter(Boolean),backend:Ra(m.backend),confirmed:m.confirm==="import",actorId:A.actor.id,idempotencyKey:`cli-import:${crypto.randomUUID()}`});if(!L.document.valid)throw Error(L.document.diagnostics.map((j)=>`line ${j.line}: ${j.message}`).join(`
68
+ `));if(m.json)console.log(JSON.stringify({mutation:L.mutation,diagnostics:L.document.diagnostics,warnings:L.warnings},null,2));else n(`Imported ${L.mutation?.added.length??0} new, ${L.mutation?.changed.length??0} changed, and ${L.mutation?.unchanged.length??0} unchanged entries.`)}))),Si(N.command("config:plan <file>","Preview dotenv drift using key names only")).option("--remove-missing","Include managed keys absent from the file").option("--json","Print structured JSON").action(async(t,m)=>zr(async()=>bi(m.env,async(A)=>{let{parseDotenv:L}=await import("./chunk-c7vxdwth.js"),j=L(await Al(Rs(t),"utf8"));if(!j.valid)throw Error(j.diagnostics.map((Z)=>`line ${Z.line}: ${Z.message}`).join(`
69
+ `));let G=A.service.plan({projectId:A.controlPlane.project.id,scope:ii(A,m),values:j.values,removeMissing:!!m.removeMissing});if(m.json)console.log(JSON.stringify(G,null,2));else c(["Action","Keys"],[["Add",G.added.join(", ")||"—"],["Change",G.changed.join(", ")||"—"],["Remove",G.removed.join(", ")||"—"],["Unchanged",G.unchanged.join(", ")||"—"]]),e(`${G.affectedResourceIds.length} affected service(s); no secret values were printed.`)}))),Si(N.command("config:export [file]","Export plaintext variables only as dotenv")).action(async(t,m)=>zr(async()=>bi(m.env,async(A)=>{let L=A.service.exportVariables({projectId:A.controlPlane.project.id,scope:ii(A,m)});if(t)await Vu(Rs(t),L,{mode:384}),n(`Exported variables to ${Rs(t)}; secrets were excluded.`);else process.stdout.write(L)}))),Si(N.command("config:transfer <key>","Copy or move an entry between scopes")).option("--to-scope <scope>","Destination scope").option("--to-target <id>","Destination ID or slug").option("--move","Remove the source after copying").option("--confirm <key>","Exact key confirmation").action(async(t,m)=>zr(async()=>bi(m.env,async(A)=>{let L=ii(A,m),j=So(A,t,L),G=ii(A,{env:m.env,scope:m.toScope,target:m.toTarget}),Z=await A.service.transfer({entryId:j.id,targetScope:G,mode:m.move?"move":"copy",confirmed:m.confirm===t,actorId:A.actor.id,idempotencyKey:`cli-transfer:${crypto.randomUUID()}`});n(`${t} ${m.move?"moved":"copied"}; ${Z.mutation.affectedResourceIds.length} affected service(s).`)}))),Si(N.command("config:reveal <key>","Reveal a secret after exact confirmation")).option("--confirm <key>","Exact key confirmation").action(async(t,m)=>zr(async()=>bi(m.env,async(A)=>{if(m.confirm!==t)throw Error(`Use --confirm ${t} to reveal this secret.`);let L=So(A,t,ii(A,m));console.log(await A.service.reveal({entryId:L.id,canRevealSecrets:!0,recentlyAuthenticated:!0,actorId:A.actor.id}))}))),Si(N.command("config:env","Backward-compatible variable management")).option("--list","List variables").option("--set <key=value>","Set a variable").option("--unset <key>","Delete a variable").option("--environment <environment>","Target environment").action(async(t)=>zr(async()=>bi(t.environment??t.env,async(m)=>{let A=ii(m,{env:t.environment,scope:"environment",target:t.environment});if(t.list)c(["Key","Value","Updated"],m.service.list({projectId:m.controlPlane.project.id,scope:A,kind:"variable"}).map((L)=>[L.key,L.value??"",L.updatedAt]));else if(t.set){let L=t.set.indexOf("=");if(L<1)throw Error("Use --set KEY=VALUE.");let j=t.set.slice(0,L),G=t.set.slice(L+1);await m.service.set({organizationId:m.controlPlane.organization.id,projectId:m.controlPlane.project.id,scope:A,key:j,kind:"variable",value:G,confirmed:!1,actorId:m.actor.id}),n(`Set ${j}.`)}else if(t.unset){let L=So(m,t.unset,A);await m.service.remove({entryId:L.id,expectedVersion:L.version,confirmed:await d(`Delete ${L.key}?`,!1),actorId:m.actor.id}),n(`Deleted ${L.key}.`)}else e("Use --list, --set KEY=VALUE, or --unset KEY.")}))),Si(N.command("config:secrets","Backward-compatible write-only secret management")).option("--list","List secret metadata").option("--create <key>","Create a secret").option("--update <key>","Rotate a secret").option("--get <key>","Reveal a secret").option("--delete <key>","Delete a secret").option("--from-env <name>","Read new value from an environment variable").option("--backend <backend>","Secret backend").option("--confirm <key>","Exact key confirmation").action(async(t)=>zr(async()=>bi(t.env,async(m)=>{let A=ii(m,t);if(t.list)c(["Key","Backend","Version","Rotated"],m.service.list({projectId:m.controlPlane.project.id,scope:A,kind:"secret",canReadSecretMetadata:!0}).map((L)=>[L.key,L.backend??"restricted",L.backendVersion??"—",L.rotatedAt??"never"]));else if(t.create||t.update){let L=t.create??t.update,j=kl(t)??await I("Enter secret value (write-only)","");await m.service.set({organizationId:m.controlPlane.organization.id,projectId:m.controlPlane.project.id,scope:A,key:L,kind:"secret",value:j,backend:Ra(t.backend),confirmed:t.confirm===L,actorId:m.actor.id}),n(`${L} ${t.update?"rotated":"created"}.`)}else if(t.get){if(t.confirm!==t.get)throw Error(`Use --confirm ${t.get} to reveal.`);let L=So(m,t.get,A);console.log(await m.service.reveal({entryId:L.id,canRevealSecrets:!0,recentlyAuthenticated:!0,actorId:m.actor.id}))}else if(t.delete){let L=So(m,t.delete,A);await m.service.remove({entryId:L.id,expectedVersion:L.version,confirmed:t.confirm===L.key,actorId:m.actor.id}),n(`Deleted ${L.key}.`)}else e("Use --list, --create, --update, --get, or --delete.")})))}import{existsSync as op}from"node:fs";import{readFile as sp,writeFile as ap}from"node:fs/promises";async function Is(N){let t=[];for(let m of N.steps)try{t.push({step:m,state:await m.satisfied()?"satisfied":"pending"})}catch(A){t.push({step:m,state:"unknown",reason:A instanceof Error?A.message:String(A)})}return t}function Rl(N){return N.filter((t)=>t.state!=="satisfied")}function Hu(N){return Rl(N).some((t)=>t.step.destructive===!0)}function Ps(N,t){let m=Rl(t),A=[`${N.operation} ${N.target}`];if(m.length===0)return A.push(" Nothing to do — every step is already satisfied."),A;for(let{step:j,state:G,reason:Z}of t){let ee=G==="satisfied"?"ok ":G==="unknown"?"? ":"→ ",ne=[j.destructive?"DESTRUCTIVE":"",G==="satisfied"?"already done":""].filter(Boolean);if(A.push(` ${ee}${j.title}${ne.length>0?` [${ne.join(", ")}]`:""}`),j.change)A.push(` ${j.change.from} → ${j.change.to}`);if(Z)A.push(` could not check: ${Z}`)}let L=m.filter((j)=>j.step.destructive).length;return A.push(` ${m.length} step(s) would run`+(L>0?`, ${L} irreversible — re-run with --confirm ${N.target}`:"")),A}async function xs(N,t,m={}){let A={operation:N.operation,target:N.target,steps:[],success:!0};if(Hu(t)&&m.confirm!==N.target)throw Error(`${N.operation} includes an irreversible step. Re-run with --confirm ${N.target} to authorize it.`);for(let{step:L,state:j}of t){if(j==="satisfied"){A.steps.push({id:L.id,title:L.title,state:"skipped"}),m.log?.(`skip ${L.title} (already done)`);continue}m.audit?.({operation:N.operation,target:N.target,step:L.id,state:"started"});try{await L.apply(),A.steps.push({id:L.id,title:L.title,state:"applied"}),m.log?.(`done ${L.title}`),m.audit?.({operation:N.operation,target:N.target,step:L.id,state:"applied"})}catch(G){let Z=G instanceof Error?G.message:String(G);return A.steps.push({id:L.id,title:L.title,state:"failed",error:Z}),A.success=!1,m.log?.(`FAIL ${L.title}: ${Z}`),m.audit?.({operation:N.operation,target:N.target,step:L.id,state:"failed",error:Z}),A}}return A}function $o(N,t){return`/tmp/ts-cloud-move-${N}-${t}.tar.gz`}function Il(N,t){return`${N}-${t}*.service`}function tn(N){return`'${N.split("'").join("'\\''")}'`}function Ku(N,t){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${tn(Pl(N,t))} || true); do`,' systemctl stop "$unit" 2>/dev/null || true',"done"].join(`
70
+ `)}function Pl(N,t){return`^${N}-${t}-((queue|daemon)-.*|scheduler)\\.service$`}function Ju(N,t){return xl(Pl(N,t))}function xl(N){return[`ls /etc/systemd/system/ 2>/dev/null | grep -E ${tn(N)} | while read -r unit; do`,' systemctl is-active "$unit" >/dev/null 2>&1 && echo "active:$unit" || true',"done"].join(`
71
+ `)}function Yu(N){return!N.split(`
72
+ `).some((t)=>t.trim().startsWith("active:"))}function Zu(N,t,m){let A=$o(N,t);return["set -eu",`test -d ${tn(m)} || { echo "no site tree at ${m}" >&2; exit 1; }`,`rm -f ${tn(A)}`,`tar czf ${tn(A)} -C "$(dirname ${tn(m)})" "$(basename ${tn(m)})" -C /etc/systemd/system $(cd /etc/systemd/system && ls ${Il(N,t)} 2>/dev/null | tr '\\n' ' ')`,`sha256sum ${tn(A)} | cut -d' ' -f1`].join(`
73
+ `)}function Qu(N,t,m){let A=$o(N,t);return["set -eu",`test -f ${tn(A)} || { echo "archive not staged at ${A}" >&2; exit 1; }`,`mkdir -p "$(dirname ${tn(m)})"`,`tar xzf ${tn(A)} -C "$(dirname ${tn(m)})" "$(basename ${tn(m)})"`,`tar xzf ${tn(A)} -C /etc/systemd/system --wildcards ${tn(Il(N,t))} 2>/dev/null || true`,"systemctl daemon-reload",`systemctl enable ${N}-${t}.service 2>/dev/null || true`,`systemctl restart ${N}-${t}.service`,`rm -f ${tn(A)}`].join(`
74
+ `)}function Xu(N,t,m){return[`test -d ${tn(m)} && echo tree:present || echo tree:absent`,`systemctl is-active ${N}-${t}.service >/dev/null 2>&1 && echo unit:active || echo unit:inactive`].join(`
75
+ `)}function ep(N){return N.includes("tree:present")&&N.includes("unit:active")}function tp(N,t="/",m=10){let A=`http://127.0.0.1:${N}${t.startsWith("/")?t:`/${t}`}`;return["set -eu",`for i in $(seq 1 ${m}); do`,` if curl -fsS -o /dev/null --max-time 5 ${tn(A)}; then echo healthy; exit 0; fi`," sleep 3","done",`echo "no healthy response from ${A}" >&2`,"exit 1"].join(`
76
+ `)}function np(N,t){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${tn(Dl(N,t))} || true); do`,' systemctl disable --now "$unit" 2>/dev/null || true',"done",`rm -f /etc/rpx/sites.d/${N}.json 2>/dev/null || true`,"systemctl reload rpx-gateway.service 2>/dev/null || systemctl restart rpx-gateway.service 2>/dev/null || true"].join(`
77
+ `)}function Dl(N,t){return`^${N}-${t}[-@.]`}function rp(N,t){return xl(Dl(N,t))}function ip(N){return!N.split(`
78
+ `).some((t)=>t.trim().startsWith("active:"))}function El(N,t){return`/tmp/ts-cloud-move-${N}-${t}-certs.tar.gz`}function Tl(N,t){return["set -u",...t.map((m)=>`if [ -s ${tn(`${N}/${m}.crt`)} ]; then echo "cert:${m}:$(sha256sum ${tn(`${N}/${m}.crt`)} | cut -d' ' -f1)"; else echo "cert:${m}:absent"; fi`)].join(`
79
+ `)}function xa(N){let t=new Map;for(let m of N.split(`
80
+ `)){let A=m.trim();if(!A.startsWith("cert:"))continue;let[,L,j]=A.split(":");if(L&&j)t.set(L,j)}return t}function Nl(N,t){for(let[m,A]of N){if(A==="absent")continue;if(t.get(m)!==A)return!1}return!0}function Ol(N,t,m){let A=t.flatMap((L)=>[`${L}.crt`,`${L}.key`]);return["set -eu",`rm -f ${tn(m)}`,`cd ${tn(N)} 2>/dev/null || { echo "no certs dir at ${N}" >&2; exit 0; }`,`TS_CLOUD_CERTS="$(ls ${A.map(tn).join(" ")} 2>/dev/null | tr '\\n' ' ' || true)"`,'[ -n "$TS_CLOUD_CERTS" ] || { echo "no certificates to carry"; exit 0; }',`tar czf ${tn(m)} $TS_CLOUD_CERTS`].join(`
81
+ `)}function Ll(N,t){return["set -eu",`[ -s ${tn(t)} ] || { echo "no certificate archive staged"; exit 0; }`,`mkdir -p ${tn(N)}`,`tar xzf ${tn(t)} -C ${tn(N)}`,`chmod 600 ${tn(N)}/*.key 2>/dev/null || true`,`rm -f ${tn(t)}`].join(`
82
+ `)}async function jl(N,t){let{slug:m,siteName:A,appBase:L,from:j,to:G,targetAddress:Z}=N;if(j===G)throw Error(`'${A}' is already on ${G}.`);if(N.database&&!t.database)throw Error(`'${A}' uses the on-box database '${N.database.name}', which lives in the engine's own data directory rather than in the site tree, and this move has no way to carry it. Moving the site without it would cut traffic over to an empty database.`);if(N.database&&t.database&&await t.database.targetHasData())throw Error(`${G} already has a database named '${N.database.name}' with tables in it. Restoring this site's dump over it would destroy that data. Rename or drop it on the target first, or point this project at a different database name.`);let ee=[{id:"pause-workers",title:`Stop background work on ${j} so the snapshot is consistent`,satisfied:async()=>Yu(await t.runOnSource(Ju(m,A))),apply:async()=>{await t.runOnSource(Ku(m,A))}},...t.database?[{id:"database-dump",title:`Dump the '${N.database?.name}' database on ${j}`,satisfied:async()=>!1,apply:()=>t.database.dump()}]:[],{id:"snapshot",title:`Archive the site's tree and units on ${j}`,change:{from:`${j}:${L}`,to:$o(m,A)},satisfied:async()=>!1,apply:async()=>{await t.runOnSource(Zu(m,A,L))}},{id:"transfer",title:`Carry the archive to ${G}`,change:{from:j,to:G},satisfied:()=>t.archiveStaged(),apply:()=>t.transferArchive()},...t.database?[{id:"database-transfer",title:`Carry the database dump to ${G}`,change:{from:j,to:G},satisfied:()=>t.database.dumpStaged(),apply:()=>t.database.transferDump()},{id:"database-restore",title:`Create and load '${N.database?.name}' on ${G}`,satisfied:async()=>!1,apply:()=>t.database.restore()}]:[],{id:"restore",title:`Unpack the site on ${G} and start it`,change:{from:$o(m,A),to:`${G}:${L}`},satisfied:async()=>ep(await t.runOnTarget(Xu(m,A,L))),apply:async()=>{await t.runOnTarget(Qu(m,A,L))}}];if(N.port!=null){let{port:ne}=N;ee.push({id:"health",title:`Wait for the site to answer on ${G} (127.0.0.1:${ne})`,satisfied:async()=>!1,apply:async()=>{await t.runOnTarget(tp(ne,N.healthCheckPath??"/"))}})}return ee.push(...t.certificates?[{id:"certificates",title:`Carry the site's TLS material to ${G}`,satisfied:()=>t.certificates.inPlace(),apply:()=>t.certificates.carry()}]:[],{id:"gateway",title:`Route the site on ${G}`,satisfied:()=>t.targetRoutesSite(),apply:()=>t.refreshTargetGateway()},{id:"dns",title:`Point DNS at ${G}`,change:{from:`${j}`,to:Z},satisfied:async()=>await t.publishedAddress()===Z,apply:async()=>{let ne=await t.cutoverDns();if(ne.length>0)throw Error(ne.join("; "))}},{id:"drain-source",title:`Stop the site on ${j}, leaving its files in place`,satisfied:async()=>ip(await t.runOnSource(rp(m,A))),apply:async()=>{await t.runOnSource(np(m,A))}}),{operation:"site:move",target:A,steps:ee}}async function mo(N,t,m){let A=await N.exec(t,m);if(A.code!==0)throw Error(`${t.name}: ${A.stderr.trim()||A.stdout.trim()||`exited ${A.code}`}`);return A.stdout}async function ko(N,t,m){let A=Bun.spawn(["scp","-P",String(N.sshPort),"-o","BatchMode=yes","-o","StrictHostKeyChecking=yes",t,m],{stdout:"pipe",stderr:"pipe"}),[L,j]=await Promise.all([A.exited,new Response(A.stderr).text()]);if(L!==0)throw Error(`scp failed: ${j.trim()||`exited ${L}`}`)}async function cp(N,t){if(!t.to)throw Error("Name the target server with --to <server>.");let m=await Ne(),A=m.sites?.[N];if(!A)throw Error(`Site '${N}' is not in cloud.config.ts.`);let L=m.project.slug,j=y(process.cwd(),m);try{let Z=new Ie(j.store).list(j.project.id),ee=(ht)=>{let Vt=Z.find((nn)=>nn.id===ht||nn.name===ht);if(!Vt)throw Error(`Server ${ht} was not found. Run \`cloud server:list\`.`);if(Vt.trustState!=="pinned")throw Error(`${Vt.name} has no pinned host key. Run \`cloud server:validate ${Vt.name}\` first.`);return Vt},ne=ee(t.to),oe=Z.filter((ht)=>ht.id!==ne.id);if(!t.from&&oe.length!==1)throw Error(`Name the source server with --from <server> — ${oe.length} others are enrolled.`);let be=ee(t.from??oe[0].name),we=new Rr,Ce=W(L,N),De=$o(L,N),Se=A.domain,xe=m.infrastructure?.dns?.provider,Me=m.infrastructure?.compute?.proxy?.engine==="rpx"?m.infrastructure.compute.proxy:void 0,Ae=m.infrastructure?.dns?.domain,Ee=(ht)=>Ae&&ht.endsWith(Ae)?Ae:ht.split(".").slice(-2).join("."),Te=C(m),Qe=Te?.name&&si(Te)?{name:Te.name}:void 0,Ke=`/tmp/ts-cloud-move-${L}-${N}.sql.gz`,Ye=El(L,N),et=Me?.certsDir??Mr,Pt=[Se,...A.aliases??[]].filter((ht)=>!!ht),Bt=Te?.engine??"mysql",vt={runOnSource:(ht)=>mo(we,be,ht),runOnTarget:(ht)=>mo(we,ne,ht),archiveStaged:async()=>(await we.exec(ne,`test -f ${De} && echo staged || true`)).stdout.includes("staged"),transferArchive:async()=>{let ht=`${process.cwd()}/.ts-cloud-move-${L}-${N}.tar.gz`;await ko(be,`${be.sshUser}@${be.endpoint}:${De}`,ht),await ko(ne,ht,`${ne.sshUser}@${ne.endpoint}:${De}`),await Bun.file(ht).delete().catch(()=>{})},targetRoutesSite:async()=>{if(!Me||!Se)return!0;return(await we.exec(ne,`grep -qF ${JSON.stringify(Se)} /etc/rpx/sites.d/${L}.json 2>/dev/null && echo routed || true`)).stdout.includes("routed")},refreshTargetGateway:async()=>{if(!Me)throw Error(`This project does not run the rpx gateway, so the move cannot publish a route. Point ${ne.name}'s web server at ${Ce}/current yourself, then re-run to continue.`);await mo(we,ne,Ve({config:fe(m.sites??{},{proxy:Me,slug:L,lan:_i(m)}),slug:L,preserveManagementDashboardRoutes:!0}).join(`
83
+ `))},publishedAddress:async()=>{if(!Se||!xe)return ne.endpoint;let ht=ir(xe);if(!ht)return;return await Ni(O(ht),Ee(Se),Se,ne.endpoint,"A")?ne.endpoint:void 0},...Qe?{database:{dump:async()=>{await mo(we,be,["set -euo pipefail",'eval "$(cd /opt/pantry && pantry env 2>/dev/null)" || true',...Bo(Bt,Qe.name,"/tmp",Te),`mv -f "$(ls -1t /tmp/${Qe.name}-*.sql.gz | head -1)" ${Ke}`].join(`
84
+ `))},dumpStaged:async()=>(await we.exec(ne,`test -s ${Ke} && echo staged || true`)).stdout.includes("staged"),transferDump:async()=>{let ht=`${process.cwd()}/.ts-cloud-move-${L}-${N}.sql.gz`;await ko(be,`${be.sshUser}@${be.endpoint}:${Ke}`,ht),await ko(ne,ht,`${ne.sshUser}@${ne.endpoint}:${Ke}`),await Bun.file(ht).delete().catch(()=>{})},restore:async()=>{await mo(we,ne,[...yt(Te,m.infrastructure?.compute?.managedServices??{}),...ai(Te,{from:Ke}),`rm -f ${Ke}`].join(`
85
+ `))},targetHasData:async()=>{let ht=Bt==="postgres"?`psql -tAc "select count(*) from information_schema.tables where table_schema='public'" -d ${Qe.name} 2>/dev/null || echo 0`:`mysql -N -B -e "select count(*) from information_schema.tables where table_schema='${Qe.name}'" 2>/dev/null || echo 0`,Vt=await we.exec(ne,`eval "$(cd /opt/pantry && pantry env 2>/dev/null)" || true
86
+ ${ht}`);return Number.parseInt(Vt.stdout.trim().split(`
87
+ `).pop()??"0",10)>0}}}:{},...Me?{certificates:{inPlace:async()=>{let ht=Tl(et,Pt),[Vt,nn]=await Promise.all([we.exec(be,ht),we.exec(ne,ht)]);return Nl(xa(Vt.stdout),xa(nn.stdout))},carry:async()=>{await mo(we,be,Ol(et,Pt,Ye));let ht=`${process.cwd()}/.ts-cloud-move-${L}-${N}-certs.tar.gz`;if(!(await we.exec(be,`test -s ${Ye} && echo staged || true`)).stdout.includes("staged"))return;await ko(be,`${be.sshUser}@${be.endpoint}:${Ye}`,ht),await ko(ne,ht,`${ne.sshUser}@${ne.endpoint}:${Ye}`),await Bun.file(ht).delete().catch(()=>{}),await mo(we,ne,Ll(et,Ye)),await we.exec(be,`rm -f ${Ye}`)}}}:{},cutoverDns:async()=>{if(!Se)return[];if(!xe)return[`No DNS provider configured — point ${Se} at ${ne.endpoint} manually.`];let ht=ir(xe);if(!ht)return[`DNS provider '${xe}' is not configured.`];return(await zn({provider:O(ht),zone:Ee(Se),fqdn:Se,ipv4:ne.endpoint,ipv6:le(void 0)})).warnings}},Ot=await jl({slug:L,siteName:N,appBase:Ce,from:be.name,to:ne.name,targetAddress:ne.endpoint,port:A.port,healthCheckPath:A.healthCheck?.path,database:Qe},vt),an=await Is(Ot);if(!t.apply){if(t.json){console.log(JSON.stringify({schemaVersion:1,operation:Ot.operation,target:Ot.target,move:{site:N,from:be.name,to:ne.name,address:ne.endpoint},steps:an.map((ht)=>({id:ht.step.id,title:ht.step.title,state:ht.state,change:ht.step.change,reason:ht.reason}))},null,2));return}for(let ht of Ps(Ot,an))console.log(ht);console.log(` The source keeps its files either way — reversible until ${be.name} is destroyed.`),console.log(" Re-run with --apply to perform it.");return}let zt=await xs(Ot,an,{log:(ht)=>e(ht),audit:(ht)=>j.store.appendEvent({projectId:j.project.id,type:`${ht.operation}.${ht.step}.${ht.state}`,level:ht.state==="failed"?"error":"info",payload:{site:N,from:be.name,to:ne.name,...ht.error?{error:ht.error}:{}}})});if(t.json)console.log(JSON.stringify({schemaVersion:1,outcome:zt},null,2));if(!zt.success){let ht=zt.steps.find((Vt)=>Vt.state==="failed");throw Error(`${Ot.operation} stopped at '${ht?.title}': ${ht?.error}. ${be.name} still has the site and its files; re-run the same command to continue from here.`)}if(!t.json)n(`Moved ${N} from ${be.name} to ${ne.name}. ${be.name} keeps its copy until you destroy it.`)}finally{j.store.close()}}function Na(N){N.command("site:move <name>","Move a deployed site to another server").option("--to <server>","Target server (enrolled name or id)").option("--from <server>","Source server; inferred when only one other is enrolled").option("--apply","Perform the reviewed move plan").option("--json","Print structured JSON").action(async(t,m)=>{try{await cp(t,m)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("site:add <name>","Add a site entry to cloud.config.ts").option("--config <path>","Path to cloud config file").option("--root <path>","Directory to deploy").option("--domain <domain>","Domain for the site").option("--path <path>","Path prefix for the site").option("--deploy <target>","Deployment target: server or bucket").option("--build <command>","Build command to run before deployment").option("--start <command>","Start command for server apps").option("--port <port>","Port for server apps").option("--type <type>","Site type, e.g. static, laravel, php").option("--path-rewrite-style <style>","Static rewrite style: directory or flat").option("--dry-run","Print the updated config without writing").action(async(t,m)=>{let A=m?.config||"cloud.config.ts",L=m?.root;if(!L){r("Missing --root <path>");return}if(!op(A)){r(`Config file not found: ${A}`);return}try{let j=pn({configText:await sp(A,"utf8"),name:t,root:L,domain:m?.domain,path:m?.path,deploy:lp(m?.deploy),build:m?.build,start:m?.start,port:up(m?.port),type:m?.type,pathRewriteStyle:dp(m?.pathRewriteStyle)});if(m?.dryRun){console.log(j);return}await ap(A,j),n(`Added site '${t}' to ${A}`)}catch(j){r(j instanceof Error?j.message:String(j))}})}function lp(N){if(!N)return;if(N==="bucket"||N==="server")return N;throw Error(`Invalid --deploy '${N}'. Expected 'server' or 'bucket'.`)}function dp(N){if(!N)return;if(N==="directory"||N==="flat")return N;throw Error(`Invalid --path-rewrite-style '${N}'. Expected 'directory' or 'flat'.`)}function up(N){if(!N)return;let t=Number.parseInt(N,10);if(!Number.isInteger(t)||t<1||t>65535)throw Error(`Invalid --port '${N}'. Expected a TCP port between 1 and 65535.`);return t}import{existsSync as hp}from"node:fs";import{mkdir as yp,writeFile as vp}from"node:fs/promises";import{join as bp}from"node:path";class Io{builder;config;environment;mergedConfig;serverEipLogicalIds=new Map;storageFailoverReplicas=[];constructor(N){this.config=N.config,this.environment=N.environment,this.builder=new To(`${this.config.project.name} - ${this.environment}`),this.mergedConfig=this.mergeEnvironmentConfig()}mergeEnvironmentConfig(){let t=this.config.environments[this.environment]?.infrastructure;if(!t)return this.config;return{...this.config,infrastructure:{...this.config.infrastructure,...t,compute:{...this.config.infrastructure?.compute,...t.compute},storage:{...this.config.infrastructure?.storage,...t.storage},functions:{...this.config.infrastructure?.functions,...t.functions},servers:{...this.config.infrastructure?.servers,...t.servers},databases:{...this.config.infrastructure?.databases,...t.databases},cdn:{...this.config.infrastructure?.cdn,...t.cdn},sftp:t.sftp??this.config.infrastructure?.sftp,queues:{...this.config.infrastructure?.queues,...t.queues},redirects:{...this.config.infrastructure?.redirects,...t.redirects},realtime:{...this.config.infrastructure?.realtime,...t.realtime},cache:{...this.config.infrastructure?.cache,...t.cache},fileSystem:{...this.config.infrastructure?.fileSystem,...t.fileSystem},email:{...this.config.infrastructure?.email,...t.email},search:{...this.config.infrastructure?.search,...t.search},ai:{...this.config.infrastructure?.ai,...t.ai}}}}shouldDeploy(N){if(N.environments&&!N.environments.includes(this.environment))return!1;if(N.requiresFeatures){let t=this.config.features||{};if(!N.requiresFeatures.every((A)=>t[A]===!0))return!1}if(N.regions){let t=this.config.environments[this.environment]?.region||this.config.project.region;if(!N.regions.includes(t))return!1}if(N.condition&&typeof N.condition==="function")return N.condition(this.config,this.environment);return!0}resolveApiOriginPort(){let N=this.mergedConfig.infrastructure?.api?.port??this.mergedConfig.ports?.api,t=Number(N||3008);return Number.isFinite(t)&&t>0?t:3008}defaultComputeCachePathPatterns(){return["/api/*","/auth/*","/publisher/api/*","/publisher/*","/publish","/publish/*","/analytics/*","/packages/*","/npm/*","/zig/*","/php/*","/commits/*","/health","/dashboard/*","/search","/login","/logout","/signup","/account","/account/*"]}shouldRouteStorageBucketToCompute(N,t){return N==="public"||t.routeCompute===!0}storageBucketName(N,t,m){return this.mergedConfig.infrastructure?.storage?.[m]?.bucket??`${N}-${t}-${m}`}fileSystemLogicalId(N,t,m){return s(l({slug:`${N}-${m}`,environment:t,resourceType:"efs"}))}resolveSftpStorage(N,t,m){let A=N.storage;if(A?.type==="efs"&&A.fileSystem){if(!this.mergedConfig.infrastructure?.fileSystem?.[A.fileSystem])throw Error(`sftp: storage.fileSystem "${A.fileSystem}" is not defined in infrastructure.fileSystem`);return{type:"efs",fileSystemId:{Ref:this.fileSystemLogicalId(t,m,A.fileSystem)},posixProfile:A.posixProfile}}if(A?.type==="s3"&&A.storageBucket){if(!this.mergedConfig.infrastructure?.storage?.[A.storageBucket])throw Error(`sftp: storage.storageBucket "${A.storageBucket}" is not defined in infrastructure.storage`);return{type:"s3",bucket:this.storageBucketName(t,m,A.storageBucket)}}return}resolveComputeCachePathPatterns(N){if(N&&N.length>0)return N;return this.defaultComputeCachePathPatterns()}createComputeCacheBehavior(N,t){return{PathPattern:N,TargetOriginId:t,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS","PUT","POST","PATCH","DELETE"],CachedMethods:["GET","HEAD"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",OriginRequestPolicyId:"b689b0a8-53d0-40ab-baf2-68738e2966ac"}}appendComputeAppOrigin(N,t,m,A,L,j){let G=this.serverEipLogicalIds.get("app"),Z=this.serverEipLogicalIds.get("appInstance");if(!G)return;let ee=`EC2-${A}-${L}-api`,ne=this.mergedConfig.infrastructure?.servers?.app?.region||this.mergedConfig.project?.region||"us-east-1",oe=ne==="us-east-1"?".compute-1.amazonaws.com":`.${ne}.compute.amazonaws.com`,be={"Fn::Join":["",["ec2-",{"Fn::Join":["-",{"Fn::Split":[".",{Ref:G}]}]},oe]]},we=this.resolveApiOriginPort();if(N.push({Id:ee,DomainName:be,CustomOriginConfig:{HTTPPort:we,HTTPSPort:443,OriginProtocolPolicy:"http-only",OriginSSLProtocols:["TLSv1.2"]}}),m.push(G),Z)m.push(Z);for(let Ce of j)t.push(this.createComputeCacheBehavior(Ce,ee))}buildCaddyfile(N){let t=N.filter(([,L])=>typeof L.domain==="string"&&L.domain&&typeof L.port==="number");if(t.length===0)return;let m=new Map;for(let[,L]of t){let j=m.get(L.domain)??[];j.push({port:L.port,path:L.path}),m.set(L.domain,j)}let A=[];for(let[L,j]of m){let Z=[...j].sort((ee,ne)=>{let oe=!ee.path||ee.path==="/",be=!ne.path||ne.path==="/";if(oe&&!be)return 1;if(!oe&&be)return-1;return(ne.path?.length??0)-(ee.path?.length??0)}).map((ee)=>{let ne=!ee.path||ee.path==="/",oe=`reverse_proxy localhost:${ee.port}`;return ne?` handle {
88
88
  ${oe}
89
89
  }`:` handle ${ee.path} {
90
90
  ${oe}
@@ -93,11 +93,11 @@ ${Z.join(`
93
93
  `)}
94
94
  }`)}return A.join(`
95
95
 
96
- `)}normalizeMountPath(N){let t=N?.mountPath||N?.path;if(!t||t==="/")return;let p=`/${t}`.replace(/\/+/g,"/").replace(/\/$/,"");return p==="/"?void 0:p}storageBucketLogicalId(N,t,p){return`${N}-${t}-s3-${p}`.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((A)=>A.charAt(0).toUpperCase()+A.slice(1)).join("")}pathMountRewriteFunctionCode(N,t="directory"){return`function handler(event) { var request = event.request; var prefix = ${JSON.stringify(N)}; var rewriteStyle = ${JSON.stringify(t)}; var uri = request.uri; if (uri === prefix) { uri = '/'; } else if (uri.indexOf(prefix + '/') === 0) { uri = uri.substring(prefix.length); } if (uri === '' || uri === '/') { request.uri = '/index.html'; return request; } if (uri.endsWith('/')) { request.uri = uri + 'index.html'; return request; } var lastSegment = uri.substring(uri.lastIndexOf('/') + 1); if (lastSegment.indexOf('.') === -1) { request.uri = rewriteStyle === 'flat' ? uri + '.html' : uri + '/index.html'; return request; } request.uri = uri; return request; }`}generate(){let N=this.mergedConfig.project.slug,t=this.environment,p=!!(this.mergedConfig.infrastructure?.functions&&Object.keys(this.mergedConfig.infrastructure.functions).length>0||this.mergedConfig.infrastructure?.api),A=!!(this.mergedConfig.infrastructure?.servers&&Object.keys(this.mergedConfig.infrastructure.servers).length>0),L=!!this.mergedConfig.infrastructure?.compute;if(!!((this.mergedConfig.mode||"server")==="serverless"&&this.mergedConfig.infrastructure?.containers&&Object.keys(this.mergedConfig.infrastructure.containers).length>0))this.generateNetworkInfrastructure(N,t),this.generateContainerInfrastructure(N,t);if(p)this.generateServerless(N,t);if(A)this.generateServer(N,t);if(L)this.generateComputeApp(N,t);if(this.mergedConfig.infrastructure?.jumpBox)this.generateJumpBox(N,t);if(this.generateSharedInfrastructure(N,t),this.config.tags)this.applyGlobalTags(this.config.tags);return this}applyGlobalTags(N){}generateNetworkInfrastructure(N,t){if(this.builder.hasResource("VPC"))return;let A=this.mergedConfig.infrastructure?.network?.cidr||"10.0.0.0/16",{vpc:L,logicalId:j}=_.createVpc({slug:N,environment:t,cidr:A,enableDnsHostnames:!0,enableDnsSupport:!0});this.builder.addResource("VPC",L);let G=`${N}${t}IGW`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(G,{Type:"AWS::EC2::InternetGateway",Properties:{Tags:[{Key:"Name",Value:`${N}-${t}-igw`},{Key:"Environment",Value:t}]}});let Z=`${N}${t}IGWAttach`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Z,{Type:"AWS::EC2::VPCGatewayAttachment",Properties:{VpcId:{Ref:"VPC"},InternetGatewayId:{Ref:G}}});let ee=`${N}${t}PublicRT`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(ee,{Type:"AWS::EC2::RouteTable",Properties:{VpcId:{Ref:"VPC"},Tags:[{Key:"Name",Value:`${N}-${t}-public-rt`},{Key:"Environment",Value:t}]}}),this.builder.addResource(`${N}${t}PublicRoute`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::EC2::Route",Properties:{RouteTableId:{Ref:ee},DestinationCidrBlock:"0.0.0.0/0",GatewayId:{Ref:G}},DependsOn:Z});let re=this.mergedConfig.environments[t]?.region||this.mergedConfig.project.region||"us-east-1",oe=["a","b"];for(let be=0;be<2;be++){let we=`PublicSubnet${be+1}`;this.builder.addResource(we,{Type:"AWS::EC2::Subnet",Properties:{VpcId:{Ref:"VPC"},CidrBlock:`10.0.${be}.0/24`,AvailabilityZone:`${re}${oe[be]}`,MapPublicIpOnLaunch:!0,Tags:[{Key:"Name",Value:`${N}-${t}-public-${oe[be]}`},{Key:"Environment",Value:t}]}}),this.builder.addResource(`${we}RTAssoc`,{Type:"AWS::EC2::SubnetRouteTableAssociation",Properties:{SubnetId:{Ref:we},RouteTableId:{Ref:ee}}})}}generateContainerInfrastructure(N,t){let p=this.mergedConfig.infrastructure?.containers;if(!p)return;let A=this.mergedConfig.infrastructure?.loadBalancer,L=this.mergedConfig.infrastructure?.ssl,j=this.mergedConfig.infrastructure?.dns,G;if(L?.enabled&&L.domains?.length)if(L.certificateArn);else{let oe=L.domains[0],be=L.domains.slice(1).map((Ee)=>{if(Ee.includes(".")&&Ee.endsWith(oe))return Ee.replace(`.${oe}`,"");return Ee}),{certificate:we,logicalId:Ce}=B.createCertificate({domain:oe,subdomains:be,slug:N,environment:t,validationMethod:"DNS",hostedZoneId:j?.hostedZoneId});G=Ce,this.builder.addResource(Ce,we)}let Z="ECSCluster";this.builder.addResource(Z,{Type:"AWS::ECS::Cluster",Properties:{ClusterName:`${N}-${t}`,ClusterSettings:[{Name:"containerInsights",Value:"enabled"}],Tags:[{Key:"Name",Value:`${N}-${t}`},{Key:"Environment",Value:t}]}});let ee=`${N}${t}ALBSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(ee,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ALB security group for ${N}-${t}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:80,ToPort:80,CidrIp:"0.0.0.0/0",Description:"HTTP"},{IpProtocol:"tcp",FromPort:443,ToPort:443,CidrIp:"0.0.0.0/0",Description:"HTTPS"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${N}-${t}-alb-sg`},{Key:"Environment",Value:t}]}});let re=`${N}${t}ECSSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");for(let[oe,be]of Object.entries(p)){let we=be.port||3000;this.builder.addResource(re,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ECS tasks security group for ${N}-${t}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:we,ToPort:we,SourceSecurityGroupId:{Ref:ee},Description:"Allow traffic from ALB"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${N}-${t}-ecs-sg`},{Key:"Environment",Value:t}]}});let Ce=`${N}${t}TaskExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ce,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-ecs-exec-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"],Policies:[{PolicyName:"ECRPullPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["ecr:GetAuthorizationToken","ecr:BatchCheckLayerAvailability","ecr:GetDownloadUrlForLayer","ecr:BatchGetImage","logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"}]}}]}});let Ee=`${N}${t}TaskRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ee,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-ecs-task-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},Policies:[{PolicyName:"TaskPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"},{Effect:"Allow",Action:["s3:GetObject","s3:PutObject","s3:ListBucket"],Resource:"*"}]}}]}});let Se=`${N}${t}${oe}LogGroup`.replace(/[^a-zA-Z0-9]/g,""),De=`/ecs/${N}-${t}-${oe}`;this.builder.addResource(Se,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:De,RetentionInDays:30}});let _e=`${N}${t}${oe}TaskDef`.replace(/[^a-zA-Z0-9]/g,""),Ae=String(be.cpu||512),Te=String(be.memory||1024);this.builder.addResource(_e,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${N}-${t}-${oe}`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:Ae,Memory:Te,ExecutionRoleArn:{"Fn::GetAtt":[Ce,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[Ee,"Arn"]},ContainerDefinitions:[{Name:oe,Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${N}:latest`},Essential:!0,PortMappings:[{ContainerPort:we,Protocol:"tcp"}],LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":De,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":oe}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${we}${be.healthCheck||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${N}-${t}-${oe}`},{Key:"Environment",Value:t}]},DependsOn:[Ce,Ee,Se]});let Ne=`${N}${t}ALB`.replace(/[^a-zA-Z0-9]/g,"");if(A?.enabled!==!1){this.builder.addResource(Ne,{Type:"AWS::ElasticLoadBalancingV2::LoadBalancer",Properties:{Name:`${N}-${t}-alb`,Scheme:"internet-facing",Type:"application",Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],SecurityGroups:[{Ref:ee}],Tags:[{Key:"Name",Value:`${N}-${t}-alb`},{Key:"Environment",Value:t}]}});let Xe=`${N}${t}TargetGroup`.replace(/[^a-zA-Z0-9]/g,""),Ge=A?.healthCheck?.path||be.healthCheck||"/health";this.builder.addResource(Xe,{Type:"AWS::ElasticLoadBalancingV2::TargetGroup",Properties:{Name:`${N}-${t}-tg`,Port:we,Protocol:"HTTP",VpcId:{Ref:"VPC"},TargetType:"ip",HealthCheckPath:Ge,HealthCheckIntervalSeconds:A?.healthCheck?.interval||30,HealthyThresholdCount:A?.healthCheck?.healthyThreshold||2,UnhealthyThresholdCount:A?.healthCheck?.unhealthyThreshold||5,HealthCheckTimeoutSeconds:10,Tags:[{Key:"Name",Value:`${N}-${t}-tg`},{Key:"Environment",Value:t}]}});let Ye=`${N}${t}HTTPListener`.replace(/[^a-zA-Z0-9]/g,"");if(L?.enabled&&L?.redirectHttp)this.builder.addResource(Ye,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:Ne},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"redirect",RedirectConfig:{Protocol:"HTTPS",Port:"443",StatusCode:"HTTP_301"}}]}});else this.builder.addResource(Ye,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:Ne},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:Xe}}]}});if(L?.enabled){let Ot=L.certificateArn||(G?{Ref:G}:void 0);if(Ot){let Ut=`${N}${t}HTTPSListener`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ut,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:Ne},Port:443,Protocol:"HTTPS",Certificates:[{CertificateArn:Ot}],DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:Xe}}],SslPolicy:"ELBSecurityPolicy-TLS13-1-2-2021-06"}})}}let et=`${N}${t}${oe}Service`.replace(/[^a-zA-Z0-9]/g,""),Pt=be.desiredCount||1;this.builder.addResource(et,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${N}-${t}-${oe}`,Cluster:{Ref:Z},TaskDefinition:{Ref:_e},DesiredCount:Pt,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:re}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},LoadBalancers:[{ContainerName:oe,ContainerPort:we,TargetGroupArn:{Ref:Xe}}],HealthCheckGracePeriodSeconds:120,Tags:[{Key:"Name",Value:`${N}-${t}-${oe}`},{Key:"Environment",Value:t}]},DependsOn:[_e,Ye,Xe]});let wt=be.autoScaling;if(wt){let Ot=`${N}${t}${oe}ScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(Ot,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:wt.max||10,MinCapacity:wt.min||1,ResourceId:{"Fn::Sub":`service/\${${Z}}/${N}-${t}-${oe}`},ScalableDimension:"ecs:service:DesiredCount",ServiceNamespace:"ecs",RoleARN:{"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"}},DependsOn:et}),wt.targetCpuUtilization)this.builder.addResource(`${N}${t}${oe}CPUScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${N}-${t}-${oe}-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:Ot},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:wt.targetCpuUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}});if(wt.targetMemoryUtilization)this.builder.addResource(`${N}${t}${oe}MemoryScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${N}-${t}-${oe}-memory-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:Ot},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageMemoryUtilization"},TargetValue:wt.targetMemoryUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(j?.domain&&j?.hostedZoneId)this.builder.addResource(`${N}${t}ApiDnsRecord`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:j.hostedZoneId,Name:`api.${j.domain}`,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[Ne,"DNSName"]},HostedZoneId:{"Fn::GetAtt":[Ne,"CanonicalHostedZoneID"]}}}});this.builder.addOutput("ECSClusterArn",{Description:"ECS Cluster ARN",Value:{"Fn::GetAtt":[Z,"Arn"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-cluster-arn"}}}),this.builder.addOutput("ECSServiceName",{Description:"ECS Service Name",Value:`${N}-${t}-${oe}`,Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-service-name"}}}),this.builder.addOutput("LoadBalancerDNS",{Description:"Application Load Balancer DNS Name",Value:{"Fn::GetAtt":[Ne,"DNSName"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-alb-dns"}}})}else{let Xe=`${N}${t}${oe}Service`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Xe,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${N}-${t}-${oe}`,Cluster:{Ref:Z},TaskDefinition:{Ref:_e},DesiredCount:be.desiredCount||1,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:re}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},Tags:[{Key:"Name",Value:`${N}-${t}-${oe}`},{Key:"Environment",Value:t}]},DependsOn:[_e]})}break}}generateServerless(N,t){if(this.mergedConfig.infrastructure?.functions)for(let[p,A]of Object.entries(this.mergedConfig.infrastructure.functions)){if(!this.shouldDeploy(A))continue;let{role:L,logicalId:j}=ve.createRole({slug:N,environment:t,roleName:`${N}-${t}-${p}-role`,servicePrincipal:"lambda.amazonaws.com",managedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]});this.builder.addResource(j,L);let{lambdaFunction:G,logicalId:Z}=S.createLambdaFunction({slug:N,environment:t,functionName:`${N}-${t}-${p}`,handler:A.handler||"index.handler",runtime:A.runtime||"nodejs20.x",code:{zipFile:A.code||"export const handler = async () => ({ statusCode: 200 });"},role:j,timeout:A.timeout,memorySize:A.memorySize});this.builder.addResource(Z,G)}if(this.mergedConfig.infrastructure?.api){let{restApi:p,logicalId:A}=wo.createRestApi({slug:N,environment:t,apiName:`${N}-${t}-api`});this.builder.addResource(A,p)}}generateServer(N,t){if(!this.mergedConfig.infrastructure?.servers)return;let p=this.mergedConfig.infrastructure.servers,A=this.mergedConfig.infrastructure?.compute,L=this.mergedConfig.infrastructure?.ssl;if(Object.values(p).some((G)=>!G.privateNetwork||G.privateNetwork==="create"))this.generateNetworkInfrastructure(N,t);for(let[G,Z]of Object.entries(p)){let ee=Z.instanceType||Z.size||A?.size,oe=(ee?S.InstanceSize.specs[ee]:void 0)?.instanceType||ee||"t3.micro",be=Z.type||"app",we=Z.userData||Z.startupScript;if(!we)we=S.UserData.generateAppServerScript({runtime:"bun",runtimeVersion:Z.bunVersion||"latest",webServer:"none",domain:Z.domain,enableSsl:!!L?.enabled,sslEmail:L?.letsEncrypt?.email,installRedis:be==="cache",installDatabaseClients:!!Z.database});let Ce=Z.privateNetwork&&Z.privateNetwork!=="create"?Z.privateNetwork:{Ref:"VPC"},Ee=Z.subnet||{Ref:"PublicSubnet1"},De=!!this.mergedConfig.infrastructure?.email?.server?.enabled,_e=[22,80,443,this.resolveApiOriginPort()];if(De)_e.push(25,465,587,143,993);let Ae=S.createServerModeStack({slug:`${N}-${G}`,environment:t,vpcId:Ce,subnetId:Ee,instanceType:oe,keyName:Z.keyName||`${N}-${t}`,domain:Z.domain,userData:we,volumeSize:Z.diskSize||20,imageId:Z.image,allowedPorts:_e});for(let[Te,Ne]of Object.entries(Ae.resources))this.builder.addResource(Te,Ne);this.serverEipLogicalIds.set(G,Ae.outputs.eipLogicalId),this.serverEipLogicalIds.set(`${G}Instance`,Ae.outputs.instanceLogicalId),this.builder.addOutput(`${G}InstanceId`,{Value:{Ref:Ae.outputs.instanceLogicalId},Description:`Instance ID for ${G} server`}),this.builder.addOutput(`${G}PublicIp`,{Value:{Ref:Ae.outputs.eipLogicalId},Description:`Public IP for ${G} server`})}}generateComputeApp(N,t){let p=this.mergedConfig.infrastructure?.compute;if(!p)return;let A=this.mergedConfig.sites||{},L=Object.entries(A),G=this.mergedConfig.infrastructure?.dns?.domain,Z=this.mergedConfig.infrastructure?.database,ee=p.size,oe=(ee?S.InstanceSize.specs[ee]:void 0)?.instanceType||ee||"t3.micro";if(!this.builder.hasResource("VPC"))this.generateNetworkInfrastructure(N,t);let be=this.buildCaddyfile(L),we=S.UserData.generateBunAppScript({runtime:p.runtime||"bun",runtimeVersion:p.runtimeVersion||"latest",systemPackages:p.systemPackages,database:Z,caddyfile:be}),Ce=Ri(p.appUpdates),Ee=Ce.length>0?`${we}
96
+ `)}normalizeMountPath(N){let t=N?.mountPath||N?.path;if(!t||t==="/")return;let m=`/${t}`.replace(/\/+/g,"/").replace(/\/$/,"");return m==="/"?void 0:m}storageBucketLogicalId(N,t,m){return`${N}-${t}-s3-${m}`.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((A)=>A.charAt(0).toUpperCase()+A.slice(1)).join("")}pathMountRewriteFunctionCode(N,t="directory"){return`function handler(event) { var request = event.request; var prefix = ${JSON.stringify(N)}; var rewriteStyle = ${JSON.stringify(t)}; var uri = request.uri; if (uri === prefix) { uri = '/'; } else if (uri.indexOf(prefix + '/') === 0) { uri = uri.substring(prefix.length); } if (uri === '' || uri === '/') { request.uri = '/index.html'; return request; } if (uri.endsWith('/')) { request.uri = uri + 'index.html'; return request; } var lastSegment = uri.substring(uri.lastIndexOf('/') + 1); if (lastSegment.indexOf('.') === -1) { request.uri = rewriteStyle === 'flat' ? uri + '.html' : uri + '/index.html'; return request; } request.uri = uri; return request; }`}generate(){let N=this.mergedConfig.project.slug,t=this.environment,m=!!(this.mergedConfig.infrastructure?.functions&&Object.keys(this.mergedConfig.infrastructure.functions).length>0||this.mergedConfig.infrastructure?.api),A=!!(this.mergedConfig.infrastructure?.servers&&Object.keys(this.mergedConfig.infrastructure.servers).length>0),L=!!this.mergedConfig.infrastructure?.compute;if(!!((this.mergedConfig.mode||"server")==="serverless"&&this.mergedConfig.infrastructure?.containers&&Object.keys(this.mergedConfig.infrastructure.containers).length>0))this.generateNetworkInfrastructure(N,t),this.generateContainerInfrastructure(N,t);if(m)this.generateServerless(N,t);if(A)this.generateServer(N,t);if(L)this.generateComputeApp(N,t);if(this.mergedConfig.infrastructure?.jumpBox)this.generateJumpBox(N,t);if(this.storageFailoverReplicas=[],this.generateSharedInfrastructure(N,t),this.storageFailoverReplicas.length>0)this.builder.addMetadata(Ao,this.storageFailoverReplicas);if(this.config.tags)this.applyGlobalTags(this.config.tags);return this}getStorageFailoverReplicas(){return[...this.storageFailoverReplicas]}applyGlobalTags(N){}generateNetworkInfrastructure(N,t){if(this.builder.hasResource("VPC"))return;let A=this.mergedConfig.infrastructure?.network?.cidr||"10.0.0.0/16",{vpc:L,logicalId:j}=x.createVpc({slug:N,environment:t,cidr:A,enableDnsHostnames:!0,enableDnsSupport:!0});this.builder.addResource("VPC",L);let G=`${N}${t}IGW`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(G,{Type:"AWS::EC2::InternetGateway",Properties:{Tags:[{Key:"Name",Value:`${N}-${t}-igw`},{Key:"Environment",Value:t}]}});let Z=`${N}${t}IGWAttach`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Z,{Type:"AWS::EC2::VPCGatewayAttachment",Properties:{VpcId:{Ref:"VPC"},InternetGatewayId:{Ref:G}}});let ee=`${N}${t}PublicRT`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(ee,{Type:"AWS::EC2::RouteTable",Properties:{VpcId:{Ref:"VPC"},Tags:[{Key:"Name",Value:`${N}-${t}-public-rt`},{Key:"Environment",Value:t}]}}),this.builder.addResource(`${N}${t}PublicRoute`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::EC2::Route",Properties:{RouteTableId:{Ref:ee},DestinationCidrBlock:"0.0.0.0/0",GatewayId:{Ref:G}},DependsOn:Z});let ne=this.mergedConfig.environments[t]?.region||this.mergedConfig.project.region||"us-east-1",oe=["a","b"];for(let be=0;be<2;be++){let we=`PublicSubnet${be+1}`;this.builder.addResource(we,{Type:"AWS::EC2::Subnet",Properties:{VpcId:{Ref:"VPC"},CidrBlock:`10.0.${be}.0/24`,AvailabilityZone:`${ne}${oe[be]}`,MapPublicIpOnLaunch:!0,Tags:[{Key:"Name",Value:`${N}-${t}-public-${oe[be]}`},{Key:"Environment",Value:t}]}}),this.builder.addResource(`${we}RTAssoc`,{Type:"AWS::EC2::SubnetRouteTableAssociation",Properties:{SubnetId:{Ref:we},RouteTableId:{Ref:ee}}})}}generateContainerInfrastructure(N,t){let m=this.mergedConfig.infrastructure?.containers;if(!m)return;let A=this.mergedConfig.infrastructure?.loadBalancer,L=this.mergedConfig.infrastructure?.ssl,j=this.mergedConfig.infrastructure?.dns,G;if(L?.enabled&&L.domains?.length)if(L.certificateArn);else{let oe=L.domains[0],be=L.domains.slice(1).map((De)=>{if(De.includes(".")&&De.endsWith(oe))return De.replace(`.${oe}`,"");return De}),{certificate:we,logicalId:Ce}=B.createCertificate({domain:oe,subdomains:be,slug:N,environment:t,validationMethod:"DNS",hostedZoneId:j?.hostedZoneId});G=Ce,this.builder.addResource(Ce,we)}let Z="ECSCluster";this.builder.addResource(Z,{Type:"AWS::ECS::Cluster",Properties:{ClusterName:`${N}-${t}`,ClusterSettings:[{Name:"containerInsights",Value:"enabled"}],Tags:[{Key:"Name",Value:`${N}-${t}`},{Key:"Environment",Value:t}]}});let ee=`${N}${t}ALBSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(ee,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ALB security group for ${N}-${t}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:80,ToPort:80,CidrIp:"0.0.0.0/0",Description:"HTTP"},{IpProtocol:"tcp",FromPort:443,ToPort:443,CidrIp:"0.0.0.0/0",Description:"HTTPS"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${N}-${t}-alb-sg`},{Key:"Environment",Value:t}]}});let ne=`${N}${t}ECSSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");for(let[oe,be]of Object.entries(m)){let we=be.port||3000;this.builder.addResource(ne,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ECS tasks security group for ${N}-${t}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:we,ToPort:we,SourceSecurityGroupId:{Ref:ee},Description:"Allow traffic from ALB"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${N}-${t}-ecs-sg`},{Key:"Environment",Value:t}]}});let Ce=`${N}${t}TaskExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ce,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-ecs-exec-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"],Policies:[{PolicyName:"ECRPullPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["ecr:GetAuthorizationToken","ecr:BatchCheckLayerAvailability","ecr:GetDownloadUrlForLayer","ecr:BatchGetImage","logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"}]}}]}});let De=`${N}${t}TaskRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(De,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-ecs-task-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},Policies:[{PolicyName:"TaskPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"},{Effect:"Allow",Action:["s3:GetObject","s3:PutObject","s3:ListBucket"],Resource:"*"}]}}]}});let Se=`${N}${t}${oe}LogGroup`.replace(/[^a-zA-Z0-9]/g,""),xe=`/ecs/${N}-${t}-${oe}`;this.builder.addResource(Se,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:xe,RetentionInDays:30}});let Me=`${N}${t}${oe}TaskDef`.replace(/[^a-zA-Z0-9]/g,""),Ae=String(be.cpu||512),Ee=String(be.memory||1024);this.builder.addResource(Me,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${N}-${t}-${oe}`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:Ae,Memory:Ee,ExecutionRoleArn:{"Fn::GetAtt":[Ce,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[De,"Arn"]},ContainerDefinitions:[{Name:oe,Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${N}:latest`},Essential:!0,PortMappings:[{ContainerPort:we,Protocol:"tcp"}],LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":xe,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":oe}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${we}${be.healthCheck||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${N}-${t}-${oe}`},{Key:"Environment",Value:t}]},DependsOn:[Ce,De,Se]});let Te=`${N}${t}ALB`.replace(/[^a-zA-Z0-9]/g,"");if(A?.enabled!==!1){this.builder.addResource(Te,{Type:"AWS::ElasticLoadBalancingV2::LoadBalancer",Properties:{Name:`${N}-${t}-alb`,Scheme:"internet-facing",Type:"application",Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],SecurityGroups:[{Ref:ee}],Tags:[{Key:"Name",Value:`${N}-${t}-alb`},{Key:"Environment",Value:t}]}});let Qe=`${N}${t}TargetGroup`.replace(/[^a-zA-Z0-9]/g,""),Ke=A?.healthCheck?.path||be.healthCheck||"/health";this.builder.addResource(Qe,{Type:"AWS::ElasticLoadBalancingV2::TargetGroup",Properties:{Name:`${N}-${t}-tg`,Port:we,Protocol:"HTTP",VpcId:{Ref:"VPC"},TargetType:"ip",HealthCheckPath:Ke,HealthCheckIntervalSeconds:A?.healthCheck?.interval||30,HealthyThresholdCount:A?.healthCheck?.healthyThreshold||2,UnhealthyThresholdCount:A?.healthCheck?.unhealthyThreshold||5,HealthCheckTimeoutSeconds:10,Tags:[{Key:"Name",Value:`${N}-${t}-tg`},{Key:"Environment",Value:t}]}});let Ye=`${N}${t}HTTPListener`.replace(/[^a-zA-Z0-9]/g,"");if(L?.enabled&&L?.redirectHttp)this.builder.addResource(Ye,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:Te},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"redirect",RedirectConfig:{Protocol:"HTTPS",Port:"443",StatusCode:"HTTP_301"}}]}});else this.builder.addResource(Ye,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:Te},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:Qe}}]}});if(L?.enabled){let vt=L.certificateArn||(G?{Ref:G}:void 0);if(vt){let Ot=`${N}${t}HTTPSListener`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ot,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:Te},Port:443,Protocol:"HTTPS",Certificates:[{CertificateArn:vt}],DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:Qe}}],SslPolicy:"ELBSecurityPolicy-TLS13-1-2-2021-06"}})}}let et=`${N}${t}${oe}Service`.replace(/[^a-zA-Z0-9]/g,""),Pt=be.desiredCount||1;this.builder.addResource(et,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${N}-${t}-${oe}`,Cluster:{Ref:Z},TaskDefinition:{Ref:Me},DesiredCount:Pt,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:ne}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},LoadBalancers:[{ContainerName:oe,ContainerPort:we,TargetGroupArn:{Ref:Qe}}],HealthCheckGracePeriodSeconds:120,Tags:[{Key:"Name",Value:`${N}-${t}-${oe}`},{Key:"Environment",Value:t}]},DependsOn:[Me,Ye,Qe]});let Bt=be.autoScaling;if(Bt){let vt=`${N}${t}${oe}ScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(vt,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:Bt.max||10,MinCapacity:Bt.min||1,ResourceId:{"Fn::Sub":`service/\${${Z}}/${N}-${t}-${oe}`},ScalableDimension:"ecs:service:DesiredCount",ServiceNamespace:"ecs",RoleARN:{"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"}},DependsOn:et}),Bt.targetCpuUtilization)this.builder.addResource(`${N}${t}${oe}CPUScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${N}-${t}-${oe}-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:vt},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:Bt.targetCpuUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}});if(Bt.targetMemoryUtilization)this.builder.addResource(`${N}${t}${oe}MemoryScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${N}-${t}-${oe}-memory-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:vt},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageMemoryUtilization"},TargetValue:Bt.targetMemoryUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(j?.domain&&j?.hostedZoneId)this.builder.addResource(`${N}${t}ApiDnsRecord`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:j.hostedZoneId,Name:`api.${j.domain}`,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[Te,"DNSName"]},HostedZoneId:{"Fn::GetAtt":[Te,"CanonicalHostedZoneID"]}}}});this.builder.addOutput("ECSClusterArn",{Description:"ECS Cluster ARN",Value:{"Fn::GetAtt":[Z,"Arn"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-cluster-arn"}}}),this.builder.addOutput("ECSServiceName",{Description:"ECS Service Name",Value:`${N}-${t}-${oe}`,Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-service-name"}}}),this.builder.addOutput("LoadBalancerDNS",{Description:"Application Load Balancer DNS Name",Value:{"Fn::GetAtt":[Te,"DNSName"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-alb-dns"}}})}else{let Qe=`${N}${t}${oe}Service`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Qe,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${N}-${t}-${oe}`,Cluster:{Ref:Z},TaskDefinition:{Ref:Me},DesiredCount:be.desiredCount||1,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:ne}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},Tags:[{Key:"Name",Value:`${N}-${t}-${oe}`},{Key:"Environment",Value:t}]},DependsOn:[Me]})}break}}generateServerless(N,t){if(this.mergedConfig.infrastructure?.functions)for(let[m,A]of Object.entries(this.mergedConfig.infrastructure.functions)){if(!this.shouldDeploy(A))continue;let{role:L,logicalId:j}=ve.createRole({slug:N,environment:t,roleName:`${N}-${t}-${m}-role`,servicePrincipal:"lambda.amazonaws.com",managedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]});this.builder.addResource(j,L);let{lambdaFunction:G,logicalId:Z}=S.createLambdaFunction({slug:N,environment:t,functionName:`${N}-${t}-${m}`,handler:A.handler||"index.handler",runtime:A.runtime||"nodejs20.x",code:{zipFile:A.code||"export const handler = async () => ({ statusCode: 200 });"},role:j,timeout:A.timeout,memorySize:A.memorySize});this.builder.addResource(Z,G)}if(this.mergedConfig.infrastructure?.api){let{restApi:m,logicalId:A}=Po.createRestApi({slug:N,environment:t,apiName:`${N}-${t}-api`});this.builder.addResource(A,m)}}generateServer(N,t){if(!this.mergedConfig.infrastructure?.servers)return;let m=this.mergedConfig.infrastructure.servers,A=this.mergedConfig.infrastructure?.compute,L=this.mergedConfig.infrastructure?.ssl;if(Object.values(m).some((G)=>!G.privateNetwork||G.privateNetwork==="create"))this.generateNetworkInfrastructure(N,t);for(let[G,Z]of Object.entries(m)){let ee=Z.instanceType||Z.size||A?.size,oe=(ee?S.InstanceSize.specs[ee]:void 0)?.instanceType||ee||"t3.micro",be=Z.type||"app",we=Z.userData||Z.startupScript;if(!we)we=S.UserData.generateAppServerScript({runtime:"bun",runtimeVersion:Z.bunVersion||"latest",webServer:"none",domain:Z.domain,enableSsl:!!L?.enabled,sslEmail:L?.letsEncrypt?.email,installRedis:be==="cache",installDatabaseClients:!!Z.database});let Ce=Z.privateNetwork&&Z.privateNetwork!=="create"?Z.privateNetwork:{Ref:"VPC"},De=Z.subnet||{Ref:"PublicSubnet1"},xe=!!this.mergedConfig.infrastructure?.email?.server?.enabled,Me=[22,80,443,this.resolveApiOriginPort()];if(xe)Me.push(25,465,587,143,993);let Ae=S.createServerModeStack({slug:`${N}-${G}`,environment:t,vpcId:Ce,subnetId:De,instanceType:oe,keyName:Z.keyName||`${N}-${t}`,domain:Z.domain,userData:we,volumeSize:Z.diskSize||20,imageId:Z.image,allowedPorts:Me});for(let[Ee,Te]of Object.entries(Ae.resources))this.builder.addResource(Ee,Te);this.serverEipLogicalIds.set(G,Ae.outputs.eipLogicalId),this.serverEipLogicalIds.set(`${G}Instance`,Ae.outputs.instanceLogicalId),this.builder.addOutput(`${G}InstanceId`,{Value:{Ref:Ae.outputs.instanceLogicalId},Description:`Instance ID for ${G} server`}),this.builder.addOutput(`${G}PublicIp`,{Value:{Ref:Ae.outputs.eipLogicalId},Description:`Public IP for ${G} server`})}}generateComputeApp(N,t){let m=this.mergedConfig.infrastructure?.compute;if(!m)return;let A=this.mergedConfig.sites||{},L=Object.entries(A),G=this.mergedConfig.infrastructure?.dns?.domain,Z=this.mergedConfig.infrastructure?.database,ee=m.size,oe=(ee?S.InstanceSize.specs[ee]:void 0)?.instanceType||ee||"t3.micro";if(!this.builder.hasResource("VPC"))this.generateNetworkInfrastructure(N,t);let be=this.buildCaddyfile(L),we=S.UserData.generateBunAppScript({runtime:m.runtime||"bun",runtimeVersion:m.runtimeVersion||"latest",systemPackages:m.systemPackages,database:Z,caddyfile:be}),Ce=Di(m.appUpdates),De=Ce.length>0?`${we}
97
97
  ${Ce.join(`
98
98
  `)}
99
- `:we,Se=L.map(([,Ge])=>Ge.port).filter((Ge)=>typeof Ge==="number"&&![80,443].includes(Ge)),De=this.resolveApiOriginPort(),_e=`${N}${t}DeployBucket`.replace(/[^a-zA-Z0-9]/g,""),Ae=`${N}-${t}-deploy`;this.builder.addResource(_e,{Type:"AWS::S3::Bucket",Properties:{BucketName:Ae,PublicAccessBlockConfiguration:{BlockPublicAcls:!0,BlockPublicPolicy:!0,IgnorePublicAcls:!0,RestrictPublicBuckets:!0},LifecycleConfiguration:{Rules:[{Id:"expire-old-releases",Status:"Enabled",ExpirationInDays:7,Prefix:"releases/"}]},Tags:[{Key:"Project",Value:N},{Key:"Environment",Value:t},{Key:"ManagedBy",Value:"ts-cloud"}]}});let Te=S.createServerModeStack({slug:`${N}-app`,environment:t,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},instanceType:oe,keyName:p.allowSsh?`${N}-${t}`:void 0,domain:G,userData:Ee,volumeSize:p.disk?.size||20,imageId:p.image,allowedPorts:[...p.allowSsh?[22]:[],80,443,De,...Se]}),Ne=Te.resources[Te.outputs.instanceLogicalId];if(Ne?.Properties){let Ge=Ne.Properties.Tags||[];Ne.Properties.Tags=[...Ge,{Key:"Project",Value:N},{Key:"Environment",Value:t},{Key:"Role",Value:"app"},{Key:"ManagedBy",Value:"ts-cloud"}]}let Xe=Te.resources[Te.outputs.roleLogicalId];if(Xe?.Properties)Xe.Properties.Policies=Xe.Properties.Policies||[],Xe.Properties.Policies.push({PolicyName:"DeployBucketRead",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["s3:GetObject","s3:ListBucket"],Resource:[`arn:aws:s3:::${Ae}`,`arn:aws:s3:::${Ae}/*`]}]}});for(let[Ge,Ye]of Object.entries(Te.resources))this.builder.addResource(Ge,Ye);this.builder.addOutput("deployBucketName",{Value:{Ref:_e},Description:"S3 bucket where release tarballs are uploaded"}),this.builder.addOutput("appInstanceId",{Value:{Ref:Te.outputs.instanceLogicalId},Description:"EC2 instance ID for the app server"}),this.builder.addOutput("appPublicIp",{Value:{Ref:Te.outputs.eipLogicalId},Description:"Public IP for the app server"}),this.serverEipLogicalIds.set("app",Te.outputs.eipLogicalId),this.serverEipLogicalIds.set("appInstance",Te.outputs.instanceLogicalId)}generateJumpBox(N,t){let p=this.mergedConfig.infrastructure?.jumpBox;if(!p)return;let A=p===!0?{}:p;if(A.enabled===!1)return;this.generateNetworkInfrastructure(N,t);let L=A.size||"micro",G=S.InstanceSize.specs[L]?.instanceType||L||"t3.micro",Z;if(A.mountEfs)Z={fileSystemId:typeof A.mountEfs==="string"?A.mountEfs:{Ref:"FileSystem"},mountPath:A.mountPath||"/mnt/efs"};let ee;if(A.databaseTools)ee=S.JumpBox.withDatabaseTools({slug:N,environment:t,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:A.keyName||`${N}-${t}`,allowedCidrs:A.allowedCidrs});else if(Z)ee=S.JumpBox.withEfsMount({slug:N,environment:t,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:A.keyName||`${N}-${t}`,fileSystemId:Z.fileSystemId,mountPath:Z.mountPath,allowedCidrs:A.allowedCidrs});else ee=S.createJumpBox({slug:N,environment:t,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:A.keyName||`${N}-${t}`,instanceType:G,allowedCidrs:A.allowedCidrs,mountEfs:Z});for(let[re,oe]of Object.entries(ee.resources))this.builder.addResource(re,oe);this.builder.addOutput("JumpBoxInstanceId",{Value:{Ref:ee.instanceLogicalId},Description:"The ID of the JumpBox EC2 instance (use with SSM Session Manager or SSH)"})}generateSharedInfrastructure(N,t){let p=this.mergedConfig.infrastructure?.ssl,A=this.mergedConfig.infrastructure?.dns,L=A?.domain,j=A?.hostedZoneId,G=[],Z,ee;if(p?.certificateArn)ee=p.certificateArn;else if(p?.enabled&&L&&j){let Se=p.domains||[L],De=Se[0],_e=Se.slice(1).map((Ne)=>{if(Ne.includes(".")&&Ne.endsWith(De))return Ne.replace(`.${De}`,"");return Ne}),{certificate:Ae,logicalId:Te}=B.createCertificate({domain:De,subdomains:_e,slug:N,environment:t,validationMethod:"DNS",hostedZoneId:j});Z=Te,this.builder.addResource(Te,Ae)}if(this.mergedConfig.infrastructure?.storage){let Se=Object.entries(this.mergedConfig.infrastructure.storage).some(([,Ae])=>Ae.website),De;if(Se&&(Z||ee))De=`${N}${t}CloudFrontOAC`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(De,{Type:"AWS::CloudFront::OriginAccessControl",Properties:{OriginAccessControlConfig:{Name:`${N}-${t}-s3-oac`,Description:`OAC for ${N} ${t} S3 website buckets`,OriginAccessControlOriginType:"s3",SigningBehavior:"always",SigningProtocol:"sigv4"}}});let _e=Object.entries(this.mergedConfig.infrastructure.storage).map(([Ae,Te])=>({name:Ae,config:Te,mountPath:this.normalizeMountPath(Te),rewriteStyle:Te.pathRewriteStyle||"directory",logicalId:this.storageBucketLogicalId(N,t,Ae)})).filter((Ae)=>Ae.name!=="public"&&Ae.config.website&&Ae.mountPath);for(let[Ae,Te]of Object.entries(this.mergedConfig.infrastructure.storage)){let Ne=!!(Te.website&&De),Xe=this.storageBucketName(N,t,Ae),{bucket:Ge,logicalId:Ye}=Le.createBucket({slug:N,name:Ae,environment:t,bucketName:Xe,versioning:Te.versioning,encryption:Te.encryption,public:Ne?!1:Te.public});if(Ne&&Ge.Properties)Ge.Properties.PublicAccessBlockConfiguration={BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!1,RestrictPublicBuckets:!1};if(this.builder.addResource(Ye,Ge),Te.website){let Pt=typeof Te.website==="object"?Te.website:{},wt=Le.enableWebsiteHosting(Ge,Pt.indexDocument||"index.html",Pt.errorDocument);this.builder.addResource(Ye,wt)}let et=this.normalizeMountPath(Te);if(Ne&&De&&L&&!(et&&Ae!=="public")){let Pt=`${N}${t}${Ae}CDN`.replace(/[^a-zA-Z0-9]/g,""),wt=[];if(Te.aliases&&Te.aliases.length>0)wt.push(...Te.aliases);else if(Ae==="public"){if(wt.push(L),p?.domains?.includes(`www.${L}`))wt.push(`www.${L}`)}else if(Ae==="docs")wt.push(`docs.${L}`);else if(Ae==="blog")wt.push(`blog.${L}`);let Ot=typeof Te.website==="object"?Te.website:{},Ut=Te.spa===!0,sn=Ot.errorDocument||(Ut?"index.html":"404.html"),jt=[];if(Ut)jt.push({ErrorCode:403,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300});else jt.push({ErrorCode:403,ResponseCode:404,ResponsePagePath:`/${sn}`,ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:404,ResponsePagePath:`/${sn}`,ErrorCachingMinTTL:300});let ft=ee&&wt.length>0?{AcmCertificateArn:ee,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:Z&&wt.length>0?{AcmCertificateArn:{Ref:Z},SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},Vt=`S3-${N}-${t}-${Ae}`,vt=this.mergedConfig.project.region||"us-east-1",Lt;if(!Ut)Lt=`${N}${t}${Ae}UrlRewrite`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(Lt,{Type:"AWS::CloudFront::Function",Properties:{Name:`${N}-${t}-${Ae}-url-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`URL rewrite for ${N} ${t} ${Ae} - appends .html to extensionless paths`,Runtime:"cloudfront-js-2.0"},FunctionCode:`function handler(event) { var request = event.request; var uri = request.uri; if (uri.endsWith('/')) { request.uri += 'index.html'; }
100
- else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`}});let nr=[{Id:Vt,DomainName:{"Fn::Sub":`\${${Ye}}.s3.${vt}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:De}}],Qn=[],di=[];if(this.shouldRouteStorageBucketToCompute(Ae,Te)){this.appendComputeAppOrigin(nr,Qn,di,N,t,this.resolveComputeCachePathPatterns(Te.computeRoutes));for(let tn of _e){let rn=`S3-${N}-${t}-${tn.name}`,Jr=`${N}${t}${tn.name}PathMountRewrite`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Jr,{Type:"AWS::CloudFront::Function",Properties:{Name:`${N}-${t}-${tn.name}-path-mount-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`Path mount rewrite for ${N} ${t} ${tn.name} at ${tn.mountPath}`,Runtime:"cloudfront-js-2.0"},FunctionCode:this.pathMountRewriteFunctionCode(tn.mountPath,tn.rewriteStyle)}}),nr.push({Id:rn,DomainName:{"Fn::Sub":`\${${tn.logicalId}}.s3.${vt}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:De}}),di.push(tn.logicalId,Jr);for(let ui of[tn.mountPath,`${tn.mountPath}/*`])Qn.push({PathPattern:ui,TargetOriginId:rn,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[Jr,"FunctionARN"]}}]})}}let fo={Type:"AWS::CloudFront::Distribution",DependsOn:[Ye,De,...Lt?[Lt]:[],...di],Properties:{DistributionConfig:{Enabled:!0,Comment:`${N} ${t} ${Ae} site`,DefaultRootObject:"index.html",Origins:nr,DefaultCacheBehavior:{TargetOriginId:Vt,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",...Lt?{FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[Lt,"FunctionARN"]}}]}:{}},...Qn.length>0?{CacheBehaviors:Qn}:{},...wt.length>0?{Aliases:wt}:{},ViewerCertificate:ft,PriceClass:"PriceClass_100",HttpVersion:"http2and3",IPV6Enabled:!0,CustomErrorResponses:jt}}};if(Z)fo.DependsOn.push(Z);this.builder.addResource(Pt,fo);let gr=`${Ye}CloudFrontPolicy`;if(this.builder.addResource(gr,{Type:"AWS::S3::BucketPolicy",DependsOn:[Ye,Pt],Properties:{Bucket:{Ref:Ye},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${Ye}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${Pt}}`}}}}]}}}),this.shouldRouteStorageBucketToCompute(Ae,Te))for(let tn of _e){let rn=`${tn.logicalId}CloudFrontPolicy`;this.builder.addResource(rn,{Type:"AWS::S3::BucketPolicy",DependsOn:[tn.logicalId,Pt],Properties:{Bucket:{Ref:tn.logicalId},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${tn.logicalId}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${Pt}}`}}}}]}}})}if(wt.length>0)G.push({name:Ae,bucketLogicalId:Ye,distLogicalId:Pt,oacLogicalId:De,aliases:wt});this.builder.addOutput(`${Ae}CloudFrontDomain`,{Value:{"Fn::GetAtt":[Pt,"DomainName"]},Description:`CloudFront domain for ${Ae}`}),this.builder.addOutput(`${Ae}CloudFrontDistributionId`,{Value:{Ref:Pt},Description:`CloudFront distribution ID for ${Ae}`})}if(this.builder.addOutput(`${Ae}BucketName`,{Value:{Ref:Ye},Description:`S3 bucket name for ${Ae}`}),Ae==="public")this.builder.addOutput("FrontendBucketName",{Value:{Ref:Ye},Description:"Frontend S3 bucket name"});if(Ae==="docs")this.builder.addOutput("DocsBucketName",{Value:{Ref:Ye},Description:"Documentation S3 bucket name"});if(Ae==="blog")this.builder.addOutput("BlogBucketName",{Value:{Ref:Ye},Description:"Blog S3 bucket name"})}}if(this.mergedConfig.infrastructure?.sftp){let Se=this.mergedConfig.infrastructure.sftp,De=Eo.create({...Se,storage:this.resolveSftpStorage(Se,N,t)??Se.storage,slug:N,environment:t});for(let[_e,Ae]of Object.entries(De.resources))this.builder.addResource(_e,Ae);this.builder.addOutput("SftpServerId",{Value:{Ref:De.serverLogicalId},Description:"AWS Transfer Family SFTP server ID"}),this.builder.addOutput("SftpEndpoint",{Value:{"Fn::Sub":`\${${De.serverLogicalId}}.server.transfer.\${AWS::Region}.amazonaws.com`},Description:"AWS Transfer Family SFTP endpoint"}),this.builder.addOutput("SftpStorageDomain",{Value:De.domain,Description:"Storage backing the SFTP server (EFS or S3)"})}if(j&&G.length>0)for(let{name:Se,distLogicalId:De,aliases:_e}of G)for(let Ae of _e){let Te=Ae.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${Te}ARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[De],Properties:{HostedZoneId:j,Name:Ae,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[De,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}}),this.builder.addResource(`${Te}AAAARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[De],Properties:{HostedZoneId:j,Name:Ae,Type:"AAAA",AliasTarget:{DNSName:{"Fn::GetAtt":[De,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}})}if(this.mergedConfig.infrastructure?.databases){for(let[Se,De]of Object.entries(this.mergedConfig.infrastructure.databases))if(De.engine==="dynamodb"){let{table:_e,logicalId:Ae}=Bt.createTable({slug:N,environment:t,tableName:`${N}-${t}-${Se}`,partitionKey:De.partitionKey||{name:"id",type:"S"},sortKey:De.sortKey});this.builder.addResource(Ae,_e)}else if(De.engine==="postgres"){let{dbInstance:_e,logicalId:Ae}=Bt.createPostgres({slug:N,environment:t,dbInstanceIdentifier:`${N}-${t}-${Se}`,masterUsername:De.username||"admin",masterUserPassword:De.password||"changeme123",allocatedStorage:De.storage||20,dbInstanceClass:De.instanceClass||"db.t3.micro"});this.builder.addResource(Ae,_e)}else if(De.engine==="mysql"){let{dbInstance:_e,logicalId:Ae}=Bt.createMysql({slug:N,environment:t,dbInstanceIdentifier:`${N}-${t}-${Se}`,masterUsername:De.username||"admin",masterUserPassword:De.password||"changeme123",allocatedStorage:De.storage||20,dbInstanceClass:De.instanceClass||"db.t3.micro"});this.builder.addResource(Ae,_e)}}if(this.mergedConfig.infrastructure?.cdn)for(let[Se,De]of Object.entries(this.mergedConfig.infrastructure.cdn)){if(!De.origin)continue;let _e=typeof De.customDomain==="string"?De.customDomain:De.customDomain?.domain||De.domain,Ae=typeof De.customDomain==="object"?De.customDomain?.certificateArn:De.certificateArn,Te=Ae||ee,Ne=`${N}${t}${Se}CDN`.replace(/[^a-zA-Z0-9]/g,""),Xe=`S3-${N}-${t}-${Se}-cdn`,Ge=[{Id:Xe,DomainName:De.origin,OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginShield:De.originShield?{Enabled:!0,OriginShieldRegion:De.originShieldRegion||this.mergedConfig.environments[t]?.region||this.mergedConfig.project.region||"us-east-1"}:{Enabled:!1}}],Ye=[],et=[],Pt=["GET","HEAD","OPTIONS"],wt=Xe,Ot;if(De.failoverOrigin){vo(Pt,`the default cache behavior of infrastructure.cdn.${Se}`);let ft=`${Xe}-failover`;Ge.push({Id:ft,DomainName:De.failoverOrigin,OriginPath:"",...To(De.failoverOrigin)?{S3OriginConfig:{OriginAccessIdentity:""}}:{CustomOriginConfig:{HTTPPort:80,HTTPSPort:443,OriginProtocolPolicy:"https-only"}},OriginShield:{Enabled:!1}}),Ot=Ws({primaryOriginId:Xe,secondaryOriginId:ft,statusCodes:De.failoverStatusCodes}),wt=Ot.Items[0].Id}else if(De.failoverStatusCodes)throw Error(`infrastructure.cdn.${Se}: failoverStatusCodes needs failoverOrigin to fail over to`);if(De.routeCompute)this.appendComputeAppOrigin(Ge,Ye,et,N,t,this.resolveComputeCachePathPatterns(De.computeRoutes));let Ut=Te&&_e?{AcmCertificateArn:Z&&!Ae?{Ref:Z}:Te,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},sn=[...et];if(Z&&!Ae&&_e)sn.push(Z);let jt={Type:"AWS::CloudFront::Distribution",...sn.length>0?{DependsOn:sn}:{},Properties:{DistributionConfig:{Enabled:!0,Comment:`${N} ${t} ${Se} CDN`,DefaultRootObject:"index.html",Origins:Ge,...Ot?{OriginGroups:Ot}:{},DefaultCacheBehavior:{TargetOriginId:wt,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:Pt,CachedMethods:["GET","HEAD","OPTIONS"],Compress:De.compress!==!1,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6"},...Ye.length>0?{CacheBehaviors:Ye}:{},..._e&&Te?{Aliases:[_e]}:{},ViewerCertificate:Ut,PriceClass:"PriceClass_100",HttpVersion:De.http3?"http2and3":"http2"}}};if(this.builder.addResource(Ne,jt),j&&_e&&Te){let ft=_e.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${ft}CdnARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[Ne,...Z&&!Ae?[Z]:[]],Properties:{HostedZoneId:j,Name:_e,Type:"A",AliasTarget:{HostedZoneId:"Z2FDTNDATAQYW2",DNSName:{"Fn::GetAtt":[Ne,"DomainName"]}}}})}}if(this.mergedConfig.infrastructure?.queues)for(let[Se,De]of Object.entries(this.mergedConfig.infrastructure.queues)){if(!this.shouldDeploy(De))continue;let{queue:_e,logicalId:Ae}=pe.createQueue({slug:N,environment:t,name:`${N}-${t}-${Se}`,fifo:De.fifo,visibilityTimeout:De.visibilityTimeout,messageRetentionPeriod:De.messageRetentionPeriod,delaySeconds:De.delaySeconds,maxMessageSize:De.maxMessageSize,receiveMessageWaitTime:De.receiveMessageWaitTime,contentBasedDeduplication:De.contentBasedDeduplication,encrypted:De.encrypted,kmsKeyId:De.kmsKeyId});this.builder.addResource(Ae,_e);let Te;if(De.deadLetterQueue){let{deadLetterQueue:Ne,updatedSourceQueue:Xe,deadLetterLogicalId:Ge}=pe.createDeadLetterQueue(Ae,{slug:N,environment:t,maxReceiveCount:De.maxReceiveCount});Te=Ge,this.builder.addResource(Ge,Ne);let et=this.builder.getResources()[Ae];if(et?.Properties)et.Properties.RedrivePolicy=Xe.Properties?.RedrivePolicy}if(De.trigger){let Ne=De.trigger,Xe=`${N}${t}${Ne.functionName}`.replace(/[^a-zA-Z0-9]/g,""),Ge={Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:{"Fn::GetAtt":[Ae,"Arn"]},FunctionName:{Ref:Xe},BatchSize:Ne.batchSize||10,MaximumBatchingWindowInSeconds:Ne.batchWindow||0,Enabled:!0,...Ne.reportBatchItemFailures!==!1&&{FunctionResponseTypes:["ReportBatchItemFailures"]},...Ne.maxConcurrency&&{ScalingConfig:{MaximumConcurrency:Ne.maxConcurrency}},...Ne.filterPattern&&{FilterCriteria:{Filters:[{Pattern:JSON.stringify(Ne.filterPattern)}]}}},DependsOn:[Ae,Xe]};this.builder.addResource(`${Ae}Trigger`,Ge)}if(De.alarms?.enabled){let Ne=De.alarms,Xe=Ne.notificationTopicArn;if(!Xe&&Ne.notificationEmails?.length){let et=`${Ae}AlarmTopic`;this.builder.addResource(et,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${N}-${t}-${Se}-alarms`,DisplayName:`${Se} Queue Alarms`}}),Ne.notificationEmails.forEach((Pt,wt)=>{this.builder.addResource(`${et}Sub${wt}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:et},Protocol:"email",Endpoint:Pt}})}),Xe={Ref:et}}let Ge=Ne.queueDepthThreshold||1000;this.builder.addResource(`${Ae}DepthAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-${Se}-queue-depth`,AlarmDescription:`Queue ${Se} depth exceeds ${Ge} messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:Ge,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[Ae,"QueueName"]}}],...Xe&&{AlarmActions:[Xe],OKActions:[Xe]}}});let Ye=Ne.messageAgeThreshold||3600;if(this.builder.addResource(`${Ae}AgeAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-${Se}-message-age`,AlarmDescription:`Queue ${Se} oldest message exceeds ${Ye} seconds`,MetricName:"ApproximateAgeOfOldestMessage",Namespace:"AWS/SQS",Statistic:"Maximum",Period:300,EvaluationPeriods:2,Threshold:Ye,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[Ae,"QueueName"]}}],...Xe&&{AlarmActions:[Xe],OKActions:[Xe]}}}),Te&&Ne.dlqAlarm!==!1)this.builder.addResource(`${Te}Alarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-${Se}-dlq-messages`,AlarmDescription:`Dead letter queue for ${Se} has messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Sum",Period:300,EvaluationPeriods:1,Threshold:0,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[Te,"QueueName"]}}],...Xe&&{AlarmActions:[Xe]}}})}if(De.subscribe){let Ne=De.subscribe,Xe=Ne.topicArn;if(!Xe&&Ne.topicName)Xe={Ref:Ne.topicName};if(Xe){this.builder.addResource(`${Ae}SnsPolicy`,{Type:"AWS::SQS::QueuePolicy",Properties:{Queues:[{Ref:Ae}],PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"sqs:SendMessage",Resource:{"Fn::GetAtt":[Ae,"Arn"]},Condition:{ArnEquals:{"aws:SourceArn":Xe}}}]}}});let Ge={TopicArn:Xe,Protocol:"sqs",Endpoint:{"Fn::GetAtt":[Ae,"Arn"]},RawMessageDelivery:Ne.rawMessageDelivery||!1};if(Ne.filterPolicy)Ge.FilterPolicy=Ne.filterPolicy,Ge.FilterPolicyScope=Ne.filterPolicyScope||"MessageAttributes";this.builder.addResource(`${Ae}SnsSub`,{Type:"AWS::SNS::Subscription",Properties:Ge,DependsOn:`${Ae}SnsPolicy`})}}}if(this.mergedConfig.infrastructure?.realtime?.enabled)if((this.mergedConfig.infrastructure.realtime.mode||"serverless")==="server")this.generateRealtimeServerResources(N,t);else this.generateRealtimeResources(N,t);let re=this.mergedConfig.infrastructure?.redirects;if(re){let Se=re.target||this.mergedConfig.infrastructure?.dns?.domain||"",De=re.protocol||"https";if(re.domains?.length&&Se)for(let _e of re.domains){let{bucket:Ae,bucketPolicy:Te,logicalId:Ne,policyLogicalId:Xe}=Et.createDomainRedirectBucket({slug:N,environment:t,sourceDomain:_e,targetDomain:Se,protocol:De});this.builder.addResource(Ne,Ae),this.builder.addResource(Xe,Te)}if(re.paths&&Object.keys(re.paths).length>0){let _e=Et.fromMapping(re.paths,{statusCode:re.statusCode||301}),{function:Ae,logicalId:Te}=Et.createPathRedirectFunction({slug:N,environment:t,rules:_e});this.builder.addResource(Te,Ae)}}if(this.mergedConfig.infrastructure?.monitoring?.alarms)for(let[Se,De]of Object.entries(this.mergedConfig.infrastructure.monitoring.alarms)){let{alarm:_e,logicalId:Ae}=gi.createAlarm({slug:N,environment:t,alarmName:`${N}-${t}-${Se}`,metricName:De.metricName||"Errors",namespace:De.namespace||"AWS/Lambda",threshold:De.threshold||1,comparisonOperator:De.comparisonOperator||"GreaterThanThreshold"});this.builder.addResource(Ae,_e)}let oe=this.mergedConfig.infrastructure?.cache;if(oe){let Se=oe.type||"redis";if(Se==="redis"){this.generateNetworkInfrastructure(N,t);let De=oe.redis||{},{replicationGroup:_e,subnetGroup:Ae,logicalId:Te,subnetGroupId:Ne}=mi.createRedis({slug:N,environment:t,nodeType:De.nodeType||oe.nodeType||"cache.t3.micro",engineVersion:De.engineVersion||"7.1",port:De.port||6379,numCacheClusters:De.numCacheNodes||2,automaticFailover:De.automaticFailoverEnabled!==!1,atRestEncryption:!0,transitEncryption:!0,snapshotRetentionDays:De.snapshotRetentionLimit||7,snapshotWindow:De.snapshotWindow,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(Te,_e),Ae&&Ne)this.builder.addResource(Ne,Ae);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[Te,"PrimaryEndPoint.Address"]},Description:"Redis primary endpoint address"}),this.builder.addOutput("CachePort",{Value:{"Fn::GetAtt":[Te,"PrimaryEndPoint.Port"]},Description:"Redis primary endpoint port"})}else if(Se==="memcached"){this.generateNetworkInfrastructure(N,t);let De=oe.elasticache||{},{cluster:_e,subnetGroup:Ae,logicalId:Te,subnetGroupId:Ne}=mi.createMemcached({slug:N,environment:t,nodeType:De.nodeType||oe.nodeType||"cache.t3.micro",engineVersion:De.engineVersion||"1.6.22",numCacheNodes:De.numCacheNodes||2,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(Te,_e),Ae&&Ne)this.builder.addResource(Ne,Ae);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[Te,"ConfigurationEndpoint.Address"]},Description:"Memcached configuration endpoint address"})}}let be=this.mergedConfig.infrastructure?.email;if(be){let Se=be.domain||this.mergedConfig.infrastructure?.dns?.domain;if(Se){let{emailIdentity:De,logicalId:_e}=D.verifyDomain({domain:Se,slug:N,environment:t,enableDkim:be.enableDkim!==!1,dkimKeyLength:be.dkimKeyLength||"RSA_2048_BIT"});if(this.builder.addResource(_e,De),be.configurationSet!==!1){let{configurationSet:Ne,logicalId:Xe}=D.createConfigurationSet({slug:N,environment:t});this.builder.addResource(Xe,Ne)}let Ae=be.hostedZoneId||this.mergedConfig.infrastructure?.dns?.hostedZoneId;if(Ae){if(be.enableDkim!==!1)for(let et=1;et<=3;et++){let Pt=`DkimRecord${et}${Se.replace(/\./g,"")}`;this.builder.addResource(Pt,{Type:"AWS::Route53::RecordSet",DependsOn:[_e],Properties:{HostedZoneId:Ae,Name:{"Fn::GetAtt":[_e,`DkimDNSTokenName${et}`]},Type:"CNAME",TTL:1800,ResourceRecords:[{"Fn::GetAtt":[_e,`DkimDNSTokenValue${et}`]}]}})}let{record:Ne,logicalId:Xe}=D.createSpfRecord(Se,Ae);this.builder.addResource(Xe,Ne);let{record:Ge,logicalId:Ye}=D.createDmarcRecord(Se,Ae,{policy:"none",reportingEmail:be.dmarcReportingEmail||`dmarc-reports@${Se}`});this.builder.addResource(Ye,Ge)}this.builder.addOutput("EmailDomain",{Value:Se,Description:"SES verified email domain"});let Te=be.server;if(Te?.enabled&&Ae){let Ne=this.mergedConfig.environments[t]?.region||this.mergedConfig.project.region||"us-east-1",Xe=`${N}-${t}-email`,{role:Ge,policy:Ye,roleLogicalId:et,policyLogicalId:Pt}=D.createEmailLambdaRole({slug:N,environment:t,s3BucketArn:`arn:aws:s3:::${Xe}`,sesIdentityArn:`arn:aws:ses:${Ne}:*:identity/${Se}`});this.builder.addResource(et,Ge),this.builder.addResource(Pt,Ye);let{function:wt,permission:Ot,logicalId:Ut,permissionLogicalId:sn}=D.createInboundEmailLambda({slug:N,environment:t,roleArn:{"Fn::GetAtt":[et,"Arn"]},s3BucketName:Xe,organizedPrefix:"mailboxes/"});this.builder.addResource(Ut,wt),this.builder.addResource(sn,Ot);let jt=D.createInboundEmailSetup({slug:N,environment:t,domain:Se,s3BucketName:Xe,s3KeyPrefix:"inbox/",region:Ne,hostedZoneId:Ae,lambdaFunctionArn:{"Fn::GetAtt":[Ut,"Arn"]}});for(let[Vt,vt]of Object.entries(jt.resources)){if(vt.Type==="AWS::SES::ReceiptRule")vt.DependsOn=[sn,Ut];this.builder.addResource(Vt,vt)}this.builder.addOutput("InboundEmailLambda",{Value:{Ref:Ut},Description:"Inbound email processing Lambda function"}),this.builder.addOutput("EmailBucket",{Value:Xe,Description:"S3 bucket for email storage"});let ft=this.serverEipLogicalIds.get("app");if(ft&&Ae){let vt=`${Te?.subdomain||"mail"}.${Se}`,Lt=vt.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${Lt}ARecord`,{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:Ae,Name:vt,Type:"A",TTL:"300",ResourceRecords:[{Ref:ft}]}})}this.builder.addOutput("MailHost",{Value:`mail.${Se}`,Description:"Mail server hostname for SMTP/IMAP clients"})}}}let we=this.mergedConfig.infrastructure?.search;if(we){let Se=we.vpc?{subnetIds:[{Ref:"PublicSubnet1"}],securityGroupIds:[]}:void 0;if(Se)this.generateNetworkInfrastructure(N,t);let{domain:De,logicalId:_e}=Ft.createDomain({slug:N,environment:t,engineVersion:we.engineVersion||"OpenSearch_2.11",instanceType:we.instanceType||"t3.small.search",instanceCount:we.instanceCount||1,volumeSize:we.volumeSize||10,volumeType:we.volumeType||"gp3",dedicatedMaster:we.dedicatedMaster||!1,dedicatedMasterType:we.dedicatedMasterType,dedicatedMasterCount:we.dedicatedMasterCount||3,multiAz:we.multiAz||!1,encryption:we.encryption||{atRest:!0,nodeToNode:!0},advancedSecurity:we.advancedSecurity,autoTune:we.autoTune!==!1,vpc:Se});this.builder.addResource(_e,De),this.builder.addOutput("SearchDomainEndpoint",{Value:{"Fn::GetAtt":[_e,"DomainEndpoint"]},Description:"OpenSearch domain endpoint"}),this.builder.addOutput("SearchDomainArn",{Value:{"Fn::GetAtt":[_e,"Arn"]},Description:"OpenSearch domain ARN"})}let Ce=this.mergedConfig.infrastructure?.fileSystem;if(Ce&&Object.keys(Ce).length>0){this.generateNetworkInfrastructure(N,t);for(let[Se,De]of Object.entries(Ce)){let{fileSystem:_e,logicalId:Ae}=Ze.createFileSystem({slug:`${N}-${Se}`,environment:t,encrypted:De.encrypted!==!1,performanceMode:De.performanceMode||"generalPurpose",throughputMode:De.throughputMode||"bursting",enableBackup:!0});this.builder.addResource(Ae,_e);let{securityGroup:Te,logicalId:Ne}=Ze.createEfsSecurityGroup({slug:`${N}-${Se}`,environment:t,vpcId:{Ref:"VPC"},sourceCidrBlocks:["10.0.0.0/16"]});this.builder.addResource(Ne,Te);let{mountTargets:Xe,logicalIds:Ge}=Ze.createMultiAzMountTargets(Ae,{slug:`${N}-${Se}`,environment:t,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],securityGroupId:{Ref:Ne}});for(let Ye=0;Ye<Xe.length;Ye++)this.builder.addResource(Ge[Ye],Xe[Ye]);this.builder.addOutput(`${Se}FileSystemId`,{Value:{Ref:Ae},Description:`EFS file system ID for ${Se}`})}}let Ee=this.mergedConfig.infrastructure?.ai;if(Ee){let Se=Ee.service||"ecs",De=Ee.models||["*"],_e=Ee.allowStreaming!==!1,Ae;if(Se==="ecs")Ae=Ue.enableBedrockForEcs({slug:N,environment:t,models:De,allowStreaming:_e});else if(Se==="ec2")Ae=Ue.enableBedrockForEc2({slug:N,environment:t,models:De,allowStreaming:_e});else if(Se==="lambda")Ae=Ue.enableBedrockForLambda({slug:N,environment:t,models:De,allowStreaming:_e});else Ae=Ue.createBedrockRole(Se,{slug:N,environment:t,models:De,allowStreaming:_e});if(this.builder.addResource(Ae.logicalId,Ae.role),Ee.allowAsync){let{policy:Te,logicalId:Ne}=Ue.createBedrockPolicy({slug:N,environment:t,models:De,allowStreaming:_e,allowAsync:!0});this.builder.addResource(Ne,Te)}this.builder.addOutput("BedrockRoleArn",{Value:{"Fn::GetAtt":[Ae.logicalId,"Arn"]},Description:"IAM role ARN with Bedrock permissions"})}}generateRealtimeResources(N,t){let p=this.mergedConfig.infrastructure?.realtime;if(!p)return;let A=p.name||`${N}-${t}-realtime`,L=p.scaling||{},j=p.storage||{type:"dynamodb"},G=L.handlerMemory||256,Z=L.handlerTimeout||30,ee=`${N}${t}RealtimeConnections`.replace(/[^a-zA-Z0-9]/g,""),re=`${N}${t}RealtimeChannels`.replace(/[^a-zA-Z0-9]/g,"");if(j.type==="dynamodb"){let Ge=j.dynamodb||{},Ye=Ge.billingMode||"PAY_PER_REQUEST";this.builder.addResource(ee,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${N}-${t}-realtime-connections`,BillingMode:Ye,AttributeDefinitions:[{AttributeName:"connectionId",AttributeType:"S"},{AttributeName:"userId",AttributeType:"S"}],KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],GlobalSecondaryIndexes:[{IndexName:"userId-index",KeySchema:[{AttributeName:"userId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...Ye==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Ge.readCapacity||5,WriteCapacityUnits:Ge.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...Ye==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Ge.readCapacity||5,WriteCapacityUnits:Ge.writeCapacity||5}},...Ge.pointInTimeRecovery&&{PointInTimeRecoverySpecification:{PointInTimeRecoveryEnabled:!0}},Tags:[{Key:"Name",Value:`${N}-${t}-realtime-connections`},{Key:"Environment",Value:t}]}}),this.builder.addResource(re,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${N}-${t}-realtime-channels`,BillingMode:Ye,AttributeDefinitions:[{AttributeName:"channel",AttributeType:"S"},{AttributeName:"connectionId",AttributeType:"S"}],KeySchema:[{AttributeName:"channel",KeyType:"HASH"},{AttributeName:"connectionId",KeyType:"RANGE"}],GlobalSecondaryIndexes:[{IndexName:"connectionId-index",KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...Ye==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Ge.readCapacity||5,WriteCapacityUnits:Ge.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...Ye==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Ge.readCapacity||5,WriteCapacityUnits:Ge.writeCapacity||5}},Tags:[{Key:"Name",Value:`${N}-${t}-realtime-channels`},{Key:"Environment",Value:t}]}})}let oe=`${N}${t}RealtimeRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(oe,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-realtime-handler-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"lambda.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"],Policies:[{PolicyName:"RealtimeHandlerPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["dynamodb:GetItem","dynamodb:PutItem","dynamodb:DeleteItem","dynamodb:Query","dynamodb:Scan","dynamodb:UpdateItem"],Resource:[{"Fn::GetAtt":[ee,"Arn"]},{"Fn::Sub":`\${${ee}.Arn}/index/*`},{"Fn::GetAtt":[re,"Arn"]},{"Fn::Sub":`\${${re}.Arn}/index/*`}]},{Effect:"Allow",Action:"execute-api:ManageConnections",Resource:{"Fn::Sub":"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"}}]}}]}});let be=`${N}${t}RealtimeConnect`.replace(/[^a-zA-Z0-9]/g,""),we=`${N}${t}RealtimeDisconnect`.replace(/[^a-zA-Z0-9]/g,""),Ce=`${N}${t}RealtimeMessage`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(be,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${N}-${t}-realtime-connect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[oe,"Arn"]},MemorySize:G,Timeout:Z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:ee},CHANNELS_TABLE:{Ref:re},ENVIRONMENT:t}},Code:{ZipFile:this.generateConnectHandlerCode()}},DependsOn:[oe,ee]}),this.builder.addResource(we,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${N}-${t}-realtime-disconnect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[oe,"Arn"]},MemorySize:G,Timeout:Z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:ee},CHANNELS_TABLE:{Ref:re},ENVIRONMENT:t}},Code:{ZipFile:this.generateDisconnectHandlerCode()}},DependsOn:[oe,ee,re]}),this.builder.addResource(Ce,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${N}-${t}-realtime-message`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[oe,"Arn"]},MemorySize:G,Timeout:Z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:ee},CHANNELS_TABLE:{Ref:re},ENVIRONMENT:t}},Code:{ZipFile:this.generateMessageHandlerCode()}},DependsOn:[oe,ee,re]});let Ee=`${N}${t}RealtimeApi`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Ee,{Type:"AWS::ApiGatewayV2::Api",Properties:{Name:A,ProtocolType:"WEBSOCKET",RouteSelectionExpression:"$request.body.action",Tags:{Name:A,Environment:t}}});let Se=`${be}Permission`,De=`${we}Permission`,_e=`${Ce}Permission`;this.builder.addResource(Se,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:be},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Ee}}/*/$connect`}}}),this.builder.addResource(De,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:we},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Ee}}/*/$disconnect`}}}),this.builder.addResource(_e,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:Ce},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${Ee}}/*/$default`}}});let Ae=`${Ee}ConnectInteg`,Te=`${Ee}DisconnectInteg`,Ne=`${Ee}MessageInteg`;this.builder.addResource(Ae,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Ee},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${be}.Arn}/invocations`}}}),this.builder.addResource(Te,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Ee},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${we}.Arn}/invocations`}}}),this.builder.addResource(Ne,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:Ee},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${Ce}.Arn}/invocations`}}}),this.builder.addResource(`${Ee}ConnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Ee},RouteKey:"$connect",AuthorizationType:"NONE",Target:{"Fn::Sub":`integrations/\${${Ae}}`}}}),this.builder.addResource(`${Ee}DisconnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Ee},RouteKey:"$disconnect",Target:{"Fn::Sub":`integrations/\${${Te}}`}}}),this.builder.addResource(`${Ee}DefaultRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:Ee},RouteKey:"$default",Target:{"Fn::Sub":`integrations/\${${Ne}}`}}});let Xe=`${Ee}Stage`;if(this.builder.addResource(Xe,{Type:"AWS::ApiGatewayV2::Stage",Properties:{ApiId:{Ref:Ee},StageName:t,AutoDeploy:!0,DefaultRouteSettings:{ThrottlingBurstLimit:L.messagesPerSecond||1000,ThrottlingRateLimit:L.messagesPerSecond||1000}}}),p.monitoring?.enabled){let Ge=p.monitoring,Ye=Ge.notificationTopicArn;if(!Ye&&Ge.notificationEmails?.length){let et=`${Ee}AlarmTopic`;this.builder.addResource(et,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${N}-${t}-realtime-alarms`,DisplayName:"Realtime WebSocket Alarms"}}),Ge.notificationEmails.forEach((Pt,wt)=>{this.builder.addResource(`${et}Sub${wt}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:et},Protocol:"email",Endpoint:Pt}})}),Ye={Ref:et}}if(Ge.connectionThreshold)this.builder.addResource(`${Ee}ConnectionAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-realtime-connections`,AlarmDescription:`WebSocket connections exceed ${Ge.connectionThreshold}`,MetricName:"ConnectCount",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:300,EvaluationPeriods:2,Threshold:Ge.connectionThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:Ee}}],...Ye&&{AlarmActions:[Ye]}}});if(Ge.errorThreshold)this.builder.addResource(`${Ee}ErrorAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-realtime-errors`,AlarmDescription:`WebSocket errors exceed ${Ge.errorThreshold}/min`,MetricName:"ExecutionError",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:60,EvaluationPeriods:3,Threshold:Ge.errorThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:Ee}}],...Ye&&{AlarmActions:[Ye]}}})}this.builder.addOutput(`${Ee}Endpoint`,{Description:"WebSocket API endpoint URL",Value:{"Fn::Sub":`wss://\${${Ee}}.execute-api.\${AWS::Region}.amazonaws.com/${t}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}}),this.builder.addOutput(`${Ee}Id`,{Description:"WebSocket API ID",Value:{Ref:Ee},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-api-id"}}})}generateConnectHandlerCode(){return`
99
+ `:we,Se=L.map(([,Ke])=>Ke.port).filter((Ke)=>typeof Ke==="number"&&![80,443].includes(Ke)),xe=this.resolveApiOriginPort(),Me=`${N}${t}DeployBucket`.replace(/[^a-zA-Z0-9]/g,""),Ae=`${N}-${t}-deploy`;this.builder.addResource(Me,{Type:"AWS::S3::Bucket",Properties:{BucketName:Ae,PublicAccessBlockConfiguration:{BlockPublicAcls:!0,BlockPublicPolicy:!0,IgnorePublicAcls:!0,RestrictPublicBuckets:!0},LifecycleConfiguration:{Rules:[{Id:"expire-old-releases",Status:"Enabled",ExpirationInDays:7,Prefix:"releases/"}]},Tags:[{Key:"Project",Value:N},{Key:"Environment",Value:t},{Key:"ManagedBy",Value:"ts-cloud"}]}});let Ee=S.createServerModeStack({slug:`${N}-app`,environment:t,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},instanceType:oe,keyName:m.allowSsh?`${N}-${t}`:void 0,domain:G,userData:De,volumeSize:m.disk?.size||20,imageId:m.image,allowedPorts:[...m.allowSsh?[22]:[],80,443,xe,...Se]}),Te=Ee.resources[Ee.outputs.instanceLogicalId];if(Te?.Properties){let Ke=Te.Properties.Tags||[];Te.Properties.Tags=[...Ke,{Key:"Project",Value:N},{Key:"Environment",Value:t},{Key:"Role",Value:"app"},{Key:"ManagedBy",Value:"ts-cloud"}]}let Qe=Ee.resources[Ee.outputs.roleLogicalId];if(Qe?.Properties)Qe.Properties.Policies=Qe.Properties.Policies||[],Qe.Properties.Policies.push({PolicyName:"DeployBucketRead",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["s3:GetObject","s3:ListBucket"],Resource:[`arn:aws:s3:::${Ae}`,`arn:aws:s3:::${Ae}/*`]}]}});for(let[Ke,Ye]of Object.entries(Ee.resources))this.builder.addResource(Ke,Ye);this.builder.addOutput("deployBucketName",{Value:{Ref:Me},Description:"S3 bucket where release tarballs are uploaded"}),this.builder.addOutput("appInstanceId",{Value:{Ref:Ee.outputs.instanceLogicalId},Description:"EC2 instance ID for the app server"}),this.builder.addOutput("appPublicIp",{Value:{Ref:Ee.outputs.eipLogicalId},Description:"Public IP for the app server"}),this.serverEipLogicalIds.set("app",Ee.outputs.eipLogicalId),this.serverEipLogicalIds.set("appInstance",Ee.outputs.instanceLogicalId)}generateJumpBox(N,t){let m=this.mergedConfig.infrastructure?.jumpBox;if(!m)return;let A=m===!0?{}:m;if(A.enabled===!1)return;this.generateNetworkInfrastructure(N,t);let L=A.size||"micro",G=S.InstanceSize.specs[L]?.instanceType||L||"t3.micro",Z;if(A.mountEfs)Z={fileSystemId:typeof A.mountEfs==="string"?A.mountEfs:{Ref:"FileSystem"},mountPath:A.mountPath||"/mnt/efs"};let ee;if(A.databaseTools)ee=S.JumpBox.withDatabaseTools({slug:N,environment:t,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:A.keyName||`${N}-${t}`,allowedCidrs:A.allowedCidrs});else if(Z)ee=S.JumpBox.withEfsMount({slug:N,environment:t,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:A.keyName||`${N}-${t}`,fileSystemId:Z.fileSystemId,mountPath:Z.mountPath,allowedCidrs:A.allowedCidrs});else ee=S.createJumpBox({slug:N,environment:t,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:A.keyName||`${N}-${t}`,instanceType:G,allowedCidrs:A.allowedCidrs,mountEfs:Z});for(let[ne,oe]of Object.entries(ee.resources))this.builder.addResource(ne,oe);this.builder.addOutput("JumpBoxInstanceId",{Value:{Ref:ee.instanceLogicalId},Description:"The ID of the JumpBox EC2 instance (use with SSM Session Manager or SSH)"})}generateSharedInfrastructure(N,t){let m=this.mergedConfig.infrastructure?.ssl,A=this.mergedConfig.infrastructure?.dns,L=A?.domain,j=A?.hostedZoneId,G=[],Z,ee;if(m?.certificateArn)ee=m.certificateArn;else if(m?.enabled&&L&&j){let Se=m.domains||[L],xe=Se[0],Me=Se.slice(1).map((Te)=>{if(Te.includes(".")&&Te.endsWith(xe))return Te.replace(`.${xe}`,"");return Te}),{certificate:Ae,logicalId:Ee}=B.createCertificate({domain:xe,subdomains:Me,slug:N,environment:t,validationMethod:"DNS",hostedZoneId:j});Z=Ee,this.builder.addResource(Ee,Ae)}if(this.mergedConfig.infrastructure?.storage){let Se=Object.entries(this.mergedConfig.infrastructure.storage).some(([,Ae])=>Ae.website),xe;if(Se&&(Z||ee))xe=`${N}${t}CloudFrontOAC`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(xe,{Type:"AWS::CloudFront::OriginAccessControl",Properties:{OriginAccessControlConfig:{Name:`${N}-${t}-s3-oac`,Description:`OAC for ${N} ${t} S3 website buckets`,OriginAccessControlOriginType:"s3",SigningBehavior:"always",SigningProtocol:"sigv4"}}});let Me=Object.entries(this.mergedConfig.infrastructure.storage).map(([Ae,Ee])=>({name:Ae,config:Ee,mountPath:this.normalizeMountPath(Ee),rewriteStyle:Ee.pathRewriteStyle||"directory",logicalId:this.storageBucketLogicalId(N,t,Ae)})).filter((Ae)=>Ae.name!=="public"&&Ae.config.website&&Ae.mountPath);for(let[Ae,Ee]of Object.entries(this.mergedConfig.infrastructure.storage)){let Te=!!(Ee.website&&xe),Qe=this.storageBucketName(N,t,Ae),{bucket:Ke,logicalId:Ye}=Le.createBucket({slug:N,name:Ae,environment:t,bucketName:Qe,versioning:Ee.versioning,encryption:Ee.encryption,public:Te?!1:Ee.public}),et=this.normalizeMountPath(Ee),Pt;if(Ee.failover){let vt=`infrastructure.storage.${Ae}`;if(!Te||!L||et&&Ae!=="public")throw Error(`${vt}.failover needs this bucket to be served by its own CloudFront distribution: a website bucket (website: true), not mounted under the path of another site, with infrastructure.dns.domain set and SSL configured (infrastructure.ssl.certificateArn, or ssl.enabled with dns.hostedZoneId).`);if(Pt=Ys({failover:Ee.failover,primaryBucket:Qe,primaryRegion:this.mergedConfig.project.region||"us-east-1",where:vt}),Pt.replicate&&Ke.Properties){let Ot=`${Ye}FailoverReplicationRole`;this.builder.addResource(Ot,ea({primaryBucket:Qe,replicaBucket:Pt.replicaBucket}));let an=Ke.Properties;an.VersioningConfiguration={Status:"Enabled"},an.ReplicationConfiguration=ta({replicaBucket:Pt.replicaBucket,roleArn:{"Fn::GetAtt":[Ot,"Arn"]}})}}if(Te&&Ke.Properties)Ke.Properties.PublicAccessBlockConfiguration={BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!1,RestrictPublicBuckets:!1};if(this.builder.addResource(Ye,Ke),Ee.website){let vt=typeof Ee.website==="object"?Ee.website:{},Ot=Le.enableWebsiteHosting(Ke,vt.indexDocument||"index.html",vt.errorDocument);this.builder.addResource(Ye,Ot)}let Bt=this.normalizeMountPath(Ee);if(Te&&xe&&L&&!(Bt&&Ae!=="public")){let vt=`${N}${t}${Ae}CDN`.replace(/[^a-zA-Z0-9]/g,""),Ot=[];if(Ee.aliases&&Ee.aliases.length>0)Ot.push(...Ee.aliases);else if(Ae==="public"){if(Ot.push(L),m?.domains?.includes(`www.${L}`))Ot.push(`www.${L}`)}else if(Ae==="docs")Ot.push(`docs.${L}`);else if(Ae==="blog")Ot.push(`blog.${L}`);let an=typeof Ee.website==="object"?Ee.website:{},zt=Ee.spa===!0,ht=an.errorDocument||(zt?"index.html":"404.html"),Vt=[];if(zt)Vt.push({ErrorCode:403,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300});else Vt.push({ErrorCode:403,ResponseCode:404,ResponsePagePath:`/${ht}`,ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:404,ResponsePagePath:`/${ht}`,ErrorCachingMinTTL:300});let nn=ee&&Ot.length>0?{AcmCertificateArn:ee,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:Z&&Ot.length>0?{AcmCertificateArn:{Ref:Z},SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},wt=`S3-${N}-${t}-${Ae}`,Mt=this.mergedConfig.project.region||"us-east-1",Yn;if(!zt)Yn=`${N}${t}${Ae}UrlRewrite`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(Yn,{Type:"AWS::CloudFront::Function",Properties:{Name:`${N}-${t}-${Ae}-url-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`URL rewrite for ${N} ${t} ${Ae} - appends .html to extensionless paths`,Runtime:"cloudfront-js-2.0"},FunctionCode:`function handler(event) { var request = event.request; var uri = request.uri; if (uri.endsWith('/')) { request.uri += 'index.html'; }
100
+ else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`}});let Zn=[{Id:wt,DomainName:{"Fn::Sub":`\${${Ye}}.s3.${Mt}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:xe}}],Qr=[],Ki=[];if(this.shouldRouteStorageBucketToCompute(Ae,Ee)){this.appendComputeAppOrigin(Zn,Qr,Ki,N,t,this.resolveComputeCachePathPatterns(Ee.computeRoutes));for(let on of Me){let bs=`S3-${N}-${t}-${on.name}`,da=`${N}${t}${on.name}PathMountRewrite`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(da,{Type:"AWS::CloudFront::Function",Properties:{Name:`${N}-${t}-${on.name}-path-mount-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`Path mount rewrite for ${N} ${t} ${on.name} at ${on.mountPath}`,Runtime:"cloudfront-js-2.0"},FunctionCode:this.pathMountRewriteFunctionCode(on.mountPath,on.rewriteStyle)}}),Zn.push({Id:bs,DomainName:{"Fn::Sub":`\${${on.logicalId}}.s3.${Mt}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:xe}}),Ki.push(on.logicalId,da);for(let Gd of[on.mountPath,`${on.mountPath}/*`])Qr.push({PathPattern:Gd,TargetOriginId:bs,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"4135ea2d-6df8-44a3-9df3-4b5a84be39ad",FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[da,"FunctionARN"]}}]})}}let ur,rn=wt;if(Pt){let on=`${wt}-failover`;Object.assign(Zn[0],Pt.primaryConnection),Zn.push({Id:on,DomainName:Zs(Pt.replicaBucket,Pt.replicaRegion),OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:xe}}),ur=wo({primaryOriginId:wt,secondaryOriginId:on,statusCodes:Pt.statusCodes}),rn=ur.Items[0].Id}let Ar={Type:"AWS::CloudFront::Distribution",DependsOn:[Ye,xe,...Yn?[Yn]:[],...Ki],Properties:{DistributionConfig:{Enabled:!0,Comment:`${N} ${t} ${Ae} site`,DefaultRootObject:"index.html",Origins:Zn,...ur?{OriginGroups:ur}:{},DefaultCacheBehavior:{TargetOriginId:rn,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",...Yn?{FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[Yn,"FunctionARN"]}}]}:{}},...Qr.length>0?{CacheBehaviors:Qr}:{},...Ot.length>0?{Aliases:Ot}:{},ViewerCertificate:nn,PriceClass:"PriceClass_100",HttpVersion:"http2and3",IPV6Enabled:!0,CustomErrorResponses:Vt}}};if(Z)Ar.DependsOn.push(Z);if(Pt)fi(Ar.Properties.DistributionConfig,`infrastructure.storage.${Ae}`),this.storageFailoverReplicas.push({name:Ae,primaryBucket:Qe,primaryRegion:Mt,replicaBucket:Pt.replicaBucket,replicaRegion:Pt.replicaRegion,replicate:Pt.replicate,distributionArnOutput:`${Ae}CloudFrontDistributionArn`}),this.builder.addOutput(`${Ae}CloudFrontDistributionArn`,{Value:{"Fn::Sub":`arn:\${AWS::Partition}:cloudfront::\${AWS::AccountId}:distribution/\${${vt}}`},Description:`CloudFront distribution ARN for ${Ae} (scopes the bucket policy of the failover replica)`}),this.builder.addOutput(`${Ae}FailoverBucketName`,{Value:Pt.replicaBucket,Description:`Failover replica bucket for ${Ae} (${Pt.replicaRegion}), outside this stack`});this.builder.addResource(vt,Ar);let Ir=`${Ye}CloudFrontPolicy`;if(this.builder.addResource(Ir,{Type:"AWS::S3::BucketPolicy",DependsOn:[Ye,vt],Properties:{Bucket:{Ref:Ye},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${Ye}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${vt}}`}}}}]}}}),this.shouldRouteStorageBucketToCompute(Ae,Ee))for(let on of Me){let bs=`${on.logicalId}CloudFrontPolicy`;this.builder.addResource(bs,{Type:"AWS::S3::BucketPolicy",DependsOn:[on.logicalId,vt],Properties:{Bucket:{Ref:on.logicalId},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${on.logicalId}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${vt}}`}}}}]}}})}if(Ot.length>0)G.push({name:Ae,bucketLogicalId:Ye,distLogicalId:vt,oacLogicalId:xe,aliases:Ot});this.builder.addOutput(`${Ae}CloudFrontDomain`,{Value:{"Fn::GetAtt":[vt,"DomainName"]},Description:`CloudFront domain for ${Ae}`}),this.builder.addOutput(`${Ae}CloudFrontDistributionId`,{Value:{Ref:vt},Description:`CloudFront distribution ID for ${Ae}`})}if(this.builder.addOutput(`${Ae}BucketName`,{Value:{Ref:Ye},Description:`S3 bucket name for ${Ae}`}),Ae==="public")this.builder.addOutput("FrontendBucketName",{Value:{Ref:Ye},Description:"Frontend S3 bucket name"});if(Ae==="docs")this.builder.addOutput("DocsBucketName",{Value:{Ref:Ye},Description:"Documentation S3 bucket name"});if(Ae==="blog")this.builder.addOutput("BlogBucketName",{Value:{Ref:Ye},Description:"Blog S3 bucket name"})}}if(this.mergedConfig.infrastructure?.sftp){let Se=this.mergedConfig.infrastructure.sftp,xe=Ro.create({...Se,storage:this.resolveSftpStorage(Se,N,t)??Se.storage,slug:N,environment:t});for(let[Me,Ae]of Object.entries(xe.resources))this.builder.addResource(Me,Ae);this.builder.addOutput("SftpServerId",{Value:{Ref:xe.serverLogicalId},Description:"AWS Transfer Family SFTP server ID"}),this.builder.addOutput("SftpEndpoint",{Value:{"Fn::Sub":`\${${xe.serverLogicalId}}.server.transfer.\${AWS::Region}.amazonaws.com`},Description:"AWS Transfer Family SFTP endpoint"}),this.builder.addOutput("SftpStorageDomain",{Value:xe.domain,Description:"Storage backing the SFTP server (EFS or S3)"})}if(j&&G.length>0)for(let{name:Se,distLogicalId:xe,aliases:Me}of G)for(let Ae of Me){let Ee=Ae.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${Ee}ARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[xe],Properties:{HostedZoneId:j,Name:Ae,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[xe,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}}),this.builder.addResource(`${Ee}AAAARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[xe],Properties:{HostedZoneId:j,Name:Ae,Type:"AAAA",AliasTarget:{DNSName:{"Fn::GetAtt":[xe,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}})}if(this.mergedConfig.infrastructure?.databases){for(let[Se,xe]of Object.entries(this.mergedConfig.infrastructure.databases))if(xe.engine==="dynamodb"){let{table:Me,logicalId:Ae}=Ft.createTable({slug:N,environment:t,tableName:`${N}-${t}-${Se}`,partitionKey:xe.partitionKey||{name:"id",type:"S"},sortKey:xe.sortKey});this.builder.addResource(Ae,Me)}else if(xe.engine==="postgres"){let{dbInstance:Me,logicalId:Ae}=Ft.createPostgres({slug:N,environment:t,dbInstanceIdentifier:`${N}-${t}-${Se}`,masterUsername:xe.username||"admin",masterUserPassword:xe.password||"changeme123",allocatedStorage:xe.storage||20,dbInstanceClass:xe.instanceClass||"db.t3.micro"});this.builder.addResource(Ae,Me)}else if(xe.engine==="mysql"){let{dbInstance:Me,logicalId:Ae}=Ft.createMysql({slug:N,environment:t,dbInstanceIdentifier:`${N}-${t}-${Se}`,masterUsername:xe.username||"admin",masterUserPassword:xe.password||"changeme123",allocatedStorage:xe.storage||20,dbInstanceClass:xe.instanceClass||"db.t3.micro"});this.builder.addResource(Ae,Me)}}if(this.mergedConfig.infrastructure?.cdn)for(let[Se,xe]of Object.entries(this.mergedConfig.infrastructure.cdn)){if(!xe.origin)continue;let Me=typeof xe.customDomain==="string"?xe.customDomain:xe.customDomain?.domain||xe.domain,Ae=typeof xe.customDomain==="object"?xe.customDomain?.certificateArn:xe.certificateArn,Ee=Ae||ee,Te=`${N}${t}${Se}CDN`.replace(/[^a-zA-Z0-9]/g,""),Qe=`S3-${N}-${t}-${Se}-cdn`,Ke=[{Id:Qe,DomainName:xe.origin,OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginShield:xe.originShield?{Enabled:!0,OriginShieldRegion:xe.originShieldRegion||this.mergedConfig.environments[t]?.region||this.mergedConfig.project.region||"us-east-1"}:{Enabled:!1},...Ut(xe,`infrastructure.cdn.${Se}`)}],Ye=[],et=[],Pt=["GET","HEAD","OPTIONS"],Bt=Qe,vt;if(xe.failoverOrigin){gi(Pt,`the default cache behavior of infrastructure.cdn.${Se}`);let ht=`${Qe}-failover`,Vt=typeof xe.failoverOrigin==="string"?{domain:xe.failoverOrigin}:xe.failoverOrigin;if(!Vt.domain)throw Error(`infrastructure.cdn.${Se}.failoverOrigin.domain is required`);if(Vt.domain===xe.origin)throw Error(`infrastructure.cdn.${Se}: failoverOrigin must differ from origin (both are ${xe.origin})`);Ke.push({Id:ht,DomainName:Vt.domain,OriginPath:Vt.originPath||"",...Co(Vt.domain)?{S3OriginConfig:{OriginAccessIdentity:""}}:{CustomOriginConfig:{HTTPPort:80,HTTPSPort:443,OriginProtocolPolicy:"https-only"}},OriginShield:{Enabled:!1},...Ut(Vt,`infrastructure.cdn.${Se}.failoverOrigin`)}),vt=wo({primaryOriginId:Qe,secondaryOriginId:ht,statusCodes:xe.failoverStatusCodes}),Bt=vt.Items[0].Id}else if(xe.failoverStatusCodes)throw Error(`infrastructure.cdn.${Se}: failoverStatusCodes needs failoverOrigin to fail over to`);if(xe.routeCompute)this.appendComputeAppOrigin(Ke,Ye,et,N,t,this.resolveComputeCachePathPatterns(xe.computeRoutes));let Ot=Ee&&Me?{AcmCertificateArn:Z&&!Ae?{Ref:Z}:Ee,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},an=[...et];if(Z&&!Ae&&Me)an.push(Z);let zt={Type:"AWS::CloudFront::Distribution",...an.length>0?{DependsOn:an}:{},Properties:{DistributionConfig:{Enabled:!0,Comment:`${N} ${t} ${Se} CDN`,DefaultRootObject:"index.html",Origins:Ke,...vt?{OriginGroups:vt}:{},DefaultCacheBehavior:{TargetOriginId:Bt,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:Pt,CachedMethods:["GET","HEAD","OPTIONS"],Compress:xe.compress!==!1,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6"},...Ye.length>0?{CacheBehaviors:Ye}:{},...Me&&Ee?{Aliases:[Me]}:{},ViewerCertificate:Ot,PriceClass:"PriceClass_100",HttpVersion:xe.http3?"http2and3":"http2"}}};if(fi(zt.Properties.DistributionConfig,`infrastructure.cdn.${Se}`),this.builder.addResource(Te,zt),j&&Me&&Ee){let ht=Me.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${ht}CdnARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[Te,...Z&&!Ae?[Z]:[]],Properties:{HostedZoneId:j,Name:Me,Type:"A",AliasTarget:{HostedZoneId:"Z2FDTNDATAQYW2",DNSName:{"Fn::GetAtt":[Te,"DomainName"]}}}})}}if(this.mergedConfig.infrastructure?.queues)for(let[Se,xe]of Object.entries(this.mergedConfig.infrastructure.queues)){if(!this.shouldDeploy(xe))continue;let{queue:Me,logicalId:Ae}=pe.createQueue({slug:N,environment:t,name:`${N}-${t}-${Se}`,fifo:xe.fifo,visibilityTimeout:xe.visibilityTimeout,messageRetentionPeriod:xe.messageRetentionPeriod,delaySeconds:xe.delaySeconds,maxMessageSize:xe.maxMessageSize,receiveMessageWaitTime:xe.receiveMessageWaitTime,contentBasedDeduplication:xe.contentBasedDeduplication,encrypted:xe.encrypted,kmsKeyId:xe.kmsKeyId});this.builder.addResource(Ae,Me);let Ee;if(xe.deadLetterQueue){let{deadLetterQueue:Te,updatedSourceQueue:Qe,deadLetterLogicalId:Ke}=pe.createDeadLetterQueue(Ae,{slug:N,environment:t,maxReceiveCount:xe.maxReceiveCount});Ee=Ke,this.builder.addResource(Ke,Te);let et=this.builder.getResources()[Ae];if(et?.Properties)et.Properties.RedrivePolicy=Qe.Properties?.RedrivePolicy}if(xe.trigger){let Te=xe.trigger,Qe=`${N}${t}${Te.functionName}`.replace(/[^a-zA-Z0-9]/g,""),Ke={Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:{"Fn::GetAtt":[Ae,"Arn"]},FunctionName:{Ref:Qe},BatchSize:Te.batchSize||10,MaximumBatchingWindowInSeconds:Te.batchWindow||0,Enabled:!0,...Te.reportBatchItemFailures!==!1&&{FunctionResponseTypes:["ReportBatchItemFailures"]},...Te.maxConcurrency&&{ScalingConfig:{MaximumConcurrency:Te.maxConcurrency}},...Te.filterPattern&&{FilterCriteria:{Filters:[{Pattern:JSON.stringify(Te.filterPattern)}]}}},DependsOn:[Ae,Qe]};this.builder.addResource(`${Ae}Trigger`,Ke)}if(xe.alarms?.enabled){let Te=xe.alarms,Qe=Te.notificationTopicArn;if(!Qe&&Te.notificationEmails?.length){let et=`${Ae}AlarmTopic`;this.builder.addResource(et,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${N}-${t}-${Se}-alarms`,DisplayName:`${Se} Queue Alarms`}}),Te.notificationEmails.forEach((Pt,Bt)=>{this.builder.addResource(`${et}Sub${Bt}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:et},Protocol:"email",Endpoint:Pt}})}),Qe={Ref:et}}let Ke=Te.queueDepthThreshold||1000;this.builder.addResource(`${Ae}DepthAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-${Se}-queue-depth`,AlarmDescription:`Queue ${Se} depth exceeds ${Ke} messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:Ke,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[Ae,"QueueName"]}}],...Qe&&{AlarmActions:[Qe],OKActions:[Qe]}}});let Ye=Te.messageAgeThreshold||3600;if(this.builder.addResource(`${Ae}AgeAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-${Se}-message-age`,AlarmDescription:`Queue ${Se} oldest message exceeds ${Ye} seconds`,MetricName:"ApproximateAgeOfOldestMessage",Namespace:"AWS/SQS",Statistic:"Maximum",Period:300,EvaluationPeriods:2,Threshold:Ye,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[Ae,"QueueName"]}}],...Qe&&{AlarmActions:[Qe],OKActions:[Qe]}}}),Ee&&Te.dlqAlarm!==!1)this.builder.addResource(`${Ee}Alarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-${Se}-dlq-messages`,AlarmDescription:`Dead letter queue for ${Se} has messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Sum",Period:300,EvaluationPeriods:1,Threshold:0,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[Ee,"QueueName"]}}],...Qe&&{AlarmActions:[Qe]}}})}if(xe.subscribe){let Te=xe.subscribe,Qe=Te.topicArn;if(!Qe&&Te.topicName)Qe={Ref:Te.topicName};if(Qe){this.builder.addResource(`${Ae}SnsPolicy`,{Type:"AWS::SQS::QueuePolicy",Properties:{Queues:[{Ref:Ae}],PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"sqs:SendMessage",Resource:{"Fn::GetAtt":[Ae,"Arn"]},Condition:{ArnEquals:{"aws:SourceArn":Qe}}}]}}});let Ke={TopicArn:Qe,Protocol:"sqs",Endpoint:{"Fn::GetAtt":[Ae,"Arn"]},RawMessageDelivery:Te.rawMessageDelivery||!1};if(Te.filterPolicy)Ke.FilterPolicy=Te.filterPolicy,Ke.FilterPolicyScope=Te.filterPolicyScope||"MessageAttributes";this.builder.addResource(`${Ae}SnsSub`,{Type:"AWS::SNS::Subscription",Properties:Ke,DependsOn:`${Ae}SnsPolicy`})}}}if(this.mergedConfig.infrastructure?.realtime?.enabled)if((this.mergedConfig.infrastructure.realtime.mode||"serverless")==="server")this.generateRealtimeServerResources(N,t);else this.generateRealtimeResources(N,t);let ne=this.mergedConfig.infrastructure?.redirects;if(ne){let Se=ne.target||this.mergedConfig.infrastructure?.dns?.domain||"",xe=ne.protocol||"https";if(ne.domains?.length&&Se)for(let Me of ne.domains){let{bucket:Ae,bucketPolicy:Ee,logicalId:Te,policyLogicalId:Qe}=Et.createDomainRedirectBucket({slug:N,environment:t,sourceDomain:Me,targetDomain:Se,protocol:xe});this.builder.addResource(Te,Ae),this.builder.addResource(Qe,Ee)}if(ne.paths&&Object.keys(ne.paths).length>0){let Me=Et.fromMapping(ne.paths,{statusCode:ne.statusCode||301}),{function:Ae,logicalId:Ee}=Et.createPathRedirectFunction({slug:N,environment:t,rules:Me});this.builder.addResource(Ee,Ae)}}if(this.mergedConfig.infrastructure?.monitoring?.alarms)for(let[Se,xe]of Object.entries(this.mergedConfig.infrastructure.monitoring.alarms)){let{alarm:Me,logicalId:Ae}=yi.createAlarm({slug:N,environment:t,alarmName:`${N}-${t}-${Se}`,metricName:xe.metricName||"Errors",namespace:xe.namespace||"AWS/Lambda",threshold:xe.threshold||1,comparisonOperator:xe.comparisonOperator||"GreaterThanThreshold"});this.builder.addResource(Ae,Me)}let oe=this.mergedConfig.infrastructure?.cache;if(oe){let Se=oe.type||"redis";if(Se==="redis"){this.generateNetworkInfrastructure(N,t);let xe=oe.redis||{},{replicationGroup:Me,subnetGroup:Ae,logicalId:Ee,subnetGroupId:Te}=hi.createRedis({slug:N,environment:t,nodeType:xe.nodeType||oe.nodeType||"cache.t3.micro",engineVersion:xe.engineVersion||"7.1",port:xe.port||6379,numCacheClusters:xe.numCacheNodes||2,automaticFailover:xe.automaticFailoverEnabled!==!1,atRestEncryption:!0,transitEncryption:!0,snapshotRetentionDays:xe.snapshotRetentionLimit||7,snapshotWindow:xe.snapshotWindow,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(Ee,Me),Ae&&Te)this.builder.addResource(Te,Ae);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[Ee,"PrimaryEndPoint.Address"]},Description:"Redis primary endpoint address"}),this.builder.addOutput("CachePort",{Value:{"Fn::GetAtt":[Ee,"PrimaryEndPoint.Port"]},Description:"Redis primary endpoint port"})}else if(Se==="memcached"){this.generateNetworkInfrastructure(N,t);let xe=oe.elasticache||{},{cluster:Me,subnetGroup:Ae,logicalId:Ee,subnetGroupId:Te}=hi.createMemcached({slug:N,environment:t,nodeType:xe.nodeType||oe.nodeType||"cache.t3.micro",engineVersion:xe.engineVersion||"1.6.22",numCacheNodes:xe.numCacheNodes||2,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(Ee,Me),Ae&&Te)this.builder.addResource(Te,Ae);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[Ee,"ConfigurationEndpoint.Address"]},Description:"Memcached configuration endpoint address"})}}let be=this.mergedConfig.infrastructure?.email;if(be){let Se=be.domain||this.mergedConfig.infrastructure?.dns?.domain;if(Se){let{emailIdentity:xe,logicalId:Me}=D.verifyDomain({domain:Se,slug:N,environment:t,enableDkim:be.enableDkim!==!1,dkimKeyLength:be.dkimKeyLength||"RSA_2048_BIT"});if(this.builder.addResource(Me,xe),be.configurationSet!==!1){let{configurationSet:Te,logicalId:Qe}=D.createConfigurationSet({slug:N,environment:t});this.builder.addResource(Qe,Te)}let Ae=be.hostedZoneId||this.mergedConfig.infrastructure?.dns?.hostedZoneId;if(Ae){if(be.enableDkim!==!1)for(let et=1;et<=3;et++){let Pt=`DkimRecord${et}${Se.replace(/\./g,"")}`;this.builder.addResource(Pt,{Type:"AWS::Route53::RecordSet",DependsOn:[Me],Properties:{HostedZoneId:Ae,Name:{"Fn::GetAtt":[Me,`DkimDNSTokenName${et}`]},Type:"CNAME",TTL:1800,ResourceRecords:[{"Fn::GetAtt":[Me,`DkimDNSTokenValue${et}`]}]}})}let{record:Te,logicalId:Qe}=D.createSpfRecord(Se,Ae);this.builder.addResource(Qe,Te);let{record:Ke,logicalId:Ye}=D.createDmarcRecord(Se,Ae,{policy:"none",reportingEmail:be.dmarcReportingEmail||`dmarc-reports@${Se}`});this.builder.addResource(Ye,Ke)}this.builder.addOutput("EmailDomain",{Value:Se,Description:"SES verified email domain"});let Ee=be.server;if(Ee?.enabled&&Ae){let Te=this.mergedConfig.environments[t]?.region||this.mergedConfig.project.region||"us-east-1",Qe=`${N}-${t}-email`,{role:Ke,policy:Ye,roleLogicalId:et,policyLogicalId:Pt}=D.createEmailLambdaRole({slug:N,environment:t,s3BucketArn:`arn:aws:s3:::${Qe}`,sesIdentityArn:`arn:aws:ses:${Te}:*:identity/${Se}`});this.builder.addResource(et,Ke),this.builder.addResource(Pt,Ye);let{function:Bt,permission:vt,logicalId:Ot,permissionLogicalId:an}=D.createInboundEmailLambda({slug:N,environment:t,roleArn:{"Fn::GetAtt":[et,"Arn"]},s3BucketName:Qe,organizedPrefix:"mailboxes/"});this.builder.addResource(Ot,Bt),this.builder.addResource(an,vt);let zt=D.createInboundEmailSetup({slug:N,environment:t,domain:Se,s3BucketName:Qe,s3KeyPrefix:"inbox/",region:Te,hostedZoneId:Ae,lambdaFunctionArn:{"Fn::GetAtt":[Ot,"Arn"]}});for(let[Vt,nn]of Object.entries(zt.resources)){if(nn.Type==="AWS::SES::ReceiptRule")nn.DependsOn=[an,Ot];this.builder.addResource(Vt,nn)}this.builder.addOutput("InboundEmailLambda",{Value:{Ref:Ot},Description:"Inbound email processing Lambda function"}),this.builder.addOutput("EmailBucket",{Value:Qe,Description:"S3 bucket for email storage"});let ht=this.serverEipLogicalIds.get("app");if(ht&&Ae){let nn=`${Ee?.subdomain||"mail"}.${Se}`,wt=nn.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${wt}ARecord`,{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:Ae,Name:nn,Type:"A",TTL:"300",ResourceRecords:[{Ref:ht}]}})}this.builder.addOutput("MailHost",{Value:`mail.${Se}`,Description:"Mail server hostname for SMTP/IMAP clients"})}}}let we=this.mergedConfig.infrastructure?.search;if(we){let Se=we.vpc?{subnetIds:[{Ref:"PublicSubnet1"}],securityGroupIds:[]}:void 0;if(Se)this.generateNetworkInfrastructure(N,t);let{domain:xe,logicalId:Me}=jt.createDomain({slug:N,environment:t,engineVersion:we.engineVersion||"OpenSearch_2.11",instanceType:we.instanceType||"t3.small.search",instanceCount:we.instanceCount||1,volumeSize:we.volumeSize||10,volumeType:we.volumeType||"gp3",dedicatedMaster:we.dedicatedMaster||!1,dedicatedMasterType:we.dedicatedMasterType,dedicatedMasterCount:we.dedicatedMasterCount||3,multiAz:we.multiAz||!1,encryption:we.encryption||{atRest:!0,nodeToNode:!0},advancedSecurity:we.advancedSecurity,autoTune:we.autoTune!==!1,vpc:Se});this.builder.addResource(Me,xe),this.builder.addOutput("SearchDomainEndpoint",{Value:{"Fn::GetAtt":[Me,"DomainEndpoint"]},Description:"OpenSearch domain endpoint"}),this.builder.addOutput("SearchDomainArn",{Value:{"Fn::GetAtt":[Me,"Arn"]},Description:"OpenSearch domain ARN"})}let Ce=this.mergedConfig.infrastructure?.fileSystem;if(Ce&&Object.keys(Ce).length>0){this.generateNetworkInfrastructure(N,t);for(let[Se,xe]of Object.entries(Ce)){let{fileSystem:Me,logicalId:Ae}=Ze.createFileSystem({slug:`${N}-${Se}`,environment:t,encrypted:xe.encrypted!==!1,performanceMode:xe.performanceMode||"generalPurpose",throughputMode:xe.throughputMode||"bursting",enableBackup:!0});this.builder.addResource(Ae,Me);let{securityGroup:Ee,logicalId:Te}=Ze.createEfsSecurityGroup({slug:`${N}-${Se}`,environment:t,vpcId:{Ref:"VPC"},sourceCidrBlocks:["10.0.0.0/16"]});this.builder.addResource(Te,Ee);let{mountTargets:Qe,logicalIds:Ke}=Ze.createMultiAzMountTargets(Ae,{slug:`${N}-${Se}`,environment:t,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],securityGroupId:{Ref:Te}});for(let Ye=0;Ye<Qe.length;Ye++)this.builder.addResource(Ke[Ye],Qe[Ye]);this.builder.addOutput(`${Se}FileSystemId`,{Value:{Ref:Ae},Description:`EFS file system ID for ${Se}`})}}let De=this.mergedConfig.infrastructure?.ai;if(De){let Se=De.service||"ecs",xe=De.models||["*"],Me=De.allowStreaming!==!1,Ae;if(Se==="ecs")Ae=Ue.enableBedrockForEcs({slug:N,environment:t,models:xe,allowStreaming:Me});else if(Se==="ec2")Ae=Ue.enableBedrockForEc2({slug:N,environment:t,models:xe,allowStreaming:Me});else if(Se==="lambda")Ae=Ue.enableBedrockForLambda({slug:N,environment:t,models:xe,allowStreaming:Me});else Ae=Ue.createBedrockRole(Se,{slug:N,environment:t,models:xe,allowStreaming:Me});if(this.builder.addResource(Ae.logicalId,Ae.role),De.allowAsync){let{policy:Ee,logicalId:Te}=Ue.createBedrockPolicy({slug:N,environment:t,models:xe,allowStreaming:Me,allowAsync:!0});this.builder.addResource(Te,Ee)}this.builder.addOutput("BedrockRoleArn",{Value:{"Fn::GetAtt":[Ae.logicalId,"Arn"]},Description:"IAM role ARN with Bedrock permissions"})}}generateRealtimeResources(N,t){let m=this.mergedConfig.infrastructure?.realtime;if(!m)return;let A=m.name||`${N}-${t}-realtime`,L=m.scaling||{},j=m.storage||{type:"dynamodb"},G=L.handlerMemory||256,Z=L.handlerTimeout||30,ee=`${N}${t}RealtimeConnections`.replace(/[^a-zA-Z0-9]/g,""),ne=`${N}${t}RealtimeChannels`.replace(/[^a-zA-Z0-9]/g,"");if(j.type==="dynamodb"){let Ke=j.dynamodb||{},Ye=Ke.billingMode||"PAY_PER_REQUEST";this.builder.addResource(ee,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${N}-${t}-realtime-connections`,BillingMode:Ye,AttributeDefinitions:[{AttributeName:"connectionId",AttributeType:"S"},{AttributeName:"userId",AttributeType:"S"}],KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],GlobalSecondaryIndexes:[{IndexName:"userId-index",KeySchema:[{AttributeName:"userId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...Ye==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Ke.readCapacity||5,WriteCapacityUnits:Ke.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...Ye==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Ke.readCapacity||5,WriteCapacityUnits:Ke.writeCapacity||5}},...Ke.pointInTimeRecovery&&{PointInTimeRecoverySpecification:{PointInTimeRecoveryEnabled:!0}},Tags:[{Key:"Name",Value:`${N}-${t}-realtime-connections`},{Key:"Environment",Value:t}]}}),this.builder.addResource(ne,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${N}-${t}-realtime-channels`,BillingMode:Ye,AttributeDefinitions:[{AttributeName:"channel",AttributeType:"S"},{AttributeName:"connectionId",AttributeType:"S"}],KeySchema:[{AttributeName:"channel",KeyType:"HASH"},{AttributeName:"connectionId",KeyType:"RANGE"}],GlobalSecondaryIndexes:[{IndexName:"connectionId-index",KeySchema:[{AttributeName:"connectionId",KeyType:"HASH"}],Projection:{ProjectionType:"ALL"},...Ye==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Ke.readCapacity||5,WriteCapacityUnits:Ke.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...Ye==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:Ke.readCapacity||5,WriteCapacityUnits:Ke.writeCapacity||5}},Tags:[{Key:"Name",Value:`${N}-${t}-realtime-channels`},{Key:"Environment",Value:t}]}})}let oe=`${N}${t}RealtimeRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(oe,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-realtime-handler-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"lambda.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"],Policies:[{PolicyName:"RealtimeHandlerPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["dynamodb:GetItem","dynamodb:PutItem","dynamodb:DeleteItem","dynamodb:Query","dynamodb:Scan","dynamodb:UpdateItem"],Resource:[{"Fn::GetAtt":[ee,"Arn"]},{"Fn::Sub":`\${${ee}.Arn}/index/*`},{"Fn::GetAtt":[ne,"Arn"]},{"Fn::Sub":`\${${ne}.Arn}/index/*`}]},{Effect:"Allow",Action:"execute-api:ManageConnections",Resource:{"Fn::Sub":"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"}}]}}]}});let be=`${N}${t}RealtimeConnect`.replace(/[^a-zA-Z0-9]/g,""),we=`${N}${t}RealtimeDisconnect`.replace(/[^a-zA-Z0-9]/g,""),Ce=`${N}${t}RealtimeMessage`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(be,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${N}-${t}-realtime-connect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[oe,"Arn"]},MemorySize:G,Timeout:Z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:ee},CHANNELS_TABLE:{Ref:ne},ENVIRONMENT:t}},Code:{ZipFile:this.generateConnectHandlerCode()}},DependsOn:[oe,ee]}),this.builder.addResource(we,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${N}-${t}-realtime-disconnect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[oe,"Arn"]},MemorySize:G,Timeout:Z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:ee},CHANNELS_TABLE:{Ref:ne},ENVIRONMENT:t}},Code:{ZipFile:this.generateDisconnectHandlerCode()}},DependsOn:[oe,ee,ne]}),this.builder.addResource(Ce,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${N}-${t}-realtime-message`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[oe,"Arn"]},MemorySize:G,Timeout:Z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:ee},CHANNELS_TABLE:{Ref:ne},ENVIRONMENT:t}},Code:{ZipFile:this.generateMessageHandlerCode()}},DependsOn:[oe,ee,ne]});let De=`${N}${t}RealtimeApi`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(De,{Type:"AWS::ApiGatewayV2::Api",Properties:{Name:A,ProtocolType:"WEBSOCKET",RouteSelectionExpression:"$request.body.action",Tags:{Name:A,Environment:t}}});let Se=`${be}Permission`,xe=`${we}Permission`,Me=`${Ce}Permission`;this.builder.addResource(Se,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:be},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${De}}/*/$connect`}}}),this.builder.addResource(xe,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:we},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${De}}/*/$disconnect`}}}),this.builder.addResource(Me,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:Ce},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${De}}/*/$default`}}});let Ae=`${De}ConnectInteg`,Ee=`${De}DisconnectInteg`,Te=`${De}MessageInteg`;this.builder.addResource(Ae,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:De},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${be}.Arn}/invocations`}}}),this.builder.addResource(Ee,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:De},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${we}.Arn}/invocations`}}}),this.builder.addResource(Te,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:De},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${Ce}.Arn}/invocations`}}}),this.builder.addResource(`${De}ConnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:De},RouteKey:"$connect",AuthorizationType:"NONE",Target:{"Fn::Sub":`integrations/\${${Ae}}`}}}),this.builder.addResource(`${De}DisconnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:De},RouteKey:"$disconnect",Target:{"Fn::Sub":`integrations/\${${Ee}}`}}}),this.builder.addResource(`${De}DefaultRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:De},RouteKey:"$default",Target:{"Fn::Sub":`integrations/\${${Te}}`}}});let Qe=`${De}Stage`;if(this.builder.addResource(Qe,{Type:"AWS::ApiGatewayV2::Stage",Properties:{ApiId:{Ref:De},StageName:t,AutoDeploy:!0,DefaultRouteSettings:{ThrottlingBurstLimit:L.messagesPerSecond||1000,ThrottlingRateLimit:L.messagesPerSecond||1000}}}),m.monitoring?.enabled){let Ke=m.monitoring,Ye=Ke.notificationTopicArn;if(!Ye&&Ke.notificationEmails?.length){let et=`${De}AlarmTopic`;this.builder.addResource(et,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${N}-${t}-realtime-alarms`,DisplayName:"Realtime WebSocket Alarms"}}),Ke.notificationEmails.forEach((Pt,Bt)=>{this.builder.addResource(`${et}Sub${Bt}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:et},Protocol:"email",Endpoint:Pt}})}),Ye={Ref:et}}if(Ke.connectionThreshold)this.builder.addResource(`${De}ConnectionAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-realtime-connections`,AlarmDescription:`WebSocket connections exceed ${Ke.connectionThreshold}`,MetricName:"ConnectCount",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:300,EvaluationPeriods:2,Threshold:Ke.connectionThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:De}}],...Ye&&{AlarmActions:[Ye]}}});if(Ke.errorThreshold)this.builder.addResource(`${De}ErrorAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-realtime-errors`,AlarmDescription:`WebSocket errors exceed ${Ke.errorThreshold}/min`,MetricName:"ExecutionError",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:60,EvaluationPeriods:3,Threshold:Ke.errorThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:De}}],...Ye&&{AlarmActions:[Ye]}}})}this.builder.addOutput(`${De}Endpoint`,{Description:"WebSocket API endpoint URL",Value:{"Fn::Sub":`wss://\${${De}}.execute-api.\${AWS::Region}.amazonaws.com/${t}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}}),this.builder.addOutput(`${De}Id`,{Description:"WebSocket API ID",Value:{Ref:De},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-api-id"}}})}generateConnectHandlerCode(){return`
101
101
  const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
102
102
  const { DynamoDBDocumentClient, PutCommand } = require('@aws-sdk/lib-dynamodb');
103
103
 
@@ -256,7 +256,7 @@ catch (error) {
256
256
  return { statusCode: 500, body: JSON.stringify({ error: 'Internal error' }) };
257
257
  }
258
258
  };
259
- `.trim()}generateRealtimeServerResources(N,t){let p=this.mergedConfig.infrastructure?.realtime;if(!p)return;let A=p.server||{},L=A.port||6001,j=A.instances||1,G=`${N}${t}RealtimeSG`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(G,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`Security group for ${N} realtime WebSocket server`,GroupName:`${N}-${t}-realtime-sg`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:L,ToPort:L,CidrIp:"0.0.0.0/0",Description:"WebSocket connections"},{IpProtocol:"tcp",FromPort:443,ToPort:443,CidrIp:"0.0.0.0/0",Description:"HTTPS/WSS connections"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${N}-${t}-realtime-sg`},{Key:"Environment",Value:t}]}});let Z=`${N}${t}RealtimeTaskDef`.replace(/[^a-zA-Z0-9]/g,""),ee=`${N}${t}RealtimeTaskRole`.replace(/[^a-zA-Z0-9]/g,""),re=`${N}${t}RealtimeExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(re,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-realtime-exec-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]}}),this.builder.addResource(ee,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-realtime-task-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},Policies:[{PolicyName:"RealtimeTaskPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"},...A.redis?.enabled?[{Effect:"Allow",Action:["elasticache:DescribeCacheClusters","elasticache:DescribeReplicationGroups"],Resource:"*"}]:[]]}}]}});let oe=[{Name:"BROADCAST_HOST",Value:A.host||"0.0.0.0"},{Name:"BROADCAST_PORT",Value:String(L)},{Name:"NODE_ENV",Value:t==="production"?"production":"development"}];if(A.redis?.enabled){if(A.redis.useElastiCache)oe.push({Name:"REDIS_HOST",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Address"]}}),oe.push({Name:"REDIS_PORT",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Port"]}});else oe.push({Name:"REDIS_HOST",Value:A.redis.host||"localhost"}),oe.push({Name:"REDIS_PORT",Value:String(A.redis.port||6379)});if(A.redis.keyPrefix)oe.push({Name:"REDIS_KEY_PREFIX",Value:A.redis.keyPrefix})}this.builder.addResource(Z,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${N}-${t}-realtime`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:"512",Memory:"1024",ExecutionRoleArn:{"Fn::GetAtt":[re,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[ee,"Arn"]},ContainerDefinitions:[{Name:"realtime",Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${N}-realtime:latest`},Essential:!0,PortMappings:[{ContainerPort:L,Protocol:"tcp"}],Environment:oe,LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":`/ecs/${N}-${t}-realtime`,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":"realtime"}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${L}${A.healthCheckPath||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${N}-${t}-realtime`},{Key:"Environment",Value:t}]},DependsOn:[re,ee]});let be=`${N}${t}RealtimeLogs`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(be,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:`/ecs/${N}-${t}-realtime`,RetentionInDays:30}});let we=`${N}${t}RealtimeService`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(we,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${N}-${t}-realtime`,Cluster:{Ref:"ECSCluster"},TaskDefinition:{Ref:Z},DesiredCount:j,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:G}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},HealthCheckGracePeriodSeconds:60,Tags:[{Key:"Name",Value:`${N}-${t}-realtime`},{Key:"Environment",Value:t}]},DependsOn:[Z,G,be]}),A.autoScaling){let Ce=A.autoScaling,Ee=`${N}${t}RealtimeScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(Ee,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:Ce.max||10,MinCapacity:Ce.min||1,ResourceId:{"Fn::Sub":`service/\${ECSCluster}/${N}-${t}-realtime`},RoleARN:{"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"},ScalableDimension:"ecs:service:DesiredCount",ServiceNamespace:"ecs"},DependsOn:we}),Ce.targetCPU)this.builder.addResource(`${N}${t}RealtimeCPUPolicy`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${N}-${t}-realtime-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:Ee},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:Ce.targetCPU,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(p.monitoring?.enabled){let Ce=p.monitoring,Ee=Ce.notificationTopicArn;if(!Ee&&Ce.notificationEmails?.length){let Se=`${N}${t}RealtimeAlarmTopic`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Se,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${N}-${t}-realtime-alarms`,DisplayName:"Realtime Server Alarms"}}),Ce.notificationEmails.forEach((De,_e)=>{this.builder.addResource(`${Se}Sub${_e}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:Se},Protocol:"email",Endpoint:De}})}),Ee={Ref:Se}}this.builder.addResource(`${N}${t}RealtimeCPUAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-realtime-high-cpu`,AlarmDescription:"Realtime server CPU utilization is high",MetricName:"CPUUtilization",Namespace:"AWS/ECS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:80,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ClusterName",Value:{Ref:"ECSCluster"}},{Name:"ServiceName",Value:`${N}-${t}-realtime`}],...Ee&&{AlarmActions:[Ee]}},DependsOn:we}),this.builder.addResource(`${N}${t}RealtimeMemoryAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-realtime-high-memory`,AlarmDescription:"Realtime server memory utilization is high",MetricName:"MemoryUtilization",Namespace:"AWS/ECS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:80,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ClusterName",Value:{Ref:"ECSCluster"}},{Name:"ServiceName",Value:`${N}-${t}-realtime`}],...Ee&&{AlarmActions:[Ee]}},DependsOn:we})}this.builder.addOutput(`${N}${t}RealtimeEndpoint`.replace(/[^a-zA-Z0-9]/g,""),{Description:"Realtime WebSocket server endpoint",Value:{"Fn::Sub":`wss://${N}-${t}-realtime.\${AWS::Region}.elb.amazonaws.com:${L}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}})}generateBroadcastConfig(){let N=this.mergedConfig.infrastructure?.realtime;if(!N||N.mode!=="server")return"";let t=N.server||{};return`import type { BroadcastConfig } from 'ts-broadcasting'
259
+ `.trim()}generateRealtimeServerResources(N,t){let m=this.mergedConfig.infrastructure?.realtime;if(!m)return;let A=m.server||{},L=A.port||6001,j=A.instances||1,G=`${N}${t}RealtimeSG`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(G,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`Security group for ${N} realtime WebSocket server`,GroupName:`${N}-${t}-realtime-sg`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:L,ToPort:L,CidrIp:"0.0.0.0/0",Description:"WebSocket connections"},{IpProtocol:"tcp",FromPort:443,ToPort:443,CidrIp:"0.0.0.0/0",Description:"HTTPS/WSS connections"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${N}-${t}-realtime-sg`},{Key:"Environment",Value:t}]}});let Z=`${N}${t}RealtimeTaskDef`.replace(/[^a-zA-Z0-9]/g,""),ee=`${N}${t}RealtimeTaskRole`.replace(/[^a-zA-Z0-9]/g,""),ne=`${N}${t}RealtimeExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(ne,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-realtime-exec-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},ManagedPolicyArns:["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]}}),this.builder.addResource(ee,{Type:"AWS::IAM::Role",Properties:{RoleName:`${N}-${t}-realtime-task-role`,AssumeRolePolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"ecs-tasks.amazonaws.com"},Action:"sts:AssumeRole"}]},Policies:[{PolicyName:"RealtimeTaskPolicy",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:"*"},...A.redis?.enabled?[{Effect:"Allow",Action:["elasticache:DescribeCacheClusters","elasticache:DescribeReplicationGroups"],Resource:"*"}]:[]]}}]}});let oe=[{Name:"BROADCAST_HOST",Value:A.host||"0.0.0.0"},{Name:"BROADCAST_PORT",Value:String(L)},{Name:"NODE_ENV",Value:t==="production"?"production":"development"}];if(A.redis?.enabled){if(A.redis.useElastiCache)oe.push({Name:"REDIS_HOST",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Address"]}}),oe.push({Name:"REDIS_PORT",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Port"]}});else oe.push({Name:"REDIS_HOST",Value:A.redis.host||"localhost"}),oe.push({Name:"REDIS_PORT",Value:String(A.redis.port||6379)});if(A.redis.keyPrefix)oe.push({Name:"REDIS_KEY_PREFIX",Value:A.redis.keyPrefix})}this.builder.addResource(Z,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${N}-${t}-realtime`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:"512",Memory:"1024",ExecutionRoleArn:{"Fn::GetAtt":[ne,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[ee,"Arn"]},ContainerDefinitions:[{Name:"realtime",Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${N}-realtime:latest`},Essential:!0,PortMappings:[{ContainerPort:L,Protocol:"tcp"}],Environment:oe,LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":`/ecs/${N}-${t}-realtime`,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":"realtime"}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${L}${A.healthCheckPath||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${N}-${t}-realtime`},{Key:"Environment",Value:t}]},DependsOn:[ne,ee]});let be=`${N}${t}RealtimeLogs`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(be,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:`/ecs/${N}-${t}-realtime`,RetentionInDays:30}});let we=`${N}${t}RealtimeService`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(we,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${N}-${t}-realtime`,Cluster:{Ref:"ECSCluster"},TaskDefinition:{Ref:Z},DesiredCount:j,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:G}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},HealthCheckGracePeriodSeconds:60,Tags:[{Key:"Name",Value:`${N}-${t}-realtime`},{Key:"Environment",Value:t}]},DependsOn:[Z,G,be]}),A.autoScaling){let Ce=A.autoScaling,De=`${N}${t}RealtimeScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(De,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:Ce.max||10,MinCapacity:Ce.min||1,ResourceId:{"Fn::Sub":`service/\${ECSCluster}/${N}-${t}-realtime`},RoleARN:{"Fn::Sub":"arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"},ScalableDimension:"ecs:service:DesiredCount",ServiceNamespace:"ecs"},DependsOn:we}),Ce.targetCPU)this.builder.addResource(`${N}${t}RealtimeCPUPolicy`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${N}-${t}-realtime-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:De},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:Ce.targetCPU,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(m.monitoring?.enabled){let Ce=m.monitoring,De=Ce.notificationTopicArn;if(!De&&Ce.notificationEmails?.length){let Se=`${N}${t}RealtimeAlarmTopic`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Se,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${N}-${t}-realtime-alarms`,DisplayName:"Realtime Server Alarms"}}),Ce.notificationEmails.forEach((xe,Me)=>{this.builder.addResource(`${Se}Sub${Me}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:Se},Protocol:"email",Endpoint:xe}})}),De={Ref:Se}}this.builder.addResource(`${N}${t}RealtimeCPUAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-realtime-high-cpu`,AlarmDescription:"Realtime server CPU utilization is high",MetricName:"CPUUtilization",Namespace:"AWS/ECS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:80,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ClusterName",Value:{Ref:"ECSCluster"}},{Name:"ServiceName",Value:`${N}-${t}-realtime`}],...De&&{AlarmActions:[De]}},DependsOn:we}),this.builder.addResource(`${N}${t}RealtimeMemoryAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${N}-${t}-realtime-high-memory`,AlarmDescription:"Realtime server memory utilization is high",MetricName:"MemoryUtilization",Namespace:"AWS/ECS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:80,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ClusterName",Value:{Ref:"ECSCluster"}},{Name:"ServiceName",Value:`${N}-${t}-realtime`}],...De&&{AlarmActions:[De]}},DependsOn:we})}this.builder.addOutput(`${N}${t}RealtimeEndpoint`.replace(/[^a-zA-Z0-9]/g,""),{Description:"Realtime WebSocket server endpoint",Value:{"Fn::Sub":`wss://${N}-${t}-realtime.\${AWS::Region}.elb.amazonaws.com:${L}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}})}generateBroadcastConfig(){let N=this.mergedConfig.infrastructure?.realtime;if(!N||N.mode!=="server")return"";let t=N.server||{};return`import type { BroadcastConfig } from 'ts-broadcasting'
260
260
 
261
261
  export default {
262
262
  verbose: ${this.environment!=="production"},
@@ -305,164 +305,164 @@ ${t.loadManagement?.enabled?`
305
305
  },
306
306
  `:""}
307
307
  } satisfies BroadcastConfig
308
- `}toYAML(){return this.builder.toYAML()}toJSON(){return this.builder.toJSON()}getBuilder(){return this.builder}}function Yo(N){let t=[],p=[];if(!N.AWSTemplateFormatVersion)t.push({path:"AWSTemplateFormatVersion",message:"AWSTemplateFormatVersion is required",severity:"error"});else if(N.AWSTemplateFormatVersion!=="2010-09-09")p.push({path:"AWSTemplateFormatVersion",message:'AWSTemplateFormatVersion should be "2010-09-09"',severity:"warning"});if(!N.Resources||Object.keys(N.Resources).length===0)t.push({path:"Resources",message:"At least one resource is required",severity:"error"});else for(let[A,L]of Object.entries(N.Resources))tm(A,L,t,p);if(N.Resources){let A=im(N.Resources);if(A.length>0)t.push({path:"Resources",message:`Circular dependencies detected: ${A.join(" -> ")}`,severity:"error"})}if(N.Parameters)for(let[A,L]of Object.entries(N.Parameters))nm(A,L,t,p);if(N.Outputs)for(let[A,L]of Object.entries(N.Outputs))rm(A,L,t,p);return{valid:t.length===0,errors:t,warnings:p}}function tm(N,t,p,A){if(!t.Type)p.push({path:`Resources.${N}.Type`,message:"Resource Type is required",severity:"error"});if(!/^[a-zA-Z0-9]+$/.test(N))A.push({path:`Resources.${N}`,message:"Logical ID should only contain alphanumeric characters",severity:"warning"});if(t.Type&&!t.Type.startsWith("AWS::")&&!t.Type.startsWith("Custom::"))p.push({path:`Resources.${N}.Type`,message:'Resource Type must start with "AWS::" or "Custom::"',severity:"error"});if(t.Properties){for(let[L,j]of Object.entries(t.Properties))if(j===void 0||j===null)A.push({path:`Resources.${N}.Properties.${L}`,message:"Property has undefined or null value",severity:"warning"})}if(t.DependsOn){if(typeof t.DependsOn==="string"){if(t.DependsOn===N)p.push({path:`Resources.${N}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}else if(Array.isArray(t.DependsOn)){if(t.DependsOn.includes(N))p.push({path:`Resources.${N}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}}}function nm(N,t,p,A){if(!t.Type)p.push({path:`Parameters.${N}.Type`,message:"Parameter Type is required",severity:"error"});let L=["String","Number","List<Number>","CommaDelimitedList","AWS::SSM::Parameter::Value<String>"];if(t.Type&&!L.includes(t.Type)&&!t.Type.startsWith("AWS::"))A.push({path:`Parameters.${N}.Type`,message:`Uncommon parameter type: ${t.Type}`,severity:"warning"});if(t.NoEcho&&t.Default)A.push({path:`Parameters.${N}`,message:"NoEcho parameters should not have default values",severity:"warning"})}function rm(N,t,p,A){if(!t.Value)p.push({path:`Outputs.${N}.Value`,message:"Output Value is required",severity:"error"})}function im(N){let t={};for(let[G,Z]of Object.entries(N)){if(t[G]=[],Z.DependsOn){if(typeof Z.DependsOn==="string")t[G].push(Z.DependsOn);else if(Array.isArray(Z.DependsOn))t[G].push(...Z.DependsOn)}let ee=Ca(Z);t[G].push(...ee)}let p=new Set,A=new Set,L=[];function j(G,Z){p.add(G),A.add(G),Z.push(G);let ee=t[G]||[];for(let re of ee)if(!p.has(re)){if(j(re,Z))return!0}else if(A.has(re)){let oe=Z.indexOf(re);return L.push(...Z.slice(oe),re),!0}return Z.pop(),A.delete(G),!1}for(let G of Object.keys(t))if(!p.has(G)){if(j(G,[]))return L}return[]}function Ca(N,t=[]){if(typeof N!=="object"||N===null)return t;if(Array.isArray(N)){for(let p of N)Ca(p,t);return t}if(N.Ref&&typeof N.Ref==="string"&&!N.Ref.startsWith("AWS::"))t.push(N.Ref);if(N["Fn::GetAtt"]){let p=N["Fn::GetAtt"];if(Array.isArray(p)&&p.length>0)t.push(p[0])}for(let p of Object.values(N))Ca(p,t);return t}function Zo(N){let t=[],p=[],A=new TextEncoder().encode(N).length,L=51200,j=460800;if(A>51200)if(A>460800)t.push({path:"template",message:`Template size (${A} bytes) exceeds maximum allowed size of 460800 bytes`,severity:"error"});else p.push({path:"template",message:`Template size (${A} bytes) exceeds direct upload limit (51200 bytes). You must upload to S3 first.`,severity:"warning"});return{valid:t.length===0,errors:t,warnings:p}}function Qo(N){let t=[],p=[],A=N.Resources?Object.keys(N.Resources).length:0,L=N.Parameters?Object.keys(N.Parameters).length:0,j=N.Outputs?Object.keys(N.Outputs).length:0;if(A>500)t.push({path:"Resources",message:`Template has ${A} resources, exceeding the limit of 500`,severity:"error"});else if(A>200)p.push({path:"Resources",message:`Template has ${A} resources. Consider using nested stacks for better organization.`,severity:"warning"});if(L>200)t.push({path:"Parameters",message:`Template has ${L} parameters, exceeding the limit of 200`,severity:"error"});if(j>200)t.push({path:"Outputs",message:`Template has ${j} outputs, exceeding the limit of 200`,severity:"error"});return{valid:t.length===0,errors:t,warnings:p}}function Ar(N,t={}){let p={ok:!1,code:"unsupported_capability",exitCode:2,command:N,provider:t.provider,target:t.target,retryable:!1,nextAction:t.nextAction??"Run `cloud capabilities` and choose a supported driver operation.",message:t.message??`${N} is not supported by the selected provider driver.`};if(r(`${p.message} ${p.nextAction}`),t.setProcessExitCode!==!1)process.exitCode=p.exitCode;return p}function Aa(N){N.command("generate","Generate CloudFormation templates").alias("gen").option("--output <path>","Output directory for templates",{default:"cloudformation"}).option("--format <format>","Output format: json or yaml",{default:"json"}).option("--module <module>","Generate specific module only").action(async(t)=>{i("Generating CloudFormation Templates");let p=new o("Loading configuration...");p.start();try{let A=await Me();p.succeed("Configuration loaded");let L=t?.output||"cloudformation",j=t?.format||"json",G=t?.env||"production";if(!om(L))await sm(L,{recursive:!0});m("Generating infrastructure...");let Z=new Ao({config:A,environment:G,modules:t?.module?[t.module]:void 0}),ee=new o("Generating CloudFormation template...");ee.start(),Z.generate();let re=j==="yaml"?Z.toYAML():Z.toJSON();ee.succeed("Template generated"),m("Validating template...");let oe=JSON.parse(Z.toJSON()),be=Yo(oe),we=Zo(re),Ce=Qo(oe),Ee=[...be.errors,...we.errors,...Ce.errors];if(Ee.length>0){r("Template validation failed:");for(let Ge of Ee)r(` - ${Ge.path}: ${Ge.message}`);if(A.project?.slug===dn.project?.slug)a("No project config was loaded — this is the built-in default ("+`project.slug "${dn.project?.slug}"). Check that cloud.config.ts is in ${process.cwd()} and is not shadowed by another config file.`);process.exitCode=1;return}n("Template validated successfully");let Se=[...be.warnings,...we.warnings,...Ce.warnings];if(Se.length>0)for(let Ge of Se)a(` - ${Ge.path}: ${Ge.message}`);let De=cm(L,`${G}.${j}`);await am(De,re),n(`Generated ${De}`);let _e=Z.getBuilder(),Ae=Object.keys(_e.getResources()).length;e(`
309
- Generated ${Ae} resources:`);let Te=_e.getResources(),Ne={};for(let Ge of Object.values(Te)){let Ye=Ge.Type;Ne[Ye]=(Ne[Ye]||0)+1}let Xe=Object.entries(Ne).sort((Ge,Ye)=>Ye[1]-Ge[1]);for(let[Ge,Ye]of Xe)e(` - ${Ge}: ${Ye}`);e(`
308
+ `}toYAML(){return this.builder.toYAML()}toJSON(){return this.builder.toJSON()}getBuilder(){return this.builder}}function es(N){let t=[],m=[];if(!N.AWSTemplateFormatVersion)t.push({path:"AWSTemplateFormatVersion",message:"AWSTemplateFormatVersion is required",severity:"error"});else if(N.AWSTemplateFormatVersion!=="2010-09-09")m.push({path:"AWSTemplateFormatVersion",message:'AWSTemplateFormatVersion should be "2010-09-09"',severity:"warning"});if(!N.Resources||Object.keys(N.Resources).length===0)t.push({path:"Resources",message:"At least one resource is required",severity:"error"});else for(let[A,L]of Object.entries(N.Resources))pp(A,L,t,m);if(N.Resources){let A=gp(N.Resources);if(A.length>0)t.push({path:"Resources",message:`Circular dependencies detected: ${A.join(" -> ")}`,severity:"error"})}if(N.Parameters)for(let[A,L]of Object.entries(N.Parameters))mp(A,L,t,m);if(N.Outputs)for(let[A,L]of Object.entries(N.Outputs))fp(A,L,t,m);return{valid:t.length===0,errors:t,warnings:m}}function pp(N,t,m,A){if(!t.Type)m.push({path:`Resources.${N}.Type`,message:"Resource Type is required",severity:"error"});if(!/^[a-zA-Z0-9]+$/.test(N))A.push({path:`Resources.${N}`,message:"Logical ID should only contain alphanumeric characters",severity:"warning"});if(t.Type&&!t.Type.startsWith("AWS::")&&!t.Type.startsWith("Custom::"))m.push({path:`Resources.${N}.Type`,message:'Resource Type must start with "AWS::" or "Custom::"',severity:"error"});if(t.Properties){for(let[L,j]of Object.entries(t.Properties))if(j===void 0||j===null)A.push({path:`Resources.${N}.Properties.${L}`,message:"Property has undefined or null value",severity:"warning"})}if(t.DependsOn){if(typeof t.DependsOn==="string"){if(t.DependsOn===N)m.push({path:`Resources.${N}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}else if(Array.isArray(t.DependsOn)){if(t.DependsOn.includes(N))m.push({path:`Resources.${N}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}}}function mp(N,t,m,A){if(!t.Type)m.push({path:`Parameters.${N}.Type`,message:"Parameter Type is required",severity:"error"});let L=["String","Number","List<Number>","CommaDelimitedList","AWS::SSM::Parameter::Value<String>"];if(t.Type&&!L.includes(t.Type)&&!t.Type.startsWith("AWS::"))A.push({path:`Parameters.${N}.Type`,message:`Uncommon parameter type: ${t.Type}`,severity:"warning"});if(t.NoEcho&&t.Default)A.push({path:`Parameters.${N}`,message:"NoEcho parameters should not have default values",severity:"warning"})}function fp(N,t,m,A){if(!t.Value)m.push({path:`Outputs.${N}.Value`,message:"Output Value is required",severity:"error"})}function gp(N){let t={};for(let[G,Z]of Object.entries(N)){if(t[G]=[],Z.DependsOn){if(typeof Z.DependsOn==="string")t[G].push(Z.DependsOn);else if(Array.isArray(Z.DependsOn))t[G].push(...Z.DependsOn)}let ee=Oa(Z);t[G].push(...ee)}let m=new Set,A=new Set,L=[];function j(G,Z){m.add(G),A.add(G),Z.push(G);let ee=t[G]||[];for(let ne of ee)if(!m.has(ne)){if(j(ne,Z))return!0}else if(A.has(ne)){let oe=Z.indexOf(ne);return L.push(...Z.slice(oe),ne),!0}return Z.pop(),A.delete(G),!1}for(let G of Object.keys(t))if(!m.has(G)){if(j(G,[]))return L}return[]}function Oa(N,t=[]){if(typeof N!=="object"||N===null)return t;if(Array.isArray(N)){for(let m of N)Oa(m,t);return t}if(N.Ref&&typeof N.Ref==="string"&&!N.Ref.startsWith("AWS::"))t.push(N.Ref);if(N["Fn::GetAtt"]){let m=N["Fn::GetAtt"];if(Array.isArray(m)&&m.length>0)t.push(m[0])}for(let m of Object.values(N))Oa(m,t);return t}function ts(N){let t=[],m=[],A=new TextEncoder().encode(N).length,L=51200,j=460800;if(A>51200)if(A>460800)t.push({path:"template",message:`Template size (${A} bytes) exceeds maximum allowed size of 460800 bytes`,severity:"error"});else m.push({path:"template",message:`Template size (${A} bytes) exceeds direct upload limit (51200 bytes). You must upload to S3 first.`,severity:"warning"});return{valid:t.length===0,errors:t,warnings:m}}function ns(N){let t=[],m=[],A=N.Resources?Object.keys(N.Resources).length:0,L=N.Parameters?Object.keys(N.Parameters).length:0,j=N.Outputs?Object.keys(N.Outputs).length:0;if(A>500)t.push({path:"Resources",message:`Template has ${A} resources, exceeding the limit of 500`,severity:"error"});else if(A>200)m.push({path:"Resources",message:`Template has ${A} resources. Consider using nested stacks for better organization.`,severity:"warning"});if(L>200)t.push({path:"Parameters",message:`Template has ${L} parameters, exceeding the limit of 200`,severity:"error"});if(j>200)t.push({path:"Outputs",message:`Template has ${j} outputs, exceeding the limit of 200`,severity:"error"});return{valid:t.length===0,errors:t,warnings:m}}function Vr(N,t={}){let m={ok:!1,code:"unsupported_capability",exitCode:2,command:N,provider:t.provider,target:t.target,retryable:!1,nextAction:t.nextAction??"Run `cloud capabilities` and choose a supported driver operation.",message:t.message??`${N} is not supported by the selected provider driver.`};if(r(`${m.message} ${m.nextAction}`),t.setProcessExitCode!==!1)process.exitCode=m.exitCode;return m}function La(N){N.command("generate","Generate CloudFormation templates").alias("gen").option("--output <path>","Output directory for templates",{default:"cloudformation"}).option("--format <format>","Output format: json or yaml",{default:"json"}).option("--module <module>","Generate specific module only").action(async(t)=>{i("Generating CloudFormation Templates");let m=new o("Loading configuration...");m.start();try{let A=await Ne();m.succeed("Configuration loaded");let L=t?.output||"cloudformation",j=t?.format||"json",G=t?.env||"production";if(!hp(L))await yp(L,{recursive:!0});p("Generating infrastructure...");let Z=new Io({config:A,environment:G,modules:t?.module?[t.module]:void 0}),ee=new o("Generating CloudFormation template...");ee.start(),Z.generate();let ne=j==="yaml"?Z.toYAML():Z.toJSON();ee.succeed("Template generated"),p("Validating template...");let oe=JSON.parse(Z.toJSON()),be=es(oe),we=ts(ne),Ce=ns(oe),De=[...be.errors,...we.errors,...Ce.errors];if(De.length>0){r("Template validation failed:");for(let Ke of De)r(` - ${Ke.path}: ${Ke.message}`);if(A.project?.slug===un.project?.slug)a("No project config was loaded — this is the built-in default ("+`project.slug "${un.project?.slug}"). Check that cloud.config.ts is in ${process.cwd()} and is not shadowed by another config file.`);process.exitCode=1;return}n("Template validated successfully");let Se=[...be.warnings,...we.warnings,...Ce.warnings];if(Se.length>0)for(let Ke of Se)a(` - ${Ke.path}: ${Ke.message}`);let xe=bp(L,`${G}.${j}`);await vp(xe,ne),n(`Generated ${xe}`);let Me=Z.getBuilder(),Ae=Object.keys(Me.getResources()).length;e(`
309
+ Generated ${Ae} resources:`);let Ee=Me.getResources(),Te={};for(let Ke of Object.values(Ee)){let Ye=Ke.Type;Te[Ye]=(Te[Ye]||0)+1}let Qe=Object.entries(Te).sort((Ke,Ye)=>Ye[1]-Ke[1]);for(let[Ke,Ye]of Qe)e(` - ${Ke}: ${Ye}`);e(`
310
310
  Next steps:
311
311
  1. Review the generated templates in ${L}/
312
- 2. Run 'cloud deploy' to deploy your infrastructure`)}catch(A){p.fail("Failed to generate templates"),r(A instanceof Error?A.message:"Unknown error")}}),N.command("generate:preview","Preview what will be generated").action(async()=>{Ar("generate:preview",{message:"The legacy preview renderer has no stable output contract.",nextAction:"Run `cloud generate` into a temporary output directory and inspect the generated templates."})}),N.command("diff","Show diff between local config and deployed stack").alias("generate:diff").option("--stack <name>","Stack name to compare against").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{i("Infrastructure Diff");try{let p=await Me(),A=t?.env||"production",{resolveProjectStackName:L}=await import("./chunk-v93xyx1p.js"),j=t?.stack||L(p,A),G=p.project.region||"us-east-1";e(`Stack: ${j}`),e(`Region: ${G}`),e(`Environment: ${A}`),m("Generating template from configuration...");let Z=new Ao({config:p,environment:A});Z.generate();let ee=Z.toJSON(),re=JSON.parse(ee);m("Fetching deployed template...");let oe=new v(G),be=null;try{let Te=await oe.getTemplate(j);if(Te.TemplateBody)be=JSON.parse(Te.TemplateBody)}catch(Te){if(Te.message?.includes("does not exist")){a(`Stack "${j}" does not exist yet`),e(`
313
- This will be a new deployment with the following resources:`);let Ne=Object.keys(re.Resources||{}).length;e(`
314
- Resources to create: ${Ne}`);let Xe={};for(let Ge of Object.values(re.Resources||{})){let Ye=Ge.Type;Xe[Ye]=(Xe[Ye]||0)+1}for(let[Ge,Ye]of Object.entries(Xe).sort((et,Pt)=>Pt[1]-et[1]))e(` + ${Ge}: ${Ye}`);e("\nRun `cloud deploy` to create this stack");return}throw Te}m("Comparing templates...");let we=be.Resources||{},Ce=re.Resources||{},Ee=new Set(Object.keys(we)),Se=new Set(Object.keys(Ce)),De=[];for(let Te of Se)if(!Ee.has(Te))De.push(Te);let _e=[];for(let Te of Ee)if(!Se.has(Te))_e.push(Te);let Ae=[];for(let Te of Se)if(Ee.has(Te)){let Ne=JSON.stringify(we[Te]),Xe=JSON.stringify(Ce[Te]);if(Ne!==Xe)Ae.push(Te)}if(De.length===0&&_e.length===0&&Ae.length===0){n(`
312
+ 2. Run 'cloud deploy' to deploy your infrastructure`)}catch(A){m.fail("Failed to generate templates"),r(A instanceof Error?A.message:"Unknown error")}}),N.command("generate:preview","Preview what will be generated").action(async()=>{Vr("generate:preview",{message:"The legacy preview renderer has no stable output contract.",nextAction:"Run `cloud generate` into a temporary output directory and inspect the generated templates."})}),N.command("diff","Show diff between local config and deployed stack").alias("generate:diff").option("--stack <name>","Stack name to compare against").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{i("Infrastructure Diff");try{let m=await Ne(),A=t?.env||"production",{resolveProjectStackName:L}=await import("./chunk-6se7ygxa.js"),j=t?.stack||L(m,A),G=m.project.region||"us-east-1";e(`Stack: ${j}`),e(`Region: ${G}`),e(`Environment: ${A}`),p("Generating template from configuration...");let Z=new Io({config:m,environment:A});Z.generate();let ee=Z.toJSON(),ne=JSON.parse(ee);p("Fetching deployed template...");let oe=new v(G),be=null;try{let Ee=await oe.getTemplate(j);if(Ee.TemplateBody)be=JSON.parse(Ee.TemplateBody)}catch(Ee){if(Ee.message?.includes("does not exist")){a(`Stack "${j}" does not exist yet`),e(`
313
+ This will be a new deployment with the following resources:`);let Te=Object.keys(ne.Resources||{}).length;e(`
314
+ Resources to create: ${Te}`);let Qe={};for(let Ke of Object.values(ne.Resources||{})){let Ye=Ke.Type;Qe[Ye]=(Qe[Ye]||0)+1}for(let[Ke,Ye]of Object.entries(Qe).sort((et,Pt)=>Pt[1]-et[1]))e(` + ${Ke}: ${Ye}`);e("\nRun `cloud deploy` to create this stack");return}throw Ee}p("Comparing templates...");let we=be.Resources||{},Ce=ne.Resources||{},De=new Set(Object.keys(we)),Se=new Set(Object.keys(Ce)),xe=[];for(let Ee of Se)if(!De.has(Ee))xe.push(Ee);let Me=[];for(let Ee of De)if(!Se.has(Ee))Me.push(Ee);let Ae=[];for(let Ee of Se)if(De.has(Ee)){let Te=JSON.stringify(we[Ee]),Qe=JSON.stringify(Ce[Ee]);if(Te!==Qe)Ae.push(Ee)}if(xe.length===0&&Me.length===0&&Ae.length===0){n(`
315
315
  No changes detected - infrastructure is up to date`);return}if(e(`
316
316
  Changes detected:
317
- `),De.length>0){n(`Resources to add (${De.length}):`);for(let Te of De){let Ne=Ce[Te].Type;e(` + ${Te} (${Ne})`)}console.log()}if(_e.length>0){r(`Resources to remove (${_e.length}):`);for(let Te of _e){let Ne=we[Te].Type;e(` - ${Te} (${Ne})`)}console.log()}if(Ae.length>0){a(`Resources to modify (${Ae.length}):`);for(let Te of Ae){let Ne=Ce[Te].Type;e(` ~ ${Te} (${Ne})`)}console.log()}e("Summary:"),e(` - Add: ${De.length}`),e(` - Remove: ${_e.length}`),e(` - Modify: ${Ae.length}`),e("\nRun `cloud deploy` to apply these changes")}catch(p){r(`Diff failed: ${p.message}`)}})}function Gi(N,t=0){let p=Number(N);return Number.isFinite(p)?p:t}function ka(N){return Gi(N)*1024}function lm(N){let t=N?.date?.trim(),p=N?.time?.trim();if(!t||!p)return null;let A=new Date(`${t}T${p}Z`);return Number.isFinite(A.getTime())?A.toISOString():null}function dm(N,t,p){let A;try{A=JSON.parse(N)}catch{return[]}let L=(A?.sysstat?.hosts??[]).flatMap((G)=>G.statistics??[]),j=[];for(let G of L){let Z=lm(G.timestamp);if(!Z)continue;let ee=new Date(Z).getTime();if(ee<t.getTime()||ee>p.getTime())continue;let re=G["cpu-load"]?.find((be)=>be.cpu==="all")??G["cpu-load"]?.[0],oe=Gi(re?.idle,100);j.push({timestamp:Z,cpuUsedPercent:Math.max(0,Math.min(100,100-oe)),cpuUserPercent:Gi(re?.user),cpuSystemPercent:Gi(re?.system),cpuIowaitPercent:Gi(re?.iowait),memoryUsedPercent:Gi(G.memory?.["memused-percent"]),memoryUsedBytes:ka(G.memory?.memused),memoryAvailableBytes:ka(G.memory?.avail),swapUsedPercent:Gi(G.memory?.["swpused-percent"]),swapUsedBytes:ka(G.memory?.swpused)})}return j.sort((G,Z)=>G.timestamp.localeCompare(Z.timestamp))}function um(N){return Object.fromEntries(Object.entries(N.time_series??{}).map(([t,p])=>[t,(p.values??[]).map(([A,L])=>({timestamp:new Date(Number(A)*1000).toISOString(),value:Gi(L,Number.NaN)})).filter((A)=>Number.isFinite(A.value))]))}function Il(N){return N.length?N.reduce((t,p)=>t+p,0)/N.length:null}function Ra(N){return N.length?Math.max(...N):null}function mm(N){return N.length?Math.min(...N):null}async function Pl(N){let[t,p]=await Promise.all([N.client.getServerMetrics(N.serverId,{types:["cpu","disk","network"],start:N.from,end:N.to,step:N.step}),Re(N.remote.host,"LC_ALL=C sadf -j -- -u -r -S",N.remote).catch(()=>"")]),A=dm(p,N.from,N.to),L=um(t),G=(L.cpu??[]).map((be)=>Math.max(0,Math.min(100,be.value/Math.max(1,N.cores)))),Z=A.map((be)=>be.cpuUsedPercent),ee=G.length?G:Z,re=A.map((be)=>be.memoryUsedPercent),oe=A.map((be)=>be.swapUsedPercent);return{from:N.from.toISOString(),to:N.to.toISOString(),cores:N.cores,host:A,provider:L,summary:{cpuAveragePercent:Il(ee),cpuPeakPercent:Ra(ee),memoryAveragePercent:Il(re),memoryPeakPercent:Ra(re),memoryMinimumAvailableBytes:mm(A.map((be)=>be.memoryAvailableBytes)),swapPeakPercent:Ra(oe)}}}function pm(N){if(N.length===0)throw Error("A server name cannot be empty.");if(N.length>253)throw Error(`'${N}' is longer than the 253 characters a hostname allows.`);for(let t of N.split(".")){if(t.length===0)throw Error(`'${N}' 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 xl(N,t,p){if(pm(t),N===t)throw Error(`'${N}' is already named that.`);if((await p.takenNames()).some((j)=>j===t&&j!==N))throw Error(`'${t}' is already taken. Server names have to be unique within a provider project.`);let L=[];if(p.providerName&&p.renameProvider){let{providerName:j,renameProvider:G}=p;L.push({id:"provider",title:"Rename the server at the provider",change:{from:N,to:t},satisfied:async()=>await j()===t,apply:()=>G(t)})}if(p.stateName&&p.writeStateName){let{stateName:j,writeStateName:G}=p;L.push({id:"state-pin",title:"Update the recorded name in the local driver state",change:{from:N,to:t},satisfied:async()=>{let Z=await j();return Z===void 0||Z===t},apply:()=>G(t)})}if(p.remoteHostname&&p.setRemoteHostname){let{remoteHostname:j,setRemoteHostname:G}=p;L.push({id:"hostname",title:"Set the hostname on the box",change:{from:N,to:t},satisfied:async()=>await j()===t,apply:()=>G(t)})}return L.push({id:"inventory",title:"Rename the fleet inventory record",change:{from:N,to:t},satisfied:async()=>p.inventoryName()===t,apply:async()=>{await p.renameInventory(t)}}),{operation:"server:rename",target:N,steps:L}}function Dl(N){let t=`'${N.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${N}/" /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 fm(){let N=await Me(),t=y(process.cwd(),N),p=new Ie(t.store),A=new pn(p,[new tt("aws"),new tt("hetzner"),new tt("ssh")]);return{controlPlane:t,store:p,service:A}}async function yi(N){let t=await fm();try{return await N(t)}finally{t.controlPlane.store.close()}}var Hi=(N,t)=>{let p=N.store.list(N.controlPlane.project.id).find((A)=>A.id===t||A.name===t);if(!p)throw Error(`Server ${t} was not found.`);return p},ur=(N)=>{r(N instanceof Error?N.message:String(N)),process.exitCode=1};function Ia(N,t){let p=Number.parseInt(N??"",10);return Number.isFinite(p)&&p>=0?p:t}function gm(N="3h"){let t=N.match(/^(\d+)(m|h)$/);if(!t)throw Error("--range must use minutes or hours, for example 30m or 3h.");let p=Number(t[1])*(t[2]==="h"?3600000:60000);if(p<300000||p>86400000)throw Error("--range must be between 5 minutes and 24 hours.");return p}function Xo(N,t="%"){return N==null?"unavailable":`${N.toFixed(1)}${t}`}async function hm(N,t){let p=await Me();if(p.cloud?.provider!=="hetzner")throw Error("server:monitoring currently requires the Hetzner provider.");let A=t.env??process.env.CLOUD_ENV??"production",L=E(p,A),j=await de(L);if(!j?.serverId)throw Error(`No pinned Hetzner server was found for ${L}.`);if(![j.serverName,String(j.serverId),j.stackName].includes(N))throw Error(`Refusing to read ${N}: project state is pinned to ${j.serverName}.`);let G=tr(p),Z=new Ve({apiToken:rr(G.apiToken,p)}),ee=await Z.getServer(j.serverId),re=j.publicIp??ee.public_net.ipv4?.ip;if(!re)throw Error(`Server ${ee.name} has no public IPv4 address.`);let oe=new Date,be=new Date(oe.getTime()-gm(t.range)),we=Math.min(3600,Math.max(60,Ia(t.step,60))),Ce=await Pl({client:Z,serverId:ee.id,cores:Math.max(1,Number(ee.server_type.cores)||1),from:be,to:oe,step:we,remote:{host:re,user:j.sshUser??G.sshUser,identityFile:G.sshPrivateKeyPath,connectTimeoutSec:15}});if(t.json){console.log(JSON.stringify({schemaVersion:1,server:ee.name,result:Ce},null,2));return}c(["Server","Window","CPU average / peak","RAM average / peak","Minimum available","Swap peak"],[[ee.name,`${Ce.from} to ${Ce.to}`,`${Xo(Ce.summary.cpuAveragePercent)} / ${Xo(Ce.summary.cpuPeakPercent)}`,`${Xo(Ce.summary.memoryAveragePercent)} / ${Xo(Ce.summary.memoryPeakPercent)}`,Ce.summary.memoryMinimumAvailableBytes==null?"unavailable":`${(Ce.summary.memoryMinimumAvailableBytes/1073741824).toFixed(2)} GB`,Xo(Ce.summary.swapPeakPercent)]])}function El(N){let t=N.targetType.prices?.find((p)=>p.location===N.location);return{serverId:N.serverId,serverName:N.serverName,location:N.location,from:N.currentType.name,to:N.targetType.name,capacityAvailable:N.capacityAvailable,alreadyComplete:N.alreadyComplete,upgradeDisk:N.upgradeDisk,target:{cores:N.targetType.cores,memoryGb:N.targetType.memory,diskGb:N.targetType.disk,architecture:N.targetType.architecture,cpuType:N.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 ym(N){let t=El(N);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 vm(N,t,p){let A=await Me();if(A.cloud?.provider!=="hetzner")throw Error("server:resize currently requires the Hetzner provider.");let L=p.env??process.env.CLOUD_ENV??"production",j=E(A,L),G=await de(j),Z=tr(A),ee=new Ve({apiToken:rr(Z.apiToken,A)}),oe=G&&(N===G.serverName||N===String(G.serverId)||N===G.stackName)?await ee.getServer(G.serverId):(await ee.listServers()).find((Ge)=>Ge.name===N||String(Ge.id)===N);if(!oe)throw Error(`Hetzner server ${N} was not found.`);if(G?.serverId&&G.serverId!==oe.id)throw Error(`Refusing to resize ${N}: project state is pinned to server ${G.serverId}.`);let be=at(t),we=p.upgradeDisk!==!1,Ce=await Vn(ee,oe.id,be,we);if(p.json)console.log(JSON.stringify({schemaVersion:1,plan:El(Ce)},null,2));else ym(Ce);if(!p.apply){if(!p.json)e(`Preview only. Re-run with --apply --confirm ${oe.name} to execute.`);return}if(p.confirm!==oe.name)throw Error(`Resize requires --confirm ${oe.name}.`);let Ee=G?.publicIp??oe.public_net.ipv4?.ip;if(!Ee)throw Error(`Server ${oe.name} has no public IPv4 address for verification.`);let Se={host:Ee,user:G?.sshUser??Z.sshUser,identityFile:Z.sshPrivateKeyPath,connectTimeoutSec:15,sshTimeoutMs:600000,routeTimeoutMs:20000},De=await zo(j),_e=new Date().toISOString(),Ae={schemaVersion:1,operationId:De?.targetType===be&&De.serverId===oe.id?De.operationId:crypto.randomUUID(),stackName:j,serverId:oe.id,serverName:oe.name,sourceType:Ce.currentType.name,targetType:be,upgradeDisk:we,phase:"planning",status:"running",attempts:De?.targetType===be?De.attempts:0,startedAt:De?.targetType===be?De.startedAt:_e,updatedAt:_e,manifest:De?.targetType===be?De.manifest:void 0};await lt(Ae);let Te=Ia(p.pollSeconds,900)*1000,Ne=Ia(p.maxWaitMinutes,0)*60000,Xe=Date.now();for(;;){let Ge=await Vn(ee,oe.id,be,we);if(!Ge.capacityAvailable&&!Ge.alreadyComplete){if(Ae={...Ae,phase:"waiting-capacity",status:"waiting-capacity",updatedAt:new Date().toISOString()},await lt(Ae),!p.wait){if(!p.json)e(`${be} has no capacity in ${Ge.location}. The server remains running and unchanged. Use --wait to poll safely.`);return}if(Ne>0&&Date.now()-Xe>=Ne)throw Error(`Timed out waiting for ${be} capacity in ${Ge.location}.`);if(!p.json)e(`Waiting ${Math.round(Te/1000)} seconds for ${be} capacity...`);await Bun.sleep(Te);continue}let Ye=await qo(j);try{let et=await(async()=>{try{return Ae={...Ae,status:"running",attempts:Ae.attempts+1,updatedAt:new Date().toISOString()},await lt(Ae),await Mo({client:ee,serverId:oe.id,targetType:be,upgradeDisk:we,resumeFromOff:Ae.phase==="shutting-down"||Ae.phase==="resizing"||Ae.phase==="powering-on"||Ae.phase==="verifying",manifest:Ae.manifest,hooks:{preflight:async()=>{let Pt=await Uo(Se);return Ae={...Ae,manifest:Pt,updatedAt:new Date().toISOString()},await lt(Ae),Pt},afterBoot:async(Pt,wt,Ot,Ut)=>Bo(Se,wt,Ot,Ut),onPhase:async(Pt,wt)=>{if(Ae={...Ae,phase:Pt,status:Pt==="waiting-capacity"?"waiting-capacity":Pt==="complete"?"completed":"running",updatedAt:new Date().toISOString()},await lt(Ae),!p.json)e(`${Pt}${wt?` ${JSON.stringify(wt)}`:""}`)}}})}finally{await Ye()}})();if(Ae={...Ae,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 lt(Ae),p.json)console.log(JSON.stringify({schemaVersion:1,result:et},null,2));else if(et.status==="completed"||et.status==="already-complete")n(`${oe.name} is on ${be} and passed full post-boot verification.`);else if(et.status==="recovered"&&et.retryable&&p.wait){e(`Capacity race recovered safely. Waiting ${Math.round(Te/1000)} seconds before retry.`),await Bun.sleep(Te);continue}else if(et.status==="recovered")throw Error(`Resize did not complete and the original server was recovered: ${et.error}`);return}catch(et){throw Ae={...Ae,status:"failed",lastError:et instanceof Error?et.message:String(et),updatedAt:new Date().toISOString()},await lt(Ae),et}}}async function bm(N,t){let p=await Me();if(p.cloud?.provider!=="hetzner")throw Error("server:optimize currently requires the Hetzner provider.");let A=t.env??process.env.CLOUD_ENV??"production",L=E(p,A),j=await de(L);if(!j?.serverId)throw Error(`No pinned Hetzner server was found for ${L}.`);if(![j.serverName,String(j.serverId),j.stackName].includes(N))throw Error(`Refusing to optimize ${N}: project state is pinned to ${j.serverName}.`);let G=tr(p),Z=new Ve({apiToken:rr(G.apiToken,p)}),ee=await Z.getServer(j.serverId),re=j.publicIp??ee.public_net.ipv4?.ip;if(!re)throw Error(`Server ${ee.name} has no public IPv4 address.`);let oe=_i(p);if(t.json)console.log(JSON.stringify({schemaVersion:1,server:ee.name,plan:oe},null,2));else c(["Server","Firewall ports","Monitoring","Auto updates","Swap","SSH passwords"],[[ee.name,oe.firewallPorts.join(","),oe.monitoring?"on":"off",oe.autoUpdates?"on":"off",`${oe.swapGb} GB`,"disabled"]]);if(!t.apply){if(!t.json)e(`Preview only. Re-run with --apply --confirm ${ee.name} to execute.`);return}if(t.confirm!==ee.name)throw Error(`Optimization requires --confirm ${ee.name}.`);let be={host:re,user:j.sshUser??G.sshUser,identityFile:G.sshPrivateKeyPath,connectTimeoutSec:15},we=await Fr(be),Ce=we.routeProbes.filter((Ot)=>!Ot.ok),Ee=p.infrastructure?.compute,Se=J(Ee)?new Set(["nginx.service","bun-gateway.service","ts-cloud-nginx.service"]):new Set,De=we.failedUnits.filter((Ot)=>!Se.has(Ot));if(De.length>0)throw Error(`Preflight found failed services: ${De.join(", ")}`);if(Ce.length>0)throw Error(`Preflight found unhealthy routes: ${Ce.map((Ot)=>Ot.domain).join(", ")}`);let _e=J(Ee)?[]:Object.values(p.sites??{}).flatMap((Ot)=>typeof Ot?.port==="number"&&![80,443].includes(Ot.port)?[Ot.port]:[]),Ae=Ur({allowSsh:Ee?.allowSsh!==!1,sitePorts:_e,allowedPorts:Ee?.firewall?.allowedPorts}),Te=(j.firewallId?(await Z.listFirewalls()).find((Ot)=>Ot.id===j.firewallId):void 0)??(await Z.listFirewalls()).find((Ot)=>Ot.name===`${p.project.slug}-${A}-app-fw`);if(!Te)throw Error(`Managed firewall for ${ee.name} was not found.`);let Ne=await Z.setFirewallRules(Te.id,Ae);await Promise.all(Ne.map((Ot)=>Z.waitForAction(Ot.id))),await Fo(p,be);let[Xe,Ge]=await Promise.all([Fr(be),jo(be)]),Ye=Vo(oe,Ge),et=ma(we,Xe),Pt=Xe.routeProbes.filter((Ot)=>!Ot.ok);if(et.stoppedServices.length>0)Ye.push(`services stopped: ${et.stoppedServices.join(", ")}`);if(et.changedRouteFragments.length>0)Ye.push(`route fragments changed: ${et.changedRouteFragments.join(", ")}`);if(et.missingRouteIds.length>0)Ye.push(`routes disappeared: ${et.missingRouteIds.join(", ")}`);if(et.changedReleaseLinks.length>0)Ye.push(`release links changed: ${et.changedReleaseLinks.join(", ")}`);if(et.missingData.length>0)Ye.push(`databases or volumes disappeared: ${et.missingData.join(", ")}`);if(Pt.length>0)Ye.push(`unhealthy routes: ${Pt.map((Ot)=>Ot.domain).join(", ")}`);if(Ye.length>0)throw Error(`Host optimization verification failed: ${Ye.join("; ")}`);let wt={serverId:ee.id,serverName:ee.name,services:Xe.runningServices.length,routes:Xe.routeIds.length,healthyDomains:Xe.routeProbes.length,releaseLinks:Xe.releaseLinks.length,databasesAndVolumes:Xe.dataCatalog.length,report:Ge};if(t.json)console.log(JSON.stringify({schemaVersion:1,result:wt},null,2));else n(`${ee.name} passed full host, rpx route, service, release, and data verification.`)}async function wm(N,t,p){let A=await Me(),L=p.env??"production",j=E(A,L);await yi(async(G)=>{let Z=Hi(G,N),ee=G.store.list(G.controlPlane.project.id,!0),re=Z.provider==="hetzner"&&Z.providerId?(()=>{try{let Te=tr(A);return new Ve({apiToken:rr(Te.apiToken,A)})}catch{return null}})():null,oe=Z.providerId?Number(Z.providerId):Number.NaN,be=await de(j),we=be!=null&&be.serverId===oe,Ce=new Ir,Ee=Z.trustState==="pinned",Se={takenNames:async()=>{let Te=ee.map((Ne)=>Ne.name);if(re)Te.push(...(await re.listServers()).map((Ne)=>Ne.name));return Te},inventoryName:()=>G.store.get(Z.id)?.name??Z.name,renameInventory:(Te)=>{G.store.update(Z.id,{name:Te})},...re&&Number.isFinite(oe)?{providerName:async()=>(await re.getServer(oe)).name,renameProvider:async(Te)=>{await re.renameServer(oe,Te)}}:{},...we?{stateName:async()=>(await de(j))?.serverName,writeStateName:async(Te)=>{let Ne=await de(j);if(Ne)await ct(j,{...Ne,serverName:Te})}}:{},...Ee?{remoteHostname:async()=>{let Te=await Ce.exec(Z,"hostname");return Te.code===0?Te.stdout.trim():void 0},setRemoteHostname:async(Te)=>{let Ne=await Ce.exec(Z,Dl(Te));if(Ne.code!==0)throw Error(Ne.stderr.trim()||`hostname exited ${Ne.code}`)}}:{}},De=await xl(Z.name,t,Se),_e=await Ss(De);if(!p.apply){let Te=[re?"":"provider record (no provider id or no API token)",we?"":"local state pin (none points at this server)",Ee?"":"box hostname (host key is not pinned — run server:validate first)"].filter(Boolean);if(p.json){console.log(JSON.stringify({schemaVersion:1,operation:De.operation,target:De.target,rename:{from:Z.name,to:t},notCovered:Te,steps:_e.map((Ne)=>({id:Ne.step.id,title:Ne.step.title,state:Ne.state,change:Ne.step.change,reason:Ne.reason}))},null,2));return}for(let Ne of $s(De,_e))console.log(Ne);for(let Ne of Te)console.log(` not covered: ${Ne}`);console.log(" Re-run with --apply to perform it.");return}let Ae=await Cs(De,_e,{log:(Te)=>e(Te),audit:(Te)=>G.controlPlane.store.appendEvent({projectId:G.controlPlane.project.id,resourceId:Z.resourceId,type:`${Te.operation}.${Te.step}.${Te.state}`,level:Te.state==="failed"?"error":"info",payload:{target:Te.target,renameTo:t,...Te.error?{error:Te.error}:{}}})});if(p.json)console.log(JSON.stringify({schemaVersion:1,outcome:Ae},null,2));if(!Ae.success){let Te=Ae.steps.find((Ne)=>Ne.state==="failed");throw Error(`${De.operation} stopped at '${Te?.title}': ${Te?.error}. Earlier steps stayed applied; re-run the same command to continue from here.`)}if(!p.json)n(`Renamed ${Z.name} to ${t}.`)})}function Pa(N){N.command("capabilities [server]","Show provider and target operation support").option("--json","Print structured JSON").action(async(t,p)=>{try{await yi(async(A)=>{let L=t?[Hi(A,t)]:A.store.list(A.controlPlane.project.id),j=L.flatMap((G)=>Object.entries(G.capabilities).map(([Z,ee])=>({server:G.name,provider:G.provider,action:Z,...ee})));if(p.json)console.log(JSON.stringify({schemaVersion:1,targets:j},null,2));else c(["Server","Provider","Action","Support","Explanation"],j.map((G)=>[G.server,G.provider,G.action,G.supported?"supported":"unsupported",G.reason??"—"]))})}catch(A){ur(A)}}),N.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,p,A)=>{try{await vm(t,p,A)}catch(L){ur(L)}}),N.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,p)=>{try{await bm(t,p)}catch(A){ur(A)}}),N.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,p)=>{try{await hm(t,p)}catch(A){ur(A)}}),N.command("server:list","List fleet servers").option("--json","Print structured JSON").action(async(t)=>{try{await yi(async(p)=>{let A=p.store.list(p.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((L)=>[L.name,L.providerId??"—",`${L.provider} / ${L.region??"external"}`,`${L.status} / ${L.trustState}`,L.roles.join(","),`${L.capacity.cpu??0} / ${L.capacity.memoryBytes??0}`,L.heartbeatAt??"never"]))})}catch(p){ur(p)}}),N.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,p)=>{try{await yi(async(A)=>{let L=p.provider;if(!["aws","hetzner","ssh"].includes(L)||!p.endpoint)throw Error("A supported --provider and --endpoint are required; provisioning and enrollment are separate operations.");let j=A.service.enroll({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,provider:L,providerId:p.providerId,endpoint:p.endpoint,sshUser:p.user??"deploy",credentialRef:p.credentialRef??"secret://fleet/agent",region:p.region,roles:String(p.roles??"application").split(","),labels:Object.fromEntries(String(p.labels??"").split(",").map((G)=>G.split("=")).filter((G)=>G[0]&&G[1]))});n(`Enrolled ${j.name} as ${j.id}; no remote mutation was performed.`)})}catch(A){ur(A)}}),N.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,p)=>{try{await yi(async(A)=>{let L=Hi(A,t);if(L=await A.service.test(L.id),L.trustState==="rotation_pending"){if(!p.acceptHostKey)throw Error(`Host key changed to ${L.pendingHostKey}; review it first.`);L=A.service.reviewHostKey(L.id,p.acceptHostKey)}let j=await A.service.validate(L.id);if(p.json)console.log(JSON.stringify(j.validation,null,2));else c(["Severity","Code","Finding","Remediation"],(j.validation?.findings??[]).map((G)=>[G.severity,G.code,G.message,G.remediation??"—"]));if(!j.validation?.valid)process.exitCode=1})}catch(A){ur(A)}}),N.command("server:bootstrap <name>","Preview or queue bootstrap").option("--apply","Apply reviewed plan").action(async(t,p)=>{try{await yi(async(A)=>{let L=A.service.bootstrap(Hi(A,t).id,!!p.apply);if(L.preview)c(["Step"],L.steps.map((j)=>[j]));else n(`Bootstrap queued: ${L.operation?.id}`)})}catch(A){ur(A)}}),N.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,p,A)=>{try{await wm(t,p,A)}catch(L){ur(L)}}),N.command("server:drain <name>","Drain without terminating").option("--complete","Mark movement complete").action(async(t,p)=>{try{await yi(async(A)=>n(A.service.drain(Hi(A,t).id,!!p.complete).status))}catch(A){ur(A)}}),N.command("server:uncordon <name>","Return a server to scheduling").action(async(t)=>{try{await yi(async(p)=>{p.service.uncordon(Hi(p,t).id),n(`Uncordoned ${t}.`)})}catch(p){ur(p)}}),N.command("server:archive <name>","Archive inventory without termination").option("--confirm <name>","Exact name").action(async(t,p)=>{try{await yi(async(A)=>{A.service.archive(Hi(A,t).id,p.confirm??""),n(`Archived ${t}; provider infrastructure was not terminated.`)})}catch(A){ur(A)}}),N.command("server:ssh <name>","Open strict SSH using the enrolled endpoint").action(async(t)=>{try{await yi(async(p)=>{let A=Hi(p,t);if(A.trustState!=="pinned")throw Error("Validate and pin the server host key first.");let L=Bun.spawn(["ssh","-p",String(A.sshPort),"-o","StrictHostKeyChecking=yes",`${A.sshUser}@${A.endpoint}`],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}),j=await L.exited;if(j)process.exitCode=j})}catch(p){ur(p)}});for(let[t,p]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"]])N.command(t,p).action(async(...A)=>Ar(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 Tl,mkdirSync as Sm,readFileSync as $m,writeFileSync as Cm}from"node:fs";import{join as Nl,resolve as Am}from"node:path";var xa=(N)=>{r(N instanceof Error?N.message:String(N)),process.exitCode=1};async function km(){try{return await Me()}catch{return}}function Rm(N){if(N===void 0)return;let t=Number(N);if(!Number.isInteger(t)||t<1||t>65535)throw Error(`--port must be a port number, not '${N}'.`);return t}function Ol(N,t,p){let A=N?.ssh?.hosts?.find((L)=>L.host===t);return new ar({hosts:t?[A??{host:t}]:N?.ssh?.hosts,user:p.user,port:Rm(p.port),privateKeyPath:p.key,hostKey:N?.ssh?.hostKey,sudo:N?.ssh?.sudo,profile:p.profile??N?.ssh?.profile,publicIp:N?.ssh?.publicIp,lan:N?.ssh?.lan})}var Ll=1073741824;function Da(N){N.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,p)=>{try{let A=await km(),L=Ol(A,t,p),j=t??L.targetHost(),G=await L.ensureHostKey(j),{facts:Z,findings:ee}=await L.preflight(j);if(p.json)console.log(JSON.stringify({schemaVersion:1,host:j,hostKeyFingerprint:G.fingerprint,facts:Z,findings:ee},null,2));else{if(i(`Preflight ${j}`),G.pinnedNow)e(`Pinned host key ${G.fingerprint}`);if(e(`${Z.os} on ${Z.arch}, ${Z.cpuCores} cores, ${(Z.memoryBytes/Ll).toFixed(1)} GiB memory, ${(Z.diskFreeBytes/Ll).toFixed(1)} GiB free`),Z.piModel)e(Z.piModel);if(Z.lanIp)e(`LAN address ${Z.lanIp}`);if(console.log(Br(ee)),sr(ee))r("The host is not ready; fix the errors above and run the preflight again.");else n("The host is ready to adopt.")}if(sr(ee))process.exitCode=1}catch(A){xa(A)}}),N.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 p=await Me(),A=t.env??"production";if(t.dryRun){let G=or(p),[Z]=G.hosts;console.log(ir({config:p,environment:A,profile:G.profile,sudoUser:Z.user!=="root"?Z.user:void 0,lan:G.lan}));return}let L=Ol(p,void 0,{});i(`Adopt ${L.targetHost()}`);let j=await L.provisionComputeInfrastructure({config:p,environment:A});n(`${j.appPublicIp} is bootstrapped; deploy with: cloud deploy --env ${A}`)}catch(p){xa(p)}}),N.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 p=t.os??"raspberry-pi-os";if(!(p in ki))throw Error(`--os must be raspberry-pi-os or ubuntu, not '${p}'.`);let A=await Me(),L=t.env??"production",j=or(A,{hosts:A.ssh?.hosts?.length?void 0:[{host:t.hostname}],profile:p==="raspberry-pi-os"?"raspberry-pi":void 0}),G=t.user??(A.ssh?.hosts?.[0]?.user??"pi"),Z=Mr(t.key??"~/.ssh/id_ed25519.pub");if(!Tl(Z))throw Error(`Public key not found at ${Z}; pass --key <path to a .pub file>.`);let ee=$m(Z,"utf8").trim(),re;if(t.wifiSsid){let Ee=t.wifiPassphraseEnv??"WIFI_PASSWORD",Se=process.env[Ee];if(!Se)throw Error(`--wifi-ssid needs the passphrase in $${Ee} (set it in the environment, not on the command line).`);if(!t.wifiCountry)throw Error("--wifi-country <cc> is required with --wifi-ssid.");re={ssid:t.wifiSsid,passphrase:Se,country:t.wifiCountry}}let oe=ir({config:A,environment:L,profile:j.profile,sudoUser:G!=="root"?G:void 0,lan:j.lan}),be=xi({hostname:t.hostname,user:G,publicKey:ee,timezone:t.timezone,locale:t.locale,wifi:re},oe,{os:p}),we=Am(t.out);Sm(we,{recursive:!0});let Ce=Object.keys(be.files).filter((Ee)=>Tl(Nl(we,Ee)));if(Ce.length>0&&!t.force)throw Error(`${Ce.join(", ")} already exist in ${we}; pass --force to overwrite.`);for(let[Ee,Se]of Object.entries(be.files))Cm(Nl(we,Ee),Se,{mode:384});i(`First boot for ${t.hostname}`),n(`Wrote ${Object.keys(be.files).join(", ")} to ${we}`),console.log(be.instructions)}catch(p){xa(p)}})}async function Im(){let N=await Me(),t=y(process.cwd(),N),p=new mn(t.store),A=new gn(p,new Ie(t.store));return{controlPlane:t,store:p,service:A}}async function es(N){let t=await Im();try{await N(t)}catch(p){r(p instanceof Error?p.message:String(p)),process.exitCode=1}finally{t.controlPlane.store.close()}}var As=(N)=>Object.fromEntries(String(N??"").split(",").map((t)=>t.split("=")).filter((t)=>t[0]&&t[1])),jl=(N)=>Object.fromEntries(Object.entries(As(N)).map(([t,p])=>[t,Number(p)]));function Ea(N){N.command("pool:list","List capacity pools and reservations").option("--json","Print structured JSON").action(async(t)=>es((p)=>{let A=p.store.listPools(p.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((L)=>[L.name,L.purpose,L.backend,`${L.region??"any"} / ${L.architecture??"any"}`,L.status,JSON.stringify(L.capacity),String(L.concurrency)]))})),N.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,p)=>es((A)=>{let{purpose:L,backend:j}=p;if(!["application","build","worker","monitoring","backup"].includes(L)||!["server","ecs","asg"].includes(j))throw Error("Unsupported pool purpose or backend.");let G=A.store.createPool({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,purpose:L,backend:j,region:p.region,architecture:p.arch,labels:As(p.labels),requiredServerLabels:As(p.selector),toleratedTaints:[],capacity:{cpu:0,memoryBytes:0,diskBytes:0,gpu:0,...jl(p.capacity)},maxWorkloads:Number(p.max),costWeight:1,concurrency:Number(p.concurrency),ephemeralWorkspaces:!0,allowProductionSecrets:!1,status:"active"});n(`Created ${G.name} (${G.id}).`)})),N.command("pool:add-server <pool> <server>","Attach an enrolled server to a pool").action(async(t,p)=>es((A)=>{let L=A.store.listPools(A.controlPlane.project.id).find((G)=>G.id===t||G.name===t),j=new Ie(A.controlPlane.store).list(A.controlPlane.project.id).find((G)=>G.id===p||G.name===p);if(!L||!j)throw Error("Pool or server was not found.");A.store.addMember(L.id,j.id),n(`Attached ${j.name} to ${L.name}.`)})),N.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)=>es((p)=>{let A=p.service.explain(p.controlPlane.project.id,{purpose:t.purpose,resources:jl(t.resources),region:t.region,architecture:t.arch,labels:As(t.labels)});if(t.json)console.log(JSON.stringify(A,null,2));else c(["Pool","Server","Eligible","Available","Fit / spread / cost","Reasons"],A.map((L)=>[L.poolName,L.serverId??"provider-managed",L.eligible?"yes":"no",JSON.stringify(L.available),`${L.score.fit.toFixed(2)} / ${L.score.spread} / ${L.score.cost}`,L.reasons.join("; ")||"eligible"]))})),N.command("pool:drain <pool>","Move stateless placements and block stateful placements").action(async(t)=>es((p)=>{let A=p.store.listPools(p.controlPlane.project.id).find((j)=>j.id===t||j.name===t);if(!A)throw Error("Pool was not found.");let L=p.service.drainPool(A.id);n(`Draining ${A.name}: moved ${L.moved.length}, blocked ${L.blocked.length} stateful placement(s).`)}))}async function Pm(){let N=await Me(),t=y(process.cwd(),N),p=new fn(t.store),A=new hn(p);return{controlPlane:t,store:p,service:A}}async function ao(N){let t=await Pm();try{await N(t)}catch(p){r(p instanceof Error?p.message:String(p)),process.exitCode=1}finally{t.controlPlane.store.close()}}var ts=(N,t)=>{let p=N.store.list(N.controlPlane.project.id).find((A)=>A.id===t||A.name===t);if(!p)throw Error("Regional topology was not found.");return p},xm=(N)=>N.split(",").map((t,p)=>{let[A,L="aws"]=t.split(":");return{region:String(A),provider:L,role:p===0?"primary":"secondary"}}),Dm=(N)=>{let t=new Set(["s3","dynamodb","secrets"]),p=N.split(",").map((L)=>L.trim()),A=p.find((L)=>!t.has(L));if(A)throw Error(`Unsupported replication kind: ${A}`);return p};function Ta(N){N.command("region:list","List regional topology, traffic, and replication health").option("--json","Print structured JSON").action(async(t)=>ao((p)=>{let A=p.store.list(p.controlPlane.project.id).map((L)=>({topology:L,targets:p.store.targets(L.id),channels:p.store.channels(L.id),route:p.store.route(L.id),executions:p.store.executions(L.id)}));if(t.json)console.log(JSON.stringify(A,null,2));else c(["Name","Hostname","Active","Status","Revision","Replication"],A.map((L)=>[L.topology.name,L.topology.hostname,L.topology.activeRegion,L.topology.status,L.topology.revision??"—",`${L.channels.filter((j)=>j.status==="in_sync").length}/${L.channels.length} in sync`]))})),N.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,p)=>ao((A)=>{if(!p.hostname||!p.regions)throw Error("--hostname and --regions are required.");let L=A.service.create({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,hostname:p.hostname,regions:xm(p.regions),trafficPolicy:"active_passive",dataPolicy:{replicate:Dm(String(p.replicate)),maxLagSeconds:Number(p.maxLag),retainOnDestroy:!0},dnsProvider:"route53",cdnEnabled:!!p.cdn,wafEnabled:!!p.waf});n(`Created ${L.name} (${L.id}); run region:rollout with an immutable manifest.`)})),N.command("region:plan <name> <operation>","Preview exact regional execution steps").option("--target <region>","Failover or failback region").action(async(t,p,A)=>ao((L)=>c(["Step"],L.service.plan(ts(L,t).id,p,A.target).map((j)=>[j])))),N.command("region:rollout <name> <revision>","Queue secondary-first regional stack rollout").option("--manifest <file>","Regional stack manifest JSON").action(async(t,p,A)=>ao(async(L)=>{let j=A.manifest?await Bun.file(A.manifest).json():{},G=L.service.enqueue({topologyId:ts(L,t).id,kind:"rollout",revision:p,manifest:j});n(`Regional rollout queued: ${G.operation.id}`)})),N.command("region:failover <name> <region>","Queue health and RPO-gated failover").action(async(t,p)=>ao((A)=>{let L=A.service.enqueue({topologyId:ts(A,t).id,kind:"failover",targetRegion:p});n(`Failover queued: ${L.operation.id}`)})),N.command("region:failback <name> <region>","Queue health and RPO-gated failback").action(async(t,p)=>ao((A)=>{let L=A.service.enqueue({topologyId:ts(A,t).id,kind:"failback",targetRegion:p});n(`Failback queued: ${L.operation.id}`)})),N.command("region:destroy <name>","Drain traffic and delete regional stacks while retaining data").option("--confirm <name>","Exact topology name").action(async(t,p)=>ao((A)=>{let L=A.service.enqueue({topologyId:ts(A,t).id,kind:"destroy",confirmation:p.confirm});n(`Regional teardown queued: ${L.operation.id}; replicated data remains retained.`)}))}var ns="0.16.16";async function Tm(){let N=await Me(),t=y(process.cwd(),N),p=new yn(t.store),A=new Sn(p),L=["releases","artifacts","operations","logs","previews","snapshots","orphaned_resources"].map((j)=>new bn(j,t.store,t.project.id,t.organization.id));return{controlPlane:t,store:p,service:A,cleanup:L}}async function Li(N){let t=await Tm();try{await N(t)}catch(p){r(p instanceof Error?p.message:String(p)),process.exitCode=1}finally{t.controlPlane.store.close()}}function Na(N){N.command("maintenance:list","List updates, campaigns, cleanup plans, and DR drills").option("--json","Print structured JSON").action(async(t)=>Li((p)=>{let A={manifests:p.store.manifests(p.controlPlane.organization.id),windows:p.store.windows(p.controlPlane.project.id),campaigns:p.store.campaigns(p.controlPlane.project.id),cleanups:p.store.cleanups(p.controlPlane.project.id),drills:p.store.drills(p.controlPlane.project.id)},L=[...A.manifests.map((j)=>["update",j.version,j.compatibility.compatible?"compatible":"blocked",j.compatibility.reasons.join("; ")||j.channel]),...A.campaigns.map((j)=>["upgrade",j.id,j.status,j.currentStage??j.fromVersion]),...A.cleanups.map((j)=>["cleanup",j.kind,j.status,`${j.candidates.length} items / ${j.estimatedBytes} bytes`]),...A.drills.map((j)=>["DR drill",j.scenario,j.status,j.isolatedTarget])];if(t.json)console.log(JSON.stringify(A,null,2));else c(["Area","Name / version","Status","Detail"],L)})),N.command("maintenance:key:add <name> <pem>","Trust an Ed25519 update signing key").action(async(t,p)=>Li(async(A)=>{let L=A.store.addTrustedKey({organizationId:A.controlPlane.organization.id,name:t,publicKeyPem:await Bun.file(p).text()});n(`Trusted ${L.name}: ${L.fingerprint}`)})),N.command("maintenance:manifest <document> <signature> <key>","Verify and import a signed update manifest").action(async(t,p,A)=>Li(async(L)=>{let j=await Bun.file(t).json(),G=(await Bun.file(p).text()).trim(),Z=L.store.importManifest({organizationId:L.controlPlane.organization.id,keyId:A,document:j,signature:G,current:{schemaVersion:L.controlPlane.store.health().schemaVersion,currentVersion:ns,platform:process.platform,architecture:process.arch}});n(`Verified ${Z.version}; ${Z.compatibility.compatible?"compatible":"blocked: "+Z.compatibility.reasons.join("; ")}`)})),N.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,p,A)=>Li((L)=>{let j=L.store.createWindow({projectId:L.controlPlane.project.id,name:t,schedule:p,timezone:A.timezone,durationMinutes:Number(A.minutes),allowedOperations:["platform.upgrade"],requireApproval:!0});n(`Created ${j.name} (${j.id}).`)})),N.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,p,A)=>Li((L)=>{let j=new Ie(L.controlPlane.store).list(L.controlPlane.project.id).filter((ee)=>ee.status==="ready").map((ee)=>({serverId:ee.id,target:ee.name,previousVersion:ee.bootstrapVersion??ns}));if(!j.length)j.push({target:"control-plane",previousVersion:ns});let G=L.store.createCampaign({projectId:L.controlPlane.project.id,manifestId:t,fromVersion:ns,strategy:A.strategy==="rolling"?"rolling":"canary",batchSize:Number(A.batch),healthGate:{kind:"platform"},backupId:p,targets:j}),Z=L.service.enqueueCampaign(G.id);n(`Staged upgrade queued: ${Z.operation.id}`)})),N.command("cleanup:preview <kind>","Discover a bounded cleanup candidate set without deleting").option("--older-than <days>","Minimum age",{default:"30"}).action(async(t,p)=>Li(async(A)=>{let L=await A.service.previewCleanup({projectId:A.controlPlane.project.id,kind:t,criteria:{olderThanDays:Number(p.olderThan)}},A.cleanup);c(["ID","Candidate","Bytes"],L.candidates.map((j)=>[j.id,j.label,String(j.bytes)])),e(`Apply with: cloud cleanup:apply ${L.id} --confirm "DELETE ${L.candidates.length} ${L.kind}"`)})),N.command("cleanup:apply <plan>","Apply an unchanged cleanup preview").option("--confirm <text>","Exact preview confirmation").action(async(t,p)=>Li((A)=>{let L=A.service.enqueueCleanup(t,String(p.confirm??""));n(`Cleanup queued: ${L.operation.id}`)})),N.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,p,A)=>Li((L)=>{let j=L.store.createDrill({projectId:L.controlPlane.project.id,scenario:t,isolatedTarget:p,expectedRpoMinutes:Number(A.rpo),expectedRtoMinutes:Number(A.rto),recoveryPointId:A.recoveryPoint,topologyId:A.topology});n(`Planned isolated drill ${j.id}.`)})),N.command("dr:run <drill>","Queue an isolated disaster-recovery drill").action(async(t)=>Li((p)=>{let A=p.service.enqueueDrill(t);n(`DR drill queued: ${A.operation.id}`)}))}async function Oa(){try{return(await Me()).project.region||process.env.AWS_REGION||"us-east-1"}catch{return process.env.AWS_REGION||"us-east-1"}}function La(N){N.command("function:list","List all Lambda functions").option("--region <region>","AWS region").action(async(t)=>{i("Lambda Functions");let p=t?.region||await Oa(),A=new P(p);try{let{Functions:L=[]}=await A.listFunctions({MaxItems:50});if(!L.length){e("No functions found");return}c(["Name","Runtime","Memory","Timeout","Last Modified"],L.map((j)=>[j.FunctionName??"-",j.Runtime??"-",j.MemorySize?`${j.MemorySize} MB`:"-",j.Timeout?`${j.Timeout}s`:"-",j.LastModified??"-"]))}catch(L){r(`Failed to list functions: ${L.message}`),process.exitCode=1}}),N.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,p)=>{i(`Invoking ${t}`);let A=p?.region||await Oa(),L=new P(A),j=new o("Invoking function...");j.start();try{let G=await L.invoke({FunctionName:t,InvocationType:p?.async?"Event":"RequestResponse",Payload:p?.payload||"{}",LogType:p?.async?"None":"Tail"});if(G.FunctionError){if(j.fail(`Function returned an error (${G.FunctionError})`),G.Payload)e(G.Payload);process.exitCode=1;return}if(j.succeed(`Invoked (status ${G.StatusCode})`),G.Payload)e(`
317
+ `),xe.length>0){n(`Resources to add (${xe.length}):`);for(let Ee of xe){let Te=Ce[Ee].Type;e(` + ${Ee} (${Te})`)}console.log()}if(Me.length>0){r(`Resources to remove (${Me.length}):`);for(let Ee of Me){let Te=we[Ee].Type;e(` - ${Ee} (${Te})`)}console.log()}if(Ae.length>0){a(`Resources to modify (${Ae.length}):`);for(let Ee of Ae){let Te=Ce[Ee].Type;e(` ~ ${Ee} (${Te})`)}console.log()}e("Summary:"),e(` - Add: ${xe.length}`),e(` - Remove: ${Me.length}`),e(` - Modify: ${Ae.length}`),e("\nRun `cloud deploy` to apply these changes")}catch(m){r(`Diff failed: ${m.message}`)}})}function no(N,t=0){let m=Number(N);return Number.isFinite(m)?m:t}function ja(N){return no(N)*1024}function wp(N){let t=N?.date?.trim(),m=N?.time?.trim();if(!t||!m)return null;let A=new Date(`${t}T${m}Z`);return Number.isFinite(A.getTime())?A.toISOString():null}function Sp(N,t,m){let A;try{A=JSON.parse(N)}catch{return[]}let L=(A?.sysstat?.hosts??[]).flatMap((G)=>G.statistics??[]),j=[];for(let G of L){let Z=wp(G.timestamp);if(!Z)continue;let ee=new Date(Z).getTime();if(ee<t.getTime()||ee>m.getTime())continue;let ne=G["cpu-load"]?.find((be)=>be.cpu==="all")??G["cpu-load"]?.[0],oe=no(ne?.idle,100);j.push({timestamp:Z,cpuUsedPercent:Math.max(0,Math.min(100,100-oe)),cpuUserPercent:no(ne?.user),cpuSystemPercent:no(ne?.system),cpuIowaitPercent:no(ne?.iowait),memoryUsedPercent:no(G.memory?.["memused-percent"]),memoryUsedBytes:ja(G.memory?.memused),memoryAvailableBytes:ja(G.memory?.avail),swapUsedPercent:no(G.memory?.["swpused-percent"]),swapUsedBytes:ja(G.memory?.swpused)})}return j.sort((G,Z)=>G.timestamp.localeCompare(Z.timestamp))}function $p(N){return Object.fromEntries(Object.entries(N.time_series??{}).map(([t,m])=>[t,(m.values??[]).map(([A,L])=>({timestamp:new Date(Number(A)*1000).toISOString(),value:no(L,Number.NaN)})).filter((A)=>Number.isFinite(A.value))]))}function Fl(N){return N.length?N.reduce((t,m)=>t+m,0)/N.length:null}function Fa(N){return N.length?Math.max(...N):null}function Cp(N){return N.length?Math.min(...N):null}async function Bl(N){let[t,m]=await Promise.all([N.client.getServerMetrics(N.serverId,{types:["cpu","disk","network"],start:N.from,end:N.to,step:N.step}),Re(N.remote.host,"LC_ALL=C sadf -j -- -u -r -S",N.remote).catch(()=>"")]),A=Sp(m,N.from,N.to),L=$p(t),G=(L.cpu??[]).map((be)=>Math.max(0,Math.min(100,be.value/Math.max(1,N.cores)))),Z=A.map((be)=>be.cpuUsedPercent),ee=G.length?G:Z,ne=A.map((be)=>be.memoryUsedPercent),oe=A.map((be)=>be.swapUsedPercent);return{from:N.from.toISOString(),to:N.to.toISOString(),cores:N.cores,host:A,provider:L,summary:{cpuAveragePercent:Fl(ee),cpuPeakPercent:Fa(ee),memoryAveragePercent:Fl(ne),memoryPeakPercent:Fa(ne),memoryMinimumAvailableBytes:Cp(A.map((be)=>be.memoryAvailableBytes)),swapPeakPercent:Fa(oe)}}}function Ap(N){if(N.length===0)throw Error("A server name cannot be empty.");if(N.length>253)throw Error(`'${N}' is longer than the 253 characters a hostname allows.`);for(let t of N.split(".")){if(t.length===0)throw Error(`'${N}' 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 Ml(N,t,m){if(Ap(t),N===t)throw Error(`'${N}' is already named that.`);if((await m.takenNames()).some((j)=>j===t&&j!==N))throw Error(`'${t}' is already taken. Server names have to be unique within a provider project.`);let L=[];if(m.providerName&&m.renameProvider){let{providerName:j,renameProvider:G}=m;L.push({id:"provider",title:"Rename the server at the provider",change:{from:N,to:t},satisfied:async()=>await j()===t,apply:()=>G(t)})}if(m.stateName&&m.writeStateName){let{stateName:j,writeStateName:G}=m;L.push({id:"state-pin",title:"Update the recorded name in the local driver state",change:{from:N,to:t},satisfied:async()=>{let Z=await j();return Z===void 0||Z===t},apply:()=>G(t)})}if(m.remoteHostname&&m.setRemoteHostname){let{remoteHostname:j,setRemoteHostname:G}=m;L.push({id:"hostname",title:"Set the hostname on the box",change:{from:N,to:t},satisfied:async()=>await j()===t,apply:()=>G(t)})}return L.push({id:"inventory",title:"Rename the fleet inventory record",change:{from:N,to:t},satisfied:async()=>m.inventoryName()===t,apply:async()=>{await m.renameInventory(t)}}),{operation:"server:rename",target:N,steps:L}}function _l(N){let t=`'${N.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${N}/" /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 kp(){let N=await Ne(),t=y(process.cwd(),N),m=new Ie(t.store),A=new mn(m,[new tt("aws"),new tt("hetzner"),new tt("ssh")]);return{controlPlane:t,store:m,service:A}}async function xi(N){let t=await kp();try{return await N(t)}finally{t.controlPlane.store.close()}}var io=(N,t)=>{let m=N.store.list(N.controlPlane.project.id).find((A)=>A.id===t||A.name===t);if(!m)throw Error(`Server ${t} was not found.`);return m},hr=(N)=>{r(N instanceof Error?N.message:String(N)),process.exitCode=1};function Ba(N,t){let m=Number.parseInt(N??"",10);return Number.isFinite(m)&&m>=0?m:t}function Rp(N="3h"){let t=N.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 rs(N,t="%"){return N==null?"unavailable":`${N.toFixed(1)}${t}`}async function Ip(N,t){let m=await Ne();if(m.cloud?.provider!=="hetzner")throw Error("server:monitoring currently requires the Hetzner provider.");let A=t.env??process.env.CLOUD_ENV??"production",L=E(m,A),j=await de(L);if(!j?.serverId)throw Error(`No pinned Hetzner server was found for ${L}.`);if(![j.serverName,String(j.serverId),j.stackName].includes(N))throw Error(`Refusing to read ${N}: project state is pinned to ${j.serverName}.`);let G=rr(m),Z=new ze({apiToken:nr(G.apiToken,m)}),ee=await Z.getServer(j.serverId),ne=j.publicIp??ee.public_net.ipv4?.ip;if(!ne)throw Error(`Server ${ee.name} has no public IPv4 address.`);let oe=new Date,be=new Date(oe.getTime()-Rp(t.range)),we=Math.min(3600,Math.max(60,Ba(t.step,60))),Ce=await Bl({client:Z,serverId:ee.id,cores:Math.max(1,Number(ee.server_type.cores)||1),from:be,to:oe,step:we,remote:{host:ne,user:j.sshUser??G.sshUser,identityFile:G.sshPrivateKeyPath,connectTimeoutSec:15}});if(t.json){console.log(JSON.stringify({schemaVersion:1,server:ee.name,result:Ce},null,2));return}c(["Server","Window","CPU average / peak","RAM average / peak","Minimum available","Swap peak"],[[ee.name,`${Ce.from} to ${Ce.to}`,`${rs(Ce.summary.cpuAveragePercent)} / ${rs(Ce.summary.cpuPeakPercent)}`,`${rs(Ce.summary.memoryAveragePercent)} / ${rs(Ce.summary.memoryPeakPercent)}`,Ce.summary.memoryMinimumAvailableBytes==null?"unavailable":`${(Ce.summary.memoryMinimumAvailableBytes/1073741824).toFixed(2)} GB`,rs(Ce.summary.swapPeakPercent)]])}function zl(N){let t=N.targetType.prices?.find((m)=>m.location===N.location);return{serverId:N.serverId,serverName:N.serverName,location:N.location,from:N.currentType.name,to:N.targetType.name,capacityAvailable:N.capacityAvailable,alreadyComplete:N.alreadyComplete,upgradeDisk:N.upgradeDisk,target:{cores:N.targetType.cores,memoryGb:N.targetType.memory,diskGb:N.targetType.disk,architecture:N.targetType.architecture,cpuType:N.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 Pp(N){let t=zl(N);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 xp(N,t,m){let A=await Ne();if(A.cloud?.provider!=="hetzner")throw Error("server:resize currently requires the Hetzner provider.");let L=m.env??process.env.CLOUD_ENV??"production",j=E(A,L),G=await de(j),Z=rr(A),ee=new ze({apiToken:nr(Z.apiToken,A)}),oe=G&&(N===G.serverName||N===String(G.serverId)||N===G.stackName)?await ee.getServer(G.serverId):(await ee.listServers()).find((Ke)=>Ke.name===N||String(Ke.id)===N);if(!oe)throw Error(`Hetzner server ${N} was not found.`);if(G?.serverId&&G.serverId!==oe.id)throw Error(`Refusing to resize ${N}: project state is pinned to server ${G.serverId}.`);let be=at(t),we=m.upgradeDisk!==!1,Ce=await Hn(ee,oe.id,be,we);if(m.json)console.log(JSON.stringify({schemaVersion:1,plan:zl(Ce)},null,2));else Pp(Ce);if(!m.apply){if(!m.json)e(`Preview only. Re-run with --apply --confirm ${oe.name} to execute.`);return}if(m.confirm!==oe.name)throw Error(`Resize requires --confirm ${oe.name}.`);let De=G?.publicIp??oe.public_net.ipv4?.ip;if(!De)throw Error(`Server ${oe.name} has no public IPv4 address for verification.`);let Se={host:De,user:G?.sshUser??Z.sshUser,identityFile:Z.sshPrivateKeyPath,connectTimeoutSec:15,sshTimeoutMs:600000,routeTimeoutMs:20000},xe=await Ko(j),Me=new Date().toISOString(),Ae={schemaVersion:1,operationId:xe?.targetType===be&&xe.serverId===oe.id?xe.operationId:crypto.randomUUID(),stackName:j,serverId:oe.id,serverName:oe.name,sourceType:Ce.currentType.name,targetType:be,upgradeDisk:we,phase:"planning",status:"running",attempts:xe?.targetType===be?xe.attempts:0,startedAt:xe?.targetType===be?xe.startedAt:Me,updatedAt:Me,manifest:xe?.targetType===be?xe.manifest:void 0};await lt(Ae);let Ee=Ba(m.pollSeconds,900)*1000,Te=Ba(m.maxWaitMinutes,0)*60000,Qe=Date.now();for(;;){let Ke=await Hn(ee,oe.id,be,we);if(!Ke.capacityAvailable&&!Ke.alreadyComplete){if(Ae={...Ae,phase:"waiting-capacity",status:"waiting-capacity",updatedAt:new Date().toISOString()},await lt(Ae),!m.wait){if(!m.json)e(`${be} has no capacity in ${Ke.location}. The server remains running and unchanged. Use --wait to poll safely.`);return}if(Te>0&&Date.now()-Qe>=Te)throw Error(`Timed out waiting for ${be} capacity in ${Ke.location}.`);if(!m.json)e(`Waiting ${Math.round(Ee/1000)} seconds for ${be} capacity...`);await Bun.sleep(Ee);continue}let Ye=await Jo(j);try{let et=await(async()=>{try{return Ae={...Ae,status:"running",attempts:Ae.attempts+1,updatedAt:new Date().toISOString()},await lt(Ae),await Vo({client:ee,serverId:oe.id,targetType:be,upgradeDisk:we,resumeFromOff:Ae.phase==="shutting-down"||Ae.phase==="resizing"||Ae.phase==="powering-on"||Ae.phase==="verifying",manifest:Ae.manifest,hooks:{preflight:async()=>{let Pt=await zo(Se);return Ae={...Ae,manifest:Pt,updatedAt:new Date().toISOString()},await lt(Ae),Pt},afterBoot:async(Pt,Bt,vt,Ot)=>Ho(Se,Bt,vt,Ot),onPhase:async(Pt,Bt)=>{if(Ae={...Ae,phase:Pt,status:Pt==="waiting-capacity"?"waiting-capacity":Pt==="complete"?"completed":"running",updatedAt:new Date().toISOString()},await lt(Ae),!m.json)e(`${Pt}${Bt?` ${JSON.stringify(Bt)}`:""}`)}}})}finally{await Ye()}})();if(Ae={...Ae,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 lt(Ae),m.json)console.log(JSON.stringify({schemaVersion:1,result:et},null,2));else if(et.status==="completed"||et.status==="already-complete")n(`${oe.name} is on ${be} and passed full post-boot verification.`);else if(et.status==="recovered"&&et.retryable&&m.wait){e(`Capacity race recovered safely. Waiting ${Math.round(Ee/1000)} seconds before retry.`),await Bun.sleep(Ee);continue}else if(et.status==="recovered")throw Error(`Resize did not complete and the original server was recovered: ${et.error}`);return}catch(et){throw Ae={...Ae,status:"failed",lastError:et instanceof Error?et.message:String(et),updatedAt:new Date().toISOString()},await lt(Ae),et}}}async function Dp(N,t){let m=await Ne();if(m.cloud?.provider!=="hetzner")throw Error("server:optimize currently requires the Hetzner provider.");let A=t.env??process.env.CLOUD_ENV??"production",L=E(m,A),j=await de(L);if(!j?.serverId)throw Error(`No pinned Hetzner server was found for ${L}.`);if(![j.serverName,String(j.serverId),j.stackName].includes(N))throw Error(`Refusing to optimize ${N}: project state is pinned to ${j.serverName}.`);let G=rr(m),Z=new ze({apiToken:nr(G.apiToken,m)}),ee=await Z.getServer(j.serverId),ne=j.publicIp??ee.public_net.ipv4?.ip;if(!ne)throw Error(`Server ${ee.name} has no public IPv4 address.`);let oe=Li(m);if(t.json)console.log(JSON.stringify({schemaVersion:1,server:ee.name,plan:oe},null,2));else c(["Server","Firewall ports","Monitoring","Auto updates","Swap","SSH passwords"],[[ee.name,oe.firewallPorts.join(","),oe.monitoring?"on":"off",oe.autoUpdates?"on":"off",`${oe.swapGb} GB`,"disabled"]]);if(!t.apply){if(!t.json)e(`Preview only. Re-run with --apply --confirm ${ee.name} to execute.`);return}if(t.confirm!==ee.name)throw Error(`Optimization requires --confirm ${ee.name}.`);let be={host:ne,user:j.sshUser??G.sshUser,identityFile:G.sshPrivateKeyPath,connectTimeoutSec:15},we=await jr(be),Ce=we.routeProbes.filter((vt)=>!vt.ok),De=m.infrastructure?.compute,Se=X(De)?new Set(["nginx.service","bun-gateway.service","ts-cloud-nginx.service"]):new Set,xe=we.failedUnits.filter((vt)=>!Se.has(vt));if(xe.length>0)throw Error(`Preflight found failed services: ${xe.join(", ")}`);if(Ce.length>0)throw Error(`Preflight found unhealthy routes: ${Ce.map((vt)=>vt.domain).join(", ")}`);let Me=X(De)?[]:Object.values(m.sites??{}).flatMap((vt)=>typeof vt?.port==="number"&&![80,443].includes(vt.port)?[vt.port]:[]),Ae=Br({allowSsh:De?.allowSsh!==!1,sitePorts:Me,allowedPorts:De?.firewall?.allowedPorts}),Ee=(j.firewallId?(await Z.listFirewalls()).find((vt)=>vt.id===j.firewallId):void 0)??(await Z.listFirewalls()).find((vt)=>vt.name===`${m.project.slug}-${A}-app-fw`);if(!Ee)throw Error(`Managed firewall for ${ee.name} was not found.`);let Te=await Z.setFirewallRules(Ee.id,Ae);await Promise.all(Te.map((vt)=>Z.waitForAction(vt.id))),await qo(m,be);let[Qe,Ke]=await Promise.all([jr(be),Wo(be)]),Ye=Go(oe,Ke),et=Ia(we,Qe),Pt=Qe.routeProbes.filter((vt)=>!vt.ok);if(et.stoppedServices.length>0)Ye.push(`services stopped: ${et.stoppedServices.join(", ")}`);if(et.changedRouteFragments.length>0)Ye.push(`route fragments changed: ${et.changedRouteFragments.join(", ")}`);if(et.missingRouteIds.length>0)Ye.push(`routes disappeared: ${et.missingRouteIds.join(", ")}`);if(et.changedReleaseLinks.length>0)Ye.push(`release links changed: ${et.changedReleaseLinks.join(", ")}`);if(et.missingData.length>0)Ye.push(`databases or volumes disappeared: ${et.missingData.join(", ")}`);if(Pt.length>0)Ye.push(`unhealthy routes: ${Pt.map((vt)=>vt.domain).join(", ")}`);if(Ye.length>0)throw Error(`Host optimization verification failed: ${Ye.join("; ")}`);let Bt={serverId:ee.id,serverName:ee.name,services:Qe.runningServices.length,routes:Qe.routeIds.length,healthyDomains:Qe.routeProbes.length,releaseLinks:Qe.releaseLinks.length,databasesAndVolumes:Qe.dataCatalog.length,report:Ke};if(t.json)console.log(JSON.stringify({schemaVersion:1,result:Bt},null,2));else n(`${ee.name} passed full host, rpx route, service, release, and data verification.`)}async function Ep(N,t,m){let A=await Ne(),L=m.env??"production",j=E(A,L);await xi(async(G)=>{let Z=io(G,N),ee=G.store.list(G.controlPlane.project.id,!0),ne=Z.provider==="hetzner"&&Z.providerId?(()=>{try{let Ee=rr(A);return new ze({apiToken:nr(Ee.apiToken,A)})}catch{return null}})():null,oe=Z.providerId?Number(Z.providerId):Number.NaN,be=await de(j),we=be!=null&&be.serverId===oe,Ce=new Rr,De=Z.trustState==="pinned",Se={takenNames:async()=>{let Ee=ee.map((Te)=>Te.name);if(ne)Ee.push(...(await ne.listServers()).map((Te)=>Te.name));return Ee},inventoryName:()=>G.store.get(Z.id)?.name??Z.name,renameInventory:(Ee)=>{G.store.update(Z.id,{name:Ee})},...ne&&Number.isFinite(oe)?{providerName:async()=>(await ne.getServer(oe)).name,renameProvider:async(Ee)=>{await ne.renameServer(oe,Ee)}}:{},...we?{stateName:async()=>(await de(j))?.serverName,writeStateName:async(Ee)=>{let Te=await de(j);if(Te)await ct(j,{...Te,serverName:Ee})}}:{},...De?{remoteHostname:async()=>{let Ee=await Ce.exec(Z,"hostname");return Ee.code===0?Ee.stdout.trim():void 0},setRemoteHostname:async(Ee)=>{let Te=await Ce.exec(Z,_l(Ee));if(Te.code!==0)throw Error(Te.stderr.trim()||`hostname exited ${Te.code}`)}}:{}},xe=await Ml(Z.name,t,Se),Me=await Is(xe);if(!m.apply){let Ee=[ne?"":"provider record (no provider id or no API token)",we?"":"local state pin (none points at this server)",De?"":"box hostname (host key is not pinned — run server:validate first)"].filter(Boolean);if(m.json){console.log(JSON.stringify({schemaVersion:1,operation:xe.operation,target:xe.target,rename:{from:Z.name,to:t},notCovered:Ee,steps:Me.map((Te)=>({id:Te.step.id,title:Te.step.title,state:Te.state,change:Te.step.change,reason:Te.reason}))},null,2));return}for(let Te of Ps(xe,Me))console.log(Te);for(let Te of Ee)console.log(` not covered: ${Te}`);console.log(" Re-run with --apply to perform it.");return}let Ae=await xs(xe,Me,{log:(Ee)=>e(Ee),audit:(Ee)=>G.controlPlane.store.appendEvent({projectId:G.controlPlane.project.id,resourceId:Z.resourceId,type:`${Ee.operation}.${Ee.step}.${Ee.state}`,level:Ee.state==="failed"?"error":"info",payload:{target:Ee.target,renameTo:t,...Ee.error?{error:Ee.error}:{}}})});if(m.json)console.log(JSON.stringify({schemaVersion:1,outcome:Ae},null,2));if(!Ae.success){let Ee=Ae.steps.find((Te)=>Te.state==="failed");throw Error(`${xe.operation} stopped at '${Ee?.title}': ${Ee?.error}. Earlier steps stayed applied; re-run the same command to continue from here.`)}if(!m.json)n(`Renamed ${Z.name} to ${t}.`)})}function Ma(N){N.command("capabilities [server]","Show provider and target operation support").option("--json","Print structured JSON").action(async(t,m)=>{try{await xi(async(A)=>{let L=t?[io(A,t)]:A.store.list(A.controlPlane.project.id),j=L.flatMap((G)=>Object.entries(G.capabilities).map(([Z,ee])=>({server:G.name,provider:G.provider,action:Z,...ee})));if(m.json)console.log(JSON.stringify({schemaVersion:1,targets:j},null,2));else c(["Server","Provider","Action","Support","Explanation"],j.map((G)=>[G.server,G.provider,G.action,G.supported?"supported":"unsupported",G.reason??"—"]))})}catch(A){hr(A)}}),N.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 xp(t,m,A)}catch(L){hr(L)}}),N.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 Dp(t,m)}catch(A){hr(A)}}),N.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 Ip(t,m)}catch(A){hr(A)}}),N.command("server:list","List fleet servers").option("--json","Print structured JSON").action(async(t)=>{try{await xi(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((L)=>[L.name,L.providerId??"—",`${L.provider} / ${L.region??"external"}`,`${L.status} / ${L.trustState}`,L.roles.join(","),`${L.capacity.cpu??0} / ${L.capacity.memoryBytes??0}`,L.heartbeatAt??"never"]))})}catch(m){hr(m)}}),N.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 xi(async(A)=>{let L=m.provider;if(!["aws","hetzner","ssh"].includes(L)||!m.endpoint)throw Error("A supported --provider and --endpoint are required; provisioning and enrollment are separate operations.");let j=A.service.enroll({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,provider:L,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((G)=>G.split("=")).filter((G)=>G[0]&&G[1]))});n(`Enrolled ${j.name} as ${j.id}; no remote mutation was performed.`)})}catch(A){hr(A)}}),N.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 xi(async(A)=>{let L=io(A,t);if(L=await A.service.test(L.id),L.trustState==="rotation_pending"){if(!m.acceptHostKey)throw Error(`Host key changed to ${L.pendingHostKey}; review it first.`);L=A.service.reviewHostKey(L.id,m.acceptHostKey)}let j=await A.service.validate(L.id);if(m.json)console.log(JSON.stringify(j.validation,null,2));else c(["Severity","Code","Finding","Remediation"],(j.validation?.findings??[]).map((G)=>[G.severity,G.code,G.message,G.remediation??"—"]));if(!j.validation?.valid)process.exitCode=1})}catch(A){hr(A)}}),N.command("server:bootstrap <name>","Preview or queue bootstrap").option("--apply","Apply reviewed plan").action(async(t,m)=>{try{await xi(async(A)=>{let L=A.service.bootstrap(io(A,t).id,!!m.apply);if(L.preview)c(["Step"],L.steps.map((j)=>[j]));else n(`Bootstrap queued: ${L.operation?.id}`)})}catch(A){hr(A)}}),N.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 Ep(t,m,A)}catch(L){hr(L)}}),N.command("server:drain <name>","Drain without terminating").option("--complete","Mark movement complete").action(async(t,m)=>{try{await xi(async(A)=>n(A.service.drain(io(A,t).id,!!m.complete).status))}catch(A){hr(A)}}),N.command("server:uncordon <name>","Return a server to scheduling").action(async(t)=>{try{await xi(async(m)=>{m.service.uncordon(io(m,t).id),n(`Uncordoned ${t}.`)})}catch(m){hr(m)}}),N.command("server:archive <name>","Archive inventory without termination").option("--confirm <name>","Exact name").action(async(t,m)=>{try{await xi(async(A)=>{A.service.archive(io(A,t).id,m.confirm??""),n(`Archived ${t}; provider infrastructure was not terminated.`)})}catch(A){hr(A)}}),N.command("server:ssh <name>","Open strict SSH using the enrolled endpoint").action(async(t)=>{try{await xi(async(m)=>{let A=io(m,t);if(A.trustState!=="pinned")throw Error("Validate and pin the server host key first.");let L=Bun.spawn(["ssh","-p",String(A.sshPort),"-o","StrictHostKeyChecking=yes",`${A.sshUser}@${A.endpoint}`],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}),j=await L.exited;if(j)process.exitCode=j})}catch(m){hr(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"]])N.command(t,m).action(async(...A)=>Vr(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 Ul,mkdirSync as Tp,readFileSync as Np,writeFileSync as Op}from"node:fs";import{join as Vl,resolve as Lp}from"node:path";var za=(N)=>{r(N instanceof Error?N.message:String(N)),process.exitCode=1};async function jp(){try{return await Ne()}catch{return}}function Fp(N){if(N===void 0)return;let t=Number(N);if(!Number.isInteger(t)||t<1||t>65535)throw Error(`--port must be a port number, not '${N}'.`);return t}function Wl(N,t,m){let A=N?.ssh?.hosts?.find((L)=>L.host===t);return new cr({hosts:t?[A??{host:t}]:N?.ssh?.hosts,user:m.user,port:Fp(m.port),privateKeyPath:m.key,hostKey:N?.ssh?.hostKey,sudo:N?.ssh?.sudo,profile:m.profile??N?.ssh?.profile,publicIp:N?.ssh?.publicIp,lan:N?.ssh?.lan})}var ql=1073741824;function Ua(N){N.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 jp(),L=Wl(A,t,m),j=t??L.targetHost(),G=await L.ensureHostKey(j),{facts:Z,findings:ee}=await L.preflight(j);if(m.json)console.log(JSON.stringify({schemaVersion:1,host:j,hostKeyFingerprint:G.fingerprint,facts:Z,findings:ee},null,2));else{if(i(`Preflight ${j}`),G.pinnedNow)e(`Pinned host key ${G.fingerprint}`);if(e(`${Z.os} on ${Z.arch}, ${Z.cpuCores} cores, ${(Z.memoryBytes/ql).toFixed(1)} GiB memory, ${(Z.diskFreeBytes/ql).toFixed(1)} GiB free`),Z.piModel)e(Z.piModel);if(Z.lanIp)e(`LAN address ${Z.lanIp}`);if(console.log(Fr(ee)),ar(ee))r("The host is not ready; fix the errors above and run the preflight again.");else n("The host is ready to adopt.")}if(ar(ee))process.exitCode=1}catch(A){za(A)}}),N.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 Ne(),A=t.env??"production";if(t.dryRun){let G=sr(m),[Z]=G.hosts;console.log(or({config:m,environment:A,profile:G.profile,sudoUser:Z.user!=="root"?Z.user:void 0,lan:G.lan}));return}let L=Wl(m,void 0,{});i(`Adopt ${L.targetHost()}`);let j=await L.provisionComputeInfrastructure({config:m,environment:A});n(`${j.appPublicIp} is bootstrapped; deploy with: cloud deploy --env ${A}`)}catch(m){za(m)}}),N.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 $i))throw Error(`--os must be raspberry-pi-os or ubuntu, not '${m}'.`);let A=await Ne(),L=t.env??"production",j=sr(A,{hosts:A.ssh?.hosts?.length?void 0:[{host:t.hostname}],profile:m==="raspberry-pi-os"?"raspberry-pi":void 0}),G=t.user??(A.ssh?.hosts?.[0]?.user??"pi"),Z=Ur(t.key??"~/.ssh/id_ed25519.pub");if(!Ul(Z))throw Error(`Public key not found at ${Z}; pass --key <path to a .pub file>.`);let ee=Np(Z,"utf8").trim(),ne;if(t.wifiSsid){let De=t.wifiPassphraseEnv??"WIFI_PASSWORD",Se=process.env[De];if(!Se)throw Error(`--wifi-ssid needs the passphrase in $${De} (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:Se,country:t.wifiCountry}}let oe=or({config:A,environment:L,profile:j.profile,sudoUser:G!=="root"?G:void 0,lan:j.lan}),be=Oi({hostname:t.hostname,user:G,publicKey:ee,timezone:t.timezone,locale:t.locale,wifi:ne},oe,{os:m}),we=Lp(t.out);Tp(we,{recursive:!0});let Ce=Object.keys(be.files).filter((De)=>Ul(Vl(we,De)));if(Ce.length>0&&!t.force)throw Error(`${Ce.join(", ")} already exist in ${we}; pass --force to overwrite.`);for(let[De,Se]of Object.entries(be.files))Op(Vl(we,De),Se,{mode:384});i(`First boot for ${t.hostname}`),n(`Wrote ${Object.keys(be.files).join(", ")} to ${we}`),console.log(be.instructions)}catch(m){za(m)}})}async function Bp(){let N=await Ne(),t=y(process.cwd(),N),m=new gn(t.store),A=new fn(m,new Ie(t.store));return{controlPlane:t,store:m,service:A}}async function is(N){let t=await Bp();try{await N(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}var Ts=(N)=>Object.fromEntries(String(N??"").split(",").map((t)=>t.split("=")).filter((t)=>t[0]&&t[1])),Gl=(N)=>Object.fromEntries(Object.entries(Ts(N)).map(([t,m])=>[t,Number(m)]));function Va(N){N.command("pool:list","List capacity pools and reservations").option("--json","Print structured JSON").action(async(t)=>is((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((L)=>[L.name,L.purpose,L.backend,`${L.region??"any"} / ${L.architecture??"any"}`,L.status,JSON.stringify(L.capacity),String(L.concurrency)]))})),N.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)=>is((A)=>{let{purpose:L,backend:j}=m;if(!["application","build","worker","monitoring","backup"].includes(L)||!["server","ecs","asg"].includes(j))throw Error("Unsupported pool purpose or backend.");let G=A.store.createPool({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,purpose:L,backend:j,region:m.region,architecture:m.arch,labels:Ts(m.labels),requiredServerLabels:Ts(m.selector),toleratedTaints:[],capacity:{cpu:0,memoryBytes:0,diskBytes:0,gpu:0,...Gl(m.capacity)},maxWorkloads:Number(m.max),costWeight:1,concurrency:Number(m.concurrency),ephemeralWorkspaces:!0,allowProductionSecrets:!1,status:"active"});n(`Created ${G.name} (${G.id}).`)})),N.command("pool:add-server <pool> <server>","Attach an enrolled server to a pool").action(async(t,m)=>is((A)=>{let L=A.store.listPools(A.controlPlane.project.id).find((G)=>G.id===t||G.name===t),j=new Ie(A.controlPlane.store).list(A.controlPlane.project.id).find((G)=>G.id===m||G.name===m);if(!L||!j)throw Error("Pool or server was not found.");A.store.addMember(L.id,j.id),n(`Attached ${j.name} to ${L.name}.`)})),N.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)=>is((m)=>{let A=m.service.explain(m.controlPlane.project.id,{purpose:t.purpose,resources:Gl(t.resources),region:t.region,architecture:t.arch,labels:Ts(t.labels)});if(t.json)console.log(JSON.stringify(A,null,2));else c(["Pool","Server","Eligible","Available","Fit / spread / cost","Reasons"],A.map((L)=>[L.poolName,L.serverId??"provider-managed",L.eligible?"yes":"no",JSON.stringify(L.available),`${L.score.fit.toFixed(2)} / ${L.score.spread} / ${L.score.cost}`,L.reasons.join("; ")||"eligible"]))})),N.command("pool:drain <pool>","Move stateless placements and block stateful placements").action(async(t)=>is((m)=>{let A=m.store.listPools(m.controlPlane.project.id).find((j)=>j.id===t||j.name===t);if(!A)throw Error("Pool was not found.");let L=m.service.drainPool(A.id);n(`Draining ${A.name}: moved ${L.moved.length}, blocked ${L.blocked.length} stateful placement(s).`)}))}async function Mp(){let N=await Ne(),t=y(process.cwd(),N),m=new hn(t.store),A=new yn(m);return{controlPlane:t,store:m,service:A}}async function fo(N){let t=await Mp();try{await N(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}var os=(N,t)=>{let m=N.store.list(N.controlPlane.project.id).find((A)=>A.id===t||A.name===t);if(!m)throw Error("Regional topology was not found.");return m},_p=(N)=>N.split(",").map((t,m)=>{let[A,L="aws"]=t.split(":");return{region:String(A),provider:L,role:m===0?"primary":"secondary"}}),zp=(N)=>{let t=new Set(["s3","dynamodb","secrets"]),m=N.split(",").map((L)=>L.trim()),A=m.find((L)=>!t.has(L));if(A)throw Error(`Unsupported replication kind: ${A}`);return m};function Wa(N){N.command("region:list","List regional topology, traffic, and replication health").option("--json","Print structured JSON").action(async(t)=>fo((m)=>{let A=m.store.list(m.controlPlane.project.id).map((L)=>({topology:L,targets:m.store.targets(L.id),channels:m.store.channels(L.id),route:m.store.route(L.id),executions:m.store.executions(L.id)}));if(t.json)console.log(JSON.stringify(A,null,2));else c(["Name","Hostname","Active","Status","Revision","Replication"],A.map((L)=>[L.topology.name,L.topology.hostname,L.topology.activeRegion,L.topology.status,L.topology.revision??"—",`${L.channels.filter((j)=>j.status==="in_sync").length}/${L.channels.length} in sync`]))})),N.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)=>fo((A)=>{if(!m.hostname||!m.regions)throw Error("--hostname and --regions are required.");let L=A.service.create({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,hostname:m.hostname,regions:_p(m.regions),trafficPolicy:"active_passive",dataPolicy:{replicate:zp(String(m.replicate)),maxLagSeconds:Number(m.maxLag),retainOnDestroy:!0},dnsProvider:"route53",cdnEnabled:!!m.cdn,wafEnabled:!!m.waf});n(`Created ${L.name} (${L.id}); run region:rollout with an immutable manifest.`)})),N.command("region:plan <name> <operation>","Preview exact regional execution steps").option("--target <region>","Failover or failback region").action(async(t,m,A)=>fo((L)=>c(["Step"],L.service.plan(os(L,t).id,m,A.target).map((j)=>[j])))),N.command("region:rollout <name> <revision>","Queue secondary-first regional stack rollout").option("--manifest <file>","Regional stack manifest JSON").action(async(t,m,A)=>fo(async(L)=>{let j=A.manifest?await Bun.file(A.manifest).json():{},G=L.service.enqueue({topologyId:os(L,t).id,kind:"rollout",revision:m,manifest:j});n(`Regional rollout queued: ${G.operation.id}`)})),N.command("region:failover <name> <region>","Queue health and RPO-gated failover").action(async(t,m)=>fo((A)=>{let L=A.service.enqueue({topologyId:os(A,t).id,kind:"failover",targetRegion:m});n(`Failover queued: ${L.operation.id}`)})),N.command("region:failback <name> <region>","Queue health and RPO-gated failback").action(async(t,m)=>fo((A)=>{let L=A.service.enqueue({topologyId:os(A,t).id,kind:"failback",targetRegion:m});n(`Failback queued: ${L.operation.id}`)})),N.command("region:destroy <name>","Drain traffic and delete regional stacks while retaining data").option("--confirm <name>","Exact topology name").action(async(t,m)=>fo((A)=>{let L=A.service.enqueue({topologyId:os(A,t).id,kind:"destroy",confirmation:m.confirm});n(`Regional teardown queued: ${L.operation.id}; replicated data remains retained.`)}))}var ss="0.16.18";async function Vp(){let N=await Ne(),t=y(process.cwd(),N),m=new Sn(t.store),A=new bn(m),L=["releases","artifacts","operations","logs","previews","snapshots","orphaned_resources"].map((j)=>new vn(j,t.store,t.project.id,t.organization.id));return{controlPlane:t,store:m,service:A,cleanup:L}}async function Bi(N){let t=await Vp();try{await N(t)}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}finally{t.controlPlane.store.close()}}function qa(N){N.command("maintenance:list","List updates, campaigns, cleanup plans, and DR drills").option("--json","Print structured JSON").action(async(t)=>Bi((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)},L=[...A.manifests.map((j)=>["update",j.version,j.compatibility.compatible?"compatible":"blocked",j.compatibility.reasons.join("; ")||j.channel]),...A.campaigns.map((j)=>["upgrade",j.id,j.status,j.currentStage??j.fromVersion]),...A.cleanups.map((j)=>["cleanup",j.kind,j.status,`${j.candidates.length} items / ${j.estimatedBytes} bytes`]),...A.drills.map((j)=>["DR drill",j.scenario,j.status,j.isolatedTarget])];if(t.json)console.log(JSON.stringify(A,null,2));else c(["Area","Name / version","Status","Detail"],L)})),N.command("maintenance:key:add <name> <pem>","Trust an Ed25519 update signing key").action(async(t,m)=>Bi(async(A)=>{let L=A.store.addTrustedKey({organizationId:A.controlPlane.organization.id,name:t,publicKeyPem:await Bun.file(m).text()});n(`Trusted ${L.name}: ${L.fingerprint}`)})),N.command("maintenance:manifest <document> <signature> <key>","Verify and import a signed update manifest").action(async(t,m,A)=>Bi(async(L)=>{let j=await Bun.file(t).json(),G=(await Bun.file(m).text()).trim(),Z=L.store.importManifest({organizationId:L.controlPlane.organization.id,keyId:A,document:j,signature:G,current:{schemaVersion:L.controlPlane.store.health().schemaVersion,currentVersion:ss,platform:process.platform,architecture:process.arch}});n(`Verified ${Z.version}; ${Z.compatibility.compatible?"compatible":"blocked: "+Z.compatibility.reasons.join("; ")}`)})),N.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)=>Bi((L)=>{let j=L.store.createWindow({projectId:L.controlPlane.project.id,name:t,schedule:m,timezone:A.timezone,durationMinutes:Number(A.minutes),allowedOperations:["platform.upgrade"],requireApproval:!0});n(`Created ${j.name} (${j.id}).`)})),N.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)=>Bi((L)=>{let j=new Ie(L.controlPlane.store).list(L.controlPlane.project.id).filter((ee)=>ee.status==="ready").map((ee)=>({serverId:ee.id,target:ee.name,previousVersion:ee.bootstrapVersion??ss}));if(!j.length)j.push({target:"control-plane",previousVersion:ss});let G=L.store.createCampaign({projectId:L.controlPlane.project.id,manifestId:t,fromVersion:ss,strategy:A.strategy==="rolling"?"rolling":"canary",batchSize:Number(A.batch),healthGate:{kind:"platform"},backupId:m,targets:j}),Z=L.service.enqueueCampaign(G.id);n(`Staged upgrade queued: ${Z.operation.id}`)})),N.command("cleanup:preview <kind>","Discover a bounded cleanup candidate set without deleting").option("--older-than <days>","Minimum age",{default:"30"}).action(async(t,m)=>Bi(async(A)=>{let L=await A.service.previewCleanup({projectId:A.controlPlane.project.id,kind:t,criteria:{olderThanDays:Number(m.olderThan)}},A.cleanup);c(["ID","Candidate","Bytes"],L.candidates.map((j)=>[j.id,j.label,String(j.bytes)])),e(`Apply with: cloud cleanup:apply ${L.id} --confirm "DELETE ${L.candidates.length} ${L.kind}"`)})),N.command("cleanup:apply <plan>","Apply an unchanged cleanup preview").option("--confirm <text>","Exact preview confirmation").action(async(t,m)=>Bi((A)=>{let L=A.service.enqueueCleanup(t,String(m.confirm??""));n(`Cleanup queued: ${L.operation.id}`)})),N.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)=>Bi((L)=>{let j=L.store.createDrill({projectId:L.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 ${j.id}.`)})),N.command("dr:run <drill>","Queue an isolated disaster-recovery drill").action(async(t)=>Bi((m)=>{let A=m.service.enqueueDrill(t);n(`DR drill queued: ${A.operation.id}`)}))}async function Ga(){try{return(await Ne()).project.region||process.env.AWS_REGION||"us-east-1"}catch{return process.env.AWS_REGION||"us-east-1"}}function Ha(N){N.command("function:list","List all Lambda functions").option("--region <region>","AWS region").action(async(t)=>{i("Lambda Functions");let m=t?.region||await Ga(),A=new R(m);try{let{Functions:L=[]}=await A.listFunctions({MaxItems:50});if(!L.length){e("No functions found");return}c(["Name","Runtime","Memory","Timeout","Last Modified"],L.map((j)=>[j.FunctionName??"-",j.Runtime??"-",j.MemorySize?`${j.MemorySize} MB`:"-",j.Timeout?`${j.Timeout}s`:"-",j.LastModified??"-"]))}catch(L){r(`Failed to list functions: ${L.message}`),process.exitCode=1}}),N.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 Ga(),L=new R(A),j=new o("Invoking function...");j.start();try{let G=await L.invoke({FunctionName:t,InvocationType:m?.async?"Event":"RequestResponse",Payload:m?.payload||"{}",LogType:m?.async?"None":"Tail"});if(G.FunctionError){if(j.fail(`Function returned an error (${G.FunctionError})`),G.Payload)e(G.Payload);process.exitCode=1;return}if(j.succeed(`Invoked (status ${G.StatusCode})`),G.Payload)e(`
319
319
  Response:`),e(G.Payload);if(G.LogResult)e(`
320
- Logs:`),e(Buffer.from(G.LogResult,"base64").toString("utf-8"))}catch(G){j.fail(`Invocation failed: ${G.message}`),process.exitCode=1}}),N.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,p)=>{i(`Logs for ${t}`);let A=p?.region||await Oa(),L=new ne(A),j=t.startsWith("/aws/lambda/")?t:`/aws/lambda/${t}`,G=Number(p?.since??15),Z=(ee)=>{for(let re of ee){let oe=re.timestamp?new Date(re.timestamp).toISOString():"";e(`${oe} ${(re.message??"").trimEnd()}`)}};try{let ee=Date.now()-G*60000,{events:re=[]}=await L.filterLogEvents({logGroupName:j,startTime:ee,filterPattern:p?.filter,limit:200});if(Z(re),re.length)ee=(re[re.length-1].timestamp??ee)+1;if(p?.tail){e(`
321
- (streaming — Ctrl+C to stop)`);for(;;){await new Promise((be)=>setTimeout(be,3000));let{events:oe=[]}=await L.filterLogEvents({logGroupName:j,startTime:ee,filterPattern:p?.filter,limit:200});if(oe.length)Z(oe),ee=(oe[oe.length-1].timestamp??ee)+1}}}catch(ee){r(`Failed to read logs: ${ee.message}`),process.exitCode=1}})}async function Fl(N){let t=Bun.spawn(["docker",...N],{stdin:"inherit",stdout:"inherit",stderr:"pipe"}),[p,A]=await Promise.all([t.exited,new Response(t.stderr).text()]);return{ok:p===0,exitCode:p,command:["docker",...N].join(" "),stderr:A.trim()}}function ja(N){N.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 p=["build","--file",t.file??"Dockerfile","--tag",t.tag??"latest","."];if(t.dryRun){console.log(`docker ${p.join(" ")}`);return}let A=await Fl(p);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"}.`)}),N.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 p=await Fl(["push",t.image]);if(t.json)console.log(JSON.stringify(p));if(!p.ok)r(p.stderr),process.exitCode=p.exitCode;else n(`Pushed ${t.image}.`)}),N.command("container:deploy","Deploy a container image").action(async()=>Ar("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 Fa(N){N.command("domain:list","List all domains").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(t)=>{i("Domains");try{let p=pi(t?.provider),A=Xn(t?.provider)?.provider||"unknown",L=new o(`Fetching domains from ${A}...`);L.start();let j=await p.listDomains();if(L.succeed(`Found ${j.length} domain(s)`),j.length===0){e("No domains found in this provider");return}let G=j.map((Z)=>[Z,"Active","-",A.charAt(0).toUpperCase()+A.slice(1)]);c(["Domain","Status","SSL","Provider"],G)}catch(p){r(`Failed to list domains: ${p instanceof Error?p.message:String(p)}`)}}),N.command("domain:add <domain>","Add a new domain").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(t,p)=>{i(`Adding Domain: ${t}`);try{let A=pi(p?.provider),L=Xn(p?.provider)?.provider||"unknown",j=new o(`Checking if ${L} can manage ${t}...`);if(j.start(),await A.canManageDomain(t))j.succeed(`Domain ${t} is already available in ${L}`),e(`
320
+ Logs:`),e(Buffer.from(G.LogResult,"base64").toString("utf-8"))}catch(G){j.fail(`Invocation failed: ${G.message}`),process.exitCode=1}}),N.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 Ga(),L=new re(A),j=t.startsWith("/aws/lambda/")?t:`/aws/lambda/${t}`,G=Number(m?.since??15),Z=(ee)=>{for(let ne of ee){let oe=ne.timestamp?new Date(ne.timestamp).toISOString():"";e(`${oe} ${(ne.message??"").trimEnd()}`)}};try{let ee=Date.now()-G*60000,{events:ne=[]}=await L.filterLogEvents({logGroupName:j,startTime:ee,filterPattern:m?.filter,limit:200});if(Z(ne),ne.length)ee=(ne[ne.length-1].timestamp??ee)+1;if(m?.tail){e(`
321
+ (streaming — Ctrl+C to stop)`);for(;;){await new Promise((be)=>setTimeout(be,3000));let{events:oe=[]}=await L.filterLogEvents({logGroupName:j,startTime:ee,filterPattern:m?.filter,limit:200});if(oe.length)Z(oe),ee=(oe[oe.length-1].timestamp??ee)+1}}}catch(ee){r(`Failed to read logs: ${ee.message}`),process.exitCode=1}})}async function Hl(N){let t=Bun.spawn(["docker",...N],{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",...N].join(" "),stderr:A.trim()}}function Ka(N){N.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 Hl(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"}.`)}),N.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 Hl(["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}.`)}),N.command("container:deploy","Deploy a container image").action(async()=>Vr("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 Ja(N){N.command("domain:list","List all domains").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(t)=>{i("Domains");try{let m=vi(t?.provider),A=ir(t?.provider)?.provider||"unknown",L=new o(`Fetching domains from ${A}...`);L.start();let j=await m.listDomains();if(L.succeed(`Found ${j.length} domain(s)`),j.length===0){e("No domains found in this provider");return}let G=j.map((Z)=>[Z,"Active","-",A.charAt(0).toUpperCase()+A.slice(1)]);c(["Domain","Status","SSL","Provider"],G)}catch(m){r(`Failed to list domains: ${m instanceof Error?m.message:String(m)}`)}}),N.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=vi(m?.provider),L=ir(m?.provider)?.provider||"unknown",j=new o(`Checking if ${L} can manage ${t}...`);if(j.start(),await A.canManageDomain(t))j.succeed(`Domain ${t} is already available in ${L}`),e(`
322
322
  The domain is ready to use. You can now:`),e(` - Add DNS records: cloud dns:add ${t} A 192.168.1.1`),e(` - Generate SSL: cloud domain:ssl ${t}`);else if(j.warn(`Domain ${t} is not available in ${L}`),e(`
323
- To add this domain:`),L==="route53")e(" - Create a hosted zone in Route53 for this domain"),e(" - Update nameservers at your registrar to point to Route53");else e(` - Ensure the domain is registered with ${L}`),e(" - Enable API access for the domain in your provider dashboard")}catch(A){r(`Failed to check domain: ${A instanceof Error?A.message:String(A)}`)}}),N.command("domain:ssl <domain>","Generate SSL certificate via ACM with DNS validation").option("--provider <provider>","DNS provider for validation: porkbun, godaddy, or route53").option("--region <region>","AWS region for ACM (default: us-east-1 for CloudFront compatibility)").option("--wait","Wait for certificate validation to complete").action(async(t,p)=>{i(`Generating SSL Certificate for ${t}`);try{let A=Xn(p?.provider);if(!A)throw Error("No DNS provider configured");let L=p?.region||"us-east-1",j=A.provider;e(`DNS Provider: ${j}`),e(`ACM Region: ${L}`);let G=new $t(A,L),Z=new o("Requesting certificate and creating validation records...");Z.start();let ee=await G.findOrCreateCertificate({domainName:t,subjectAlternativeNames:[`*.${t}`],waitForValidation:p?.wait??!0,maxWaitMinutes:10});if(ee.isNew)Z.succeed("Certificate requested and validation records created");else Z.succeed("Found existing valid certificate");if(e(`Certificate ARN: ${ee.certificateArn}`),e(`Status: ${ee.status}`),ee.status==="issued")n(`
323
+ To add this domain:`),L==="route53")e(" - Create a hosted zone in Route53 for this domain"),e(" - Update nameservers at your registrar to point to Route53");else e(` - Ensure the domain is registered with ${L}`),e(" - Enable API access for the domain in your provider dashboard")}catch(A){r(`Failed to check domain: ${A instanceof Error?A.message:String(A)}`)}}),N.command("domain:ssl <domain>","Generate SSL certificate via ACM with DNS validation").option("--provider <provider>","DNS provider for validation: porkbun, godaddy, or route53").option("--region <region>","AWS region for ACM (default: us-east-1 for CloudFront compatibility)").option("--wait","Wait for certificate validation to complete").action(async(t,m)=>{i(`Generating SSL Certificate for ${t}`);try{let A=ir(m?.provider);if(!A)throw Error("No DNS provider configured");let L=m?.region||"us-east-1",j=A.provider;e(`DNS Provider: ${j}`),e(`ACM Region: ${L}`);let G=new $t(A,L),Z=new o("Requesting certificate and creating validation records...");Z.start();let ee=await G.findOrCreateCertificate({domainName:t,subjectAlternativeNames:[`*.${t}`],waitForValidation:m?.wait??!0,maxWaitMinutes:10});if(ee.isNew)Z.succeed("Certificate requested and validation records created");else Z.succeed("Found existing valid certificate");if(e(`Certificate ARN: ${ee.certificateArn}`),e(`Status: ${ee.status}`),ee.status==="issued")n(`
324
324
  SSL Certificate is ready!`),e(`
325
325
  You can now use this certificate with:`),e(" - CloudFront distributions"),e(" - Application Load Balancers"),e(" - API Gateway custom domains");else if(ee.status==="pending")e(`
326
326
  DNS validation records have been created.`),e("Certificate validation may take a few more minutes."),e(`
327
327
  Check status with: cloud domain:verify ${t}`);else r(`
328
- Certificate validation failed. Check ACM console for details.`)}catch(A){r(`Failed to generate SSL: ${A instanceof Error?A.message:String(A)}`)}}),N.command("domain:verify <domain>","Verify domain ownership and SSL status").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(t,p)=>{i(`Verifying Domain: ${t}`);try{let A=pi(p?.provider),L=Xn(p?.provider)?.provider||"unknown",j=new o("Checking domain ownership...");if(j.start(),!await A.canManageDomain(t)){j.fail("Domain not found in provider"),r(`Domain ${t} is not available in ${L}`);return}j.succeed("Domain ownership verified"),j.text="Fetching DNS records...",j.start();let ee=(await A.listRecords(t)).records||[];j.succeed(`Found ${ee.length} DNS record(s)`),j.text="Checking SSL certificate status...",j.start();let re=new q("us-east-1"),oe="Not found",be="";try{let Ee=(await re.listCertificates()).CertificateSummaryList.find((Se)=>Se.DomainName===t||Se.DomainName===`*.${t}`);if(Ee)be=Ee.CertificateArn||"",oe=(await re.describeCertificate({CertificateArn:be})).Status||"Unknown"}catch{}if(j.succeed("SSL check complete"),e(`
329
- Verification details:`),e(` - Provider: ${L}`),e(` - DNS records found: ${ee.length}`),e(" - Domain managed: Yes"),e(` - SSL certificate: ${oe}`),be)e(` - Certificate ARN: ${be}`);let we=new Map;for(let Ce of ee){let Ee=we.get(Ce.type)||0;we.set(Ce.type,Ee+1)}if(we.size>0){e(`
330
- Record summary:`);for(let[Ce,Ee]of we)e(` - ${Ce}: ${Ee}`)}}catch(A){r(`Failed to verify domain: ${A instanceof Error?A.message:String(A)}`)}}),N.command("dns:migrate <domain>","Migrate a whole zone from Route53 to Cloudflare").option("--account <id>","Cloudflare account id (defaults to CLOUDFLARE_ACCOUNT_ID)").option("--hosted-zone <id>","Route53 hosted zone id (auto-discovered when omitted)").option("--region <region>","AWS region for Route53",{default:"us-east-1"}).option("--apply","Write the records. Without it this is a dry run.").action(async(t,p)=>{i(`Zone migration: ${t} (Route53 → Cloudflare)`);let A=process.env.CLOUDFLARE_API_TOKEN;if(p?.apply&&!A){r("CLOUDFLARE_API_TOKEN is not set — required for --apply.");return}let L=p?.account??process.env.CLOUDFLARE_ACCOUNT_ID;try{let j=new M(p?.region||"us-east-1"),G=p?.hostedZone;if(!G){let Se=await j.findHostedZoneForDomain(t);if(!Se){r(`No Route53 hosted zone found for ${t}`);return}G=Se.Id.replace("/hostedzone/","")}let Z=new o(`Exporting ${t} from Route53...`);Z.start();let ee=await Xi(j,G);Z.succeed(`Exported ${ee.length} record set(s) from ${G}`);let re=Qi(ee,t);e(`Planned ${re.records.length} record(s), skipped ${re.skipped.length}`);for(let Se of re.skipped)e(` - ${Se.type} ${Se.name}: ${Se.reason}`);for(let Se of re.warnings)a(` ⚠ ${Se}`);for(let Se of re.records.filter((De)=>De.translatedFrom))e(` ~ ${Se.name} ${Se.translatedFrom} → CNAME ${Se.content}`);if(!p?.apply){e(""),e("Dry run — nothing was written. Re-run with --apply to migrate.");return}let oe=new Je(A,{accountId:L}),be=await oe.createZone(t,{accountId:L});n(be.created?`Created Cloudflare zone ${be.name} (${be.id})`:`Using existing Cloudflare zone ${be.name} (${be.id})`);let we=new o(`Importing ${re.records.length} record(s)...`);we.start();let Ce=await Yi(oe,re);we.succeed(`Imported ${Ce.applied.length} record(s)`);for(let Se of Ce.failed)r(` ✗ ${Se.record.type} ${Se.record.name}: ${Se.message}`);let Ee=await Zi(oe,re);if(Ee.ok)n(`Parity check passed — all ${Ee.matched.length} record(s) present at Cloudflare`);else{r(`Parity check FAILED — ${Ee.missing.length} record(s) missing or different:`);for(let Se of Ee.missing)r(` ✗ ${Se.record.type} ${Se.record.name} → ${Se.record.content}${Se.found?` (found: ${Se.found})`:""}`);a("Do NOT move the nameservers until this is clean.");return}e(""),e("Nameservers to set at the registrar:");for(let Se of be.nameServers)e(` ${Se}`);e(""),e(` cloud dns:nameservers ${t} --set ${be.nameServers.join(",")}`)}catch(j){r(`Migration failed: ${j instanceof Error?j.message:String(j)}`)}}),N.command("dns:nameservers <domain>","Show or set the nameservers a domain is delegated to").option("--registrar <name>","Registrar: godaddy or porkbun").option("--set <nameservers>","Comma-separated nameservers to delegate to").action(async(t,p)=>{i(`Nameservers for ${t}`);let A=(p?.registrar||Nm())?.toLowerCase();if(!A){r("No registrar credentials found. Set GODADDY_PAT (or GODADDY_API_KEY/GODADDY_API_SECRET) or PORKBUN_API_KEY/PORKBUN_SECRET_KEY.");return}let L=A==="porkbun"?new Ke(process.env.PORKBUN_API_KEY,process.env.PORKBUN_SECRET_KEY):new _t(process.env.GODADDY_PAT||process.env.GODADDY_API_KEY,process.env.GODADDY_PAT?"":process.env.GODADDY_API_SECRET);try{let j=await L.getNameServers(t);e(`Current (${A}):`);for(let ee of j)e(` ${ee}`);if(!p?.set)return;let G=p.set.split(",").map((ee)=>ee.trim()).filter(Boolean);if(G.length<2){r("At least two nameservers are required.");return}if(a(""),a("Changing nameservers moves authority for the ENTIRE zone — every"),a("record on it, including mail. Make sure the destination zone is"),a("already populated and parity-checked (cloud dns:migrate)."),await L.updateNameServers(t,G)){n(`Delegated ${t} to:`);for(let ee of G)n(` ${ee}`);e("Propagation typically takes minutes to a few hours.")}else r("The registrar rejected the nameserver update.")}catch(j){r(`Failed: ${j instanceof Error?j.message:String(j)}`)}}),N.command("dns:records <domain>","List DNS records for a domain").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").option("--type <type>","Filter by record type (A, AAAA, CNAME, TXT, MX, etc.)").action(async(t,p)=>{i(`DNS Records for ${t}`);try{let A=pi(p?.provider),L=Xn(p?.provider)?.provider||"unknown",j=new o(`Fetching records from ${L}...`);j.start();let Z=(await A.listRecords(t)).records||[];if(j.succeed(`Found ${Z.length} record(s)`),p?.type){let re=p.type.toUpperCase();Z=Z.filter((oe)=>oe.type.toUpperCase()===re),e(`Filtered to ${Z.length} ${re} record(s)`)}if(Z.length===0){e("No records found");return}let ee=Z.map((re)=>[re.type,re.name||"@",re.content.length>50?`${re.content.substring(0,47)}...`:re.content,String(re.ttl||300)]);c(["Type","Name","Value","TTL"],ee)}catch(A){r(`Failed to list records: ${A instanceof Error?A.message:String(A)}`)}}),N.command("dns:add <domain> <type> <value>","Add DNS record").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").option("--name <name>","Record name (subdomain)",{default:"@"}).option("--ttl <seconds>","Time to live in seconds",{default:"300"}).action(async(t,p,A,L)=>{i("Adding DNS Record");try{let j=pi(L?.provider),G=Xn(L?.provider)?.provider||"unknown",Z=L?.name||"@",ee=Number.parseInt(L?.ttl||"300",10),re=p.toUpperCase();e(`Provider: ${G}`),e(`Domain: ${t}`),e(`Type: ${re}`),e(`Name: ${Z}`),e(`Value: ${A}`),e(`TTL: ${ee}`);let oe=new o(`Adding record via ${G}...`);oe.start();let be=await j.createRecord(t,{type:re,name:Z==="@"?"":Z,content:A,ttl:ee});if(!be.success){oe.fail("Failed to add DNS record"),r(be.message||"DNS provider rejected the record");return}oe.succeed("DNS record added successfully"),n(`
328
+ Certificate validation failed. Check ACM console for details.`)}catch(A){r(`Failed to generate SSL: ${A instanceof Error?A.message:String(A)}`)}}),N.command("domain:verify <domain>","Verify domain ownership and SSL status").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(t,m)=>{i(`Verifying Domain: ${t}`);try{let A=vi(m?.provider),L=ir(m?.provider)?.provider||"unknown",j=new o("Checking domain ownership...");if(j.start(),!await A.canManageDomain(t)){j.fail("Domain not found in provider"),r(`Domain ${t} is not available in ${L}`);return}j.succeed("Domain ownership verified"),j.text="Fetching DNS records...",j.start();let ee=(await A.listRecords(t)).records||[];j.succeed(`Found ${ee.length} DNS record(s)`),j.text="Checking SSL certificate status...",j.start();let ne=new H("us-east-1"),oe="Not found",be="";try{let De=(await ne.listCertificates()).CertificateSummaryList.find((Se)=>Se.DomainName===t||Se.DomainName===`*.${t}`);if(De)be=De.CertificateArn||"",oe=(await ne.describeCertificate({CertificateArn:be})).Status||"Unknown"}catch{}if(j.succeed("SSL check complete"),e(`
329
+ Verification details:`),e(` - Provider: ${L}`),e(` - DNS records found: ${ee.length}`),e(" - Domain managed: Yes"),e(` - SSL certificate: ${oe}`),be)e(` - Certificate ARN: ${be}`);let we=new Map;for(let Ce of ee){let De=we.get(Ce.type)||0;we.set(Ce.type,De+1)}if(we.size>0){e(`
330
+ Record summary:`);for(let[Ce,De]of we)e(` - ${Ce}: ${De}`)}}catch(A){r(`Failed to verify domain: ${A instanceof Error?A.message:String(A)}`)}}),N.command("dns:migrate <domain>","Migrate a whole zone from Route53 to Cloudflare").option("--account <id>","Cloudflare account id (defaults to CLOUDFLARE_ACCOUNT_ID)").option("--hosted-zone <id>","Route53 hosted zone id (auto-discovered when omitted)").option("--region <region>","AWS region for Route53",{default:"us-east-1"}).option("--apply","Write the records. Without it this is a dry run.").action(async(t,m)=>{i(`Zone migration: ${t} (Route53 → Cloudflare)`);let A=process.env.CLOUDFLARE_API_TOKEN;if(m?.apply&&!A){r("CLOUDFLARE_API_TOKEN is not set — required for --apply.");return}let L=m?.account??process.env.CLOUDFLARE_ACCOUNT_ID;try{let j=new M(m?.region||"us-east-1"),G=m?.hostedZone;if(!G){let Se=await j.findHostedZoneForDomain(t);if(!Se){r(`No Route53 hosted zone found for ${t}`);return}G=Se.Id.replace("/hostedzone/","")}let Z=new o(`Exporting ${t} from Route53...`);Z.start();let ee=await Zi(j,G);Z.succeed(`Exported ${ee.length} record set(s) from ${G}`);let ne=eo(ee,t);e(`Planned ${ne.records.length} record(s), skipped ${ne.skipped.length}`);for(let Se of ne.skipped)e(` - ${Se.type} ${Se.name}: ${Se.reason}`);for(let Se of ne.warnings)a(` ⚠ ${Se}`);for(let Se of ne.records.filter((xe)=>xe.translatedFrom))e(` ~ ${Se.name} ${Se.translatedFrom} → CNAME ${Se.content}`);if(!m?.apply){e(""),e("Dry run — nothing was written. Re-run with --apply to migrate.");return}let oe=new Xe(A,{accountId:L}),be=await oe.createZone(t,{accountId:L});n(be.created?`Created Cloudflare zone ${be.name} (${be.id})`:`Using existing Cloudflare zone ${be.name} (${be.id})`);let we=new o(`Importing ${ne.records.length} record(s)...`);we.start();let Ce=await to(oe,ne);we.succeed(`Imported ${Ce.applied.length} record(s)`);for(let Se of Ce.failed)r(` ✗ ${Se.record.type} ${Se.record.name}: ${Se.message}`);let De=await ro(oe,ne);if(De.ok)n(`Parity check passed — all ${De.matched.length} record(s) present at Cloudflare`);else{r(`Parity check FAILED — ${De.missing.length} record(s) missing or different:`);for(let Se of De.missing)r(` ✗ ${Se.record.type} ${Se.record.name} → ${Se.record.content}${Se.found?` (found: ${Se.found})`:""}`);a("Do NOT move the nameservers until this is clean.");return}e(""),e("Nameservers to set at the registrar:");for(let Se of be.nameServers)e(` ${Se}`);e(""),e(` cloud dns:nameservers ${t} --set ${be.nameServers.join(",")}`)}catch(j){r(`Migration failed: ${j instanceof Error?j.message:String(j)}`)}}),N.command("dns:nameservers <domain>","Show or set the nameservers a domain is delegated to").option("--registrar <name>","Registrar: godaddy or porkbun").option("--set <nameservers>","Comma-separated nameservers to delegate to").action(async(t,m)=>{i(`Nameservers for ${t}`);let A=(m?.registrar||Wp())?.toLowerCase();if(!A){r("No registrar credentials found. Set GODADDY_PAT (or GODADDY_API_KEY/GODADDY_API_SECRET) or PORKBUN_API_KEY/PORKBUN_SECRET_KEY.");return}let L=A==="porkbun"?new Je(process.env.PORKBUN_API_KEY,process.env.PORKBUN_SECRET_KEY):new xt(process.env.GODADDY_PAT||process.env.GODADDY_API_KEY,process.env.GODADDY_PAT?"":process.env.GODADDY_API_SECRET);try{let j=await L.getNameServers(t);e(`Current (${A}):`);for(let ee of j)e(` ${ee}`);if(!m?.set)return;let G=m.set.split(",").map((ee)=>ee.trim()).filter(Boolean);if(G.length<2){r("At least two nameservers are required.");return}if(a(""),a("Changing nameservers moves authority for the ENTIRE zone — every"),a("record on it, including mail. Make sure the destination zone is"),a("already populated and parity-checked (cloud dns:migrate)."),await L.updateNameServers(t,G)){n(`Delegated ${t} to:`);for(let ee of G)n(` ${ee}`);e("Propagation typically takes minutes to a few hours.")}else r("The registrar rejected the nameserver update.")}catch(j){r(`Failed: ${j instanceof Error?j.message:String(j)}`)}}),N.command("dns:records <domain>","List DNS records for a domain").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").option("--type <type>","Filter by record type (A, AAAA, CNAME, TXT, MX, etc.)").action(async(t,m)=>{i(`DNS Records for ${t}`);try{let A=vi(m?.provider),L=ir(m?.provider)?.provider||"unknown",j=new o(`Fetching records from ${L}...`);j.start();let Z=(await A.listRecords(t)).records||[];if(j.succeed(`Found ${Z.length} record(s)`),m?.type){let ne=m.type.toUpperCase();Z=Z.filter((oe)=>oe.type.toUpperCase()===ne),e(`Filtered to ${Z.length} ${ne} record(s)`)}if(Z.length===0){e("No records found");return}let ee=Z.map((ne)=>[ne.type,ne.name||"@",ne.content.length>50?`${ne.content.substring(0,47)}...`:ne.content,String(ne.ttl||300)]);c(["Type","Name","Value","TTL"],ee)}catch(A){r(`Failed to list records: ${A instanceof Error?A.message:String(A)}`)}}),N.command("dns:add <domain> <type> <value>","Add DNS record").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").option("--name <name>","Record name (subdomain)",{default:"@"}).option("--ttl <seconds>","Time to live in seconds",{default:"300"}).action(async(t,m,A,L)=>{i("Adding DNS Record");try{let j=vi(L?.provider),G=ir(L?.provider)?.provider||"unknown",Z=L?.name||"@",ee=Number.parseInt(L?.ttl||"300",10),ne=m.toUpperCase();e(`Provider: ${G}`),e(`Domain: ${t}`),e(`Type: ${ne}`),e(`Name: ${Z}`),e(`Value: ${A}`),e(`TTL: ${ee}`);let oe=new o(`Adding record via ${G}...`);oe.start();let be=await j.createRecord(t,{type:ne,name:Z==="@"?"":Z,content:A,ttl:ee});if(!be.success){oe.fail("Failed to add DNS record"),r(be.message||"DNS provider rejected the record");return}oe.succeed("DNS record added successfully"),n(`
331
331
  Record created!`),e(`
332
- Note: DNS changes may take a few minutes to propagate`)}catch(j){r(`Failed to add record: ${j instanceof Error?j.message:String(j)}`)}}),N.command("dns:delete <domain> <type>","Delete DNS record").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").option("--name <name>","Record name (subdomain)",{default:"@"}).option("--value <value>","Record value (required for multi-value records)").action(async(t,p,A)=>{i("Deleting DNS Record");try{let L=pi(A?.provider),j=Xn(A?.provider)?.provider||"unknown",G=A?.name||"@",Z=p.toUpperCase();e(`Provider: ${j}`),e(`Domain: ${t}`),e(`Type: ${Z}`),e(`Name: ${G}`);let ee=new o("Finding record...");ee.start();let be=((await L.listRecords(t)).records||[]).filter((Ee)=>Ee.type.toUpperCase()===Z&&(Ee.name===G||Ee.name===""&&G==="@"));if(be.length===0){ee.fail("No matching record found");return}if(be.length>1&&!A?.value){ee.warn("Multiple records found"),e(`
333
- Please specify --value to identify which record to delete:`);for(let Ee of be)e(` - ${Ee.content}`);return}let we=A?.value?be.find((Ee)=>Ee.content===A.value)||be[0]:be[0];if(e(`Value: ${we.content}`),!await d("Delete this record?",!1)){e("Deletion cancelled");return}ee.text=`Deleting record via ${j}...`,ee.start(),await L.deleteRecord(t,we),ee.succeed("DNS record deleted successfully"),n(`
334
- Record deleted!`)}catch(L){r(`Failed to delete record: ${L instanceof Error?L.message:String(L)}`)}})}function Nm(){if(process.env.GODADDY_PAT)return"godaddy";if(process.env.GODADDY_API_KEY&&process.env.GODADDY_API_SECRET)return"godaddy";if(process.env.PORKBUN_API_KEY&&process.env.PORKBUN_SECRET_KEY)return"porkbun";return null}var Bl=["postgres","mysql","mariadb","redis","mongodb","libsql"],Ml=["aws_rds","aws_aurora","aws_elasticache","server","container","external"],_l=["observe","connect","backup","restore","restart","resize","version","rotate","expose","delete","logs","slow_queries","users","databases"];async function Om(N,t){let p=await Me(),A=y(process.cwd(),p),L=new Wt(A.store),j=!N&&t?L.get(t)??L.list(A.project.id).find((Se)=>Se.name===t):void 0,G=j?.environmentId?A.store.listEnvironments(A.project.id).find((Se)=>Se.id===j.environmentId)?.slug:void 0,Z=N??G??Object.keys(p.environments??{})[0]??"production",ee=A.environments.get(Z);if(!ee)throw A.store.close(),Error(`Environment ${Z} was not found.`);let re=new b(A.store,{workerId:`cli:${process.pid}`}),oe=new Gt(A.store,k(process.cwd())),be=new Rr(L,re,oe),we=A.store.getActorByExternalId("system","cli")??A.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"}),Ce=p.project?.region??"us-east-1",Ee={aws_rds:new Tn(new In(new ue(Ce))),aws_aurora:new En(new Rn(new ue(Ce))),aws_elasticache:new wn(new Pn(new te(Ce))),server:new Cn(new rt),container:new An(new rt)};return{config:p,controlPlane:A,environmentRecord:ee,env:Z,store:L,queue:re,secrets:oe,lifecycle:be,actor:we,resolveAdapter:(Se)=>Ee[Se.provider]}}async function Xr(N,t,p){let A=await Om(N,p);try{return await t(A)}finally{A.controlPlane.store.close()}}async function jr(N){try{await N()}catch(t){r(t instanceof Error?t.message:String(t))}}function Ba(N){if(!Bl.includes(N))throw Error(`--engine must be ${Bl.join(", ")}.`);return N}function Ma(N){if(!Ml.includes(N))throw Error(`--provider must be ${Ml.join(", ")}.`);return N}function co(N,t){let p=N.store.list(N.controlPlane.project.id,N.environmentRecord.id),A=N.store.get(t)??p.find((L)=>L.name===t);if(!A||A.environmentId!==N.environmentRecord.id)throw Error(`Data service ${t} was not found in ${N.env}.`);return A}function zl(N){return N.map((t)=>[t.id,t.name,t.engine,t.provider,t.status,t.plan,t.publicExposure?"public allowlist":"private",t.managementEnabled?"managed":"read-only"])}function Lm(N,t){return N!==null&&typeof N==="object"&&!Array.isArray(N)&&N.serviceId===t}function jm(N){let t={};if(N.plan)t.plan=N.plan;if(N.storage!=null)t.storageGb=Number(N.storage);if(N.engineVersion)t.engineVersion=N.engineVersion;if(N.retention)t.retention=N.retention;if(N.confirm)t.confirm=N.confirm;if(N.backupId)t.backupId=N.backupId;if(N.compatibilityReviewed)t.compatibilityReviewed=!0;if(N.minCapacity!=null)t.minCapacity=Number(N.minCapacity);if(N.maxCapacity!=null)t.maxCapacity=Number(N.maxCapacity);if(N.public!=null)t.publicExposure=N.public;if(N.cidrs)t.allowedCidrs=N.cidrs.split(",").map((p)=>p.trim()).filter(Boolean);return t}function Ul(N){let t=typeof N.observedState.endpoint==="string"?N.observedState.endpoint:N.placement,p=Number(N.observedState.port)||(N.engine==="postgres"?5432:N.engine==="redis"?6379:N.engine==="mongodb"?27017:N.engine==="libsql"?8080:3306),A=typeof N.observedState.database==="string"?N.observedState.database:void 0;return[{type:N.publicExposure?"external":"internal",host:t,port:p,database:A,tls:typeof N.observedState.tls==="boolean"?N.observedState.tls:N.provider.startsWith("aws_")},{type:"tunnel",host:t,port:p,database:A,tls:typeof N.observedState.tls==="boolean"?N.observedState.tls:N.provider.startsWith("aws_")}]}function Vl(N){N.option("--env <environment>","Target environment").option("--engine <engine>","postgres, mysql, mariadb, redis, mongodb, or libsql",{default:"postgres"}).option("--provider <provider>","aws_rds, aws_aurora, aws_elasticache, server, container, or external",{default:"aws_rds"}).option("--placement <id>","Provider identifier or runtime placement").option("--plan <plan>","Provider plan or instance type",{default:"db.t4g.micro"}).option("--engine-version <version>","Engine version").option("--storage <gb>","Allocated storage in GB",{default:"20"}).option("--username <name>","Generated credential username",{default:"app"}).option("--database <name>","Initial logical database").option("--subnet-group <name>","Private subnet group").option("--security-groups <ids>","Comma-separated security group IDs").option("--high-availability","Enable provider high availability").option("--public","Request a public endpoint; requires narrow --cidrs").option("--cidrs <ranges>","Comma-separated narrow public CIDRs")}function _a(N){N.command("data:list","List first-class databases and caches").option("--env <environment>","Target environment").option("--json","Print structured JSON").action((A)=>jr(()=>Xr(A.env,async(L)=>{let j=L.store.list(L.controlPlane.project.id,L.environmentRecord.id);if(A.json)e(JSON.stringify(j,null,2));else c(["ID","Name","Engine","Provider","Status","Plan","Exposure","Management"],zl(j))}))),N.command("data:capabilities <engine> <provider>","Explain lifecycle support before taking action").action((A,L)=>jr(async()=>{e(JSON.stringify(Ht(Ba(A),Ma(L)),null,2))}));let t=N.command("data:create <name>","Provision an encrypted, private-by-default data service");Vl(t),t.action((A,L)=>jr(()=>Xr(L.env,async(j)=>{let G=Ba(L.engine),Z=Ma(L.provider),ee=j.resolveAdapter({provider:Z});if(!ee||!ee.engines.includes(G))throw Error(`No ${Z}/${G} provisioning adapter is configured. Use data:adopt for read-only inventory.`);let re={...L.database?{database:L.database}:{},...L.subnetGroup?{subnetGroup:L.subnetGroup}:{},...L.securityGroups?{securityGroupIds:L.securityGroups.split(",").map((be)=>be.trim()).filter(Boolean)}:{}},oe=await j.lifecycle.create({organizationId:j.controlPlane.organization.id,projectId:j.controlPlane.project.id,environmentId:j.environmentRecord.id,name:A,engine:G,provider:Z,placement:L.placement??A,engineVersion:L.engineVersion,plan:L.plan??"db.t4g.micro",storageGb:Number(L.storage)||void 0,highAvailability:L.highAvailability===!0,publicExposure:L.public===!0,allowedCidrs:L.cidrs?L.cidrs.split(",").map((be)=>be.trim()).filter(Boolean):[],desiredState:re,observedState:{},origin:"managed",managementEnabled:!0,ownerActorId:j.actor.id,username:L.username});if(n(`Queued ${oe.service.name} as operation ${oe.operationId}.`),oe.credential)hr("One-time credential — copy it now:"),e(`Username: ${oe.credential.username}`),e(`Password: ${oe.credential.password}`),e(`Reference: ${oe.credential.secretRef}`)})));let p=N.command("data:adopt <name>","Adopt an existing provider resource as read-only inventory");Vl(p),p.action((A,L)=>jr(()=>Xr(L.env,async(j)=>{let G=j.store.create({organizationId:j.controlPlane.organization.id,projectId:j.controlPlane.project.id,environmentId:j.environmentRecord.id,name:A,engine:Ba(L.engine),provider:Ma(L.provider),placement:L.placement??A,engineVersion:L.engineVersion,plan:L.plan??"external",storageGb:Number(L.storage)||void 0,highAvailability:L.highAvailability===!0,publicExposure:L.public===!0,allowedCidrs:L.cidrs?L.cidrs.split(",").map((Z)=>Z.trim()).filter(Boolean):[],desiredState:{},observedState:{},credentialRef:void 0,status:"adopted",origin:"adopted",managementEnabled:!1,ownerActorId:j.actor.id});n(`Adopted ${G.name} (${G.id}) read-only.`)}))),N.command("data:show <service>","Show safe service, capability, dependency, and operation metadata").option("--env <environment>","Target environment").option("--reveal","Reveal the encrypted credential with exact confirmation").option("--confirm <name>","Exact service name required with --reveal").action((A,L)=>jr(()=>Xr(L.env,async(j)=>{let G=co(j,A),Z=j.store.credential(G.id),ee={...G,credentialRef:void 0,credential:Z?{configured:!0,username:Z.username,version:Z.version,rotatedAt:Z.rotatedAt}:void 0,dependencies:j.store.dependencies(G.id).map((re)=>({resourceId:re.resourceId,requiresRedeploy:re.requiresRedeploy})),operations:j.queue.list({projectId:G.projectId,limit:250}).filter((re)=>Lm(re.operation.input,G.id))};if(e(JSON.stringify(ee,null,2)),L.reveal){if(L.confirm!==G.name)throw Error(`Credential reveal requires --confirm ${G.name}.`);if(!Z)throw Error("No managed credential is configured.");hr("Credential value follows; it will not be logged or persisted by ts-cloud:"),e(`Username: ${Z.username}`),e(`Password: ${await j.secrets.resolve(Z.secretRef)}`)}},A))),N.command("data:connect <service>","Print secret-free connection and tunnel guidance").option("--env <environment>","Target environment").action((A,L)=>jr(()=>Xr(L.env,async(j)=>{let G=co(j,A);c(["Type","Command","Credential reference"],vn(G,Ul(G)).map((Z)=>[Z.type,Z.command,Z.secretRef??"—"]))},A))),N.command("data:action <service> <action>","Preview or enqueue a durable lifecycle action").option("--env <environment>","Target environment").option("--execute","Enqueue after validation; default is preview only").option("--plan <plan>","New provider plan").option("--storage <gb>","New storage allocation").option("--engine-version <version>","Target engine version").option("--min-capacity <acu>","Aurora minimum capacity").option("--max-capacity <acu>","Aurora maximum capacity").option("--public","Enable public exposure").option("--cidrs <ranges>","Comma-separated narrow public CIDRs").option("--retention <choice>","final_backup or retain for deletion").option("--confirm <name>","Exact service name for destructive actions").option("--backup-id <id>","Required backup for a major version change").option("--compatibility-reviewed","Confirm major-version compatibility review").action((A,L,j)=>jr(()=>Xr(j.env,async(G)=>{if(!_l.includes(L))throw Error(`Action must be ${_l.join(", ")}.`);let Z=co(G,A),ee=L,re=jm(j),oe=G.lifecycle.plan(Z,ee,re);if(e(JSON.stringify({...oe,productionExecutionCreated:!1},null,2)),!j.execute)return;let be=G.lifecycle.enqueue(Z,ee,re,G.actor.id);n(`Queued ${ee} as ${be}.`)},A))),N.command("data:management <service>","Enable reviewed management of an adopted service").option("--env <environment>","Target environment").option("--confirm <name>","Exact service name").action((A,L)=>jr(()=>Xr(L.env,async(j)=>{let G=co(j,A);if(G.origin!=="adopted"||L.confirm!==G.name)throw Error(`Enabling adopted management requires --confirm ${G.name}.`);let Z=j.store.update(G.id,G.version,{managementEnabled:!0});n(`${Z.name} management is enabled.`)},A))),N.command("data:dependency <service> <resource>","Register or remove an application credential dependency").option("--env <environment>","Target environment").option("--dynamic","Dependency consumes a dynamic reference and does not need redeploy").option("--remove","Remove the dependency").action((A,L,j)=>jr(()=>Xr(j.env,async(G)=>{let Z=co(G,A),ee=G.controlPlane.store.listResources(G.controlPlane.project.id,G.environmentRecord.id).find((re)=>re.id===L||re.slug===L);if(!ee)throw Error(`Resource ${L} was not found in ${G.env}.`);if(j.remove){e(G.store.removeDependency(Z.id,ee.id)?"Dependency removed.":"Dependency was not registered.");return}G.store.addDependency({serviceId:Z.id,resourceId:ee.id,secretRef:Z.credentialRef??`secret://data-services/${Z.projectId}/${Z.name}`,requiresRedeploy:!j.dynamic}),n(`Registered dependency ${ee.slug} -> ${Z.name}.`)},A))),N.command("db:list","Alias for data:list").option("--env <environment>","Target environment").action((A)=>jr(()=>Xr(A.env,async(L)=>c(["ID","Name","Engine","Provider","Status","Plan","Exposure","Management"],zl(L.store.list(L.controlPlane.project.id,L.environmentRecord.id)))))),N.command("db:connect <service>","Alias for data:connect").option("--env <environment>","Target environment").action((A,L)=>jr(()=>Xr(L.env,async(j)=>{let G=co(j,A);c(["Type","Command","Credential reference"],vn(G,Ul(G)).map((Z)=>[Z.type,Z.command,Z.secretRef??"—"]))},A)));for(let A of["backup","snapshot"])N.command(`db:${A} <service>`,`Alias for data:action ${A==="backup"?"backup":"backup"}`).option("--env <environment>","Target environment").option("--execute","Enqueue the backup; default previews").action((L,j)=>jr(()=>Xr(j.env,async(G)=>{let Z=co(G,L),ee=G.lifecycle.plan(Z,"backup");if(e(JSON.stringify({...ee,productionExecutionCreated:!1},null,2)),j.execute)n(`Queued backup as ${G.lifecycle.enqueue(Z,"backup",{},G.actor.id)}.`)},L)))}function Wl(N,t=15){if(!N)return t*60000;let p=/^(\d+)\s*([smhd])?$/.exec(N.trim());if(!p)return t*60000;let A=Number(p[1]),L=p[2]??"m";return A*({s:1000,m:60000,h:3600000,d:86400000}[L]??60000)}function Fm(N,t){for(let p of N){let A=p.timestamp?new Date(p.timestamp).toISOString():"",L=t?`[${t}] `:"";e(`${A} ${L}${(p.message??"").trimEnd()}`)}}function Ua(N){N.command("logs","Stream serverless application logs (http + queue + cli)").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).option("--function <which>","Which function: http | queue | cli | all",{default:"all"}).option("--tail","Continuously stream new log events").option("--filter <pattern>","CloudWatch filter pattern").option("--since <duration>","Look back (e.g. 1h, 30m, 15)",{default:"15m"}).action(async(t)=>{try{let p=await Me(),A=t?.env||"production",{region:L,functions:j}=yt(p,A),G=new ne(L),Z=(t?.function??"all").toLowerCase();if(Z!=="all"&&!["http","queue","cli"].includes(Z)){r("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let re=(Z==="all"?["http","queue","cli"]:[Z]).map((we)=>({mode:we,group:`/aws/lambda/${j[we]}`}));i(`Logs — ${p.project.slug} (${A})`);let oe=Date.now()-Wl(t?.since),be=async()=>{let we=oe;for(let{mode:Ce,group:Ee}of re)try{let{events:Se=[]}=await G.filterLogEvents({logGroupName:Ee,startTime:oe,filterPattern:t?.filter,limit:200});Fm(Se,re.length>1?Ce:void 0);for(let De of Se)if((De.timestamp??0)>we)we=De.timestamp??we}catch(Se){if(!/ResourceNotFound/i.test(String(Se?.message)))throw Se}oe=we+1};if(await be(),t?.tail){e(`
335
- (streaming — Ctrl+C to stop)`);for(;;)await new Promise((we)=>setTimeout(we,3000)),await be()}}catch(p){r(`Failed to read logs: ${p.message}`),process.exitCode=1}}),N.command("metrics","Show serverless function metrics (invocations, errors, duration, throttles)").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).option("--since <duration>","Window (e.g. 1h, 24h)",{default:"1h"}).action(async(t)=>{try{let p=await Me(),A=t?.env||"production",{region:L,functions:j}=yt(p,A),G=new Q(L),Z=Wl(t?.since,60),ee=new Date,re=new Date(ee.getTime()-Z),oe=Math.max(60,Math.round(Z/1000));i(`Metrics — ${p.project.slug} (${A}), last ${t?.since??"1h"}`);let be=[];for(let we of["http","queue","cli"]){let Ce=[{Name:"FunctionName",Value:j[we]}],Ee=async(Xe,Ge)=>{let Ye=await G.getMetricStatistics({Namespace:"AWS/Lambda",MetricName:Xe,Dimensions:Ce,StartTime:re,EndTime:ee,Period:oe,Statistics:[Ge]});if(!Ye.length)return 0;if(Ge==="Sum")return Ye.reduce((et,Pt)=>et+(Pt.Sum??0),0);if(Ge==="Maximum")return Math.max(...Ye.map((et)=>et.Maximum??0));return Ye.reduce((et,Pt)=>et+(Pt.Average??0),0)/Ye.length},[Se,De,_e,Ae,Te]=await Promise.all([Ee("Invocations","Sum"),Ee("Errors","Sum"),Ee("Throttles","Sum"),Ee("Duration","Average"),Ee("Duration","Maximum")]),Ne=Se?`${(De/Se*100).toFixed(2)}%`:"—";be.push([we,String(Se),String(De),Ne,String(_e),`${Ae.toFixed(0)}ms`,`${Te.toFixed(0)}ms`])}c(["Function","Invocations","Errors","Error rate","Throttles","Avg","Max"],be)}catch(p){r(`Failed to read metrics: ${p.message}`),process.exitCode=1}}),N.command("metrics:dashboard","Print the CloudWatch console URL for this app").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).action(async(t)=>{try{let p=await Me(),A=t?.env||"production",{region:L,functions:j}=yt(p,A),G=encodeURIComponent(JSON.stringify({metrics:["http","queue","cli"].flatMap((Z)=>[["AWS/Lambda","Invocations","FunctionName",j[Z]],["AWS/Lambda","Errors","FunctionName",j[Z]]]),view:"timeSeries",region:L}));i("CloudWatch dashboard"),e(`https://console.aws.amazon.com/cloudwatch/home?region=${L}#metricsV2:graph=${G}`)}catch(p){r(p.message),process.exitCode=1}}),N.command("alarms","List CloudWatch alarms").option("--prefix <prefix>","Filter by alarm name prefix").option("--region <region>","AWS region").action(async(t)=>{i("CloudWatch Alarms");let p=t?.region;if(!p)try{p=(await Me()).project.region}catch{}try{let L=await new Q(p||process.env.AWS_REGION||"us-east-1").describeAlarms({AlarmNamePrefix:t?.prefix,MaxRecords:100});if(!L.length){e("No alarms found");return}c(["Name","State","Metric","Condition"],L.map((j)=>[j.AlarmName??"-",j.StateValue??"-",j.MetricName??"-",`${j.ComparisonOperator??""} ${j.Threshold??""}`.trim()]))}catch(A){r(`Failed to list alarms: ${A.message}`),process.exitCode=1}}),N.command("alarms:create","Create a CloudWatch alarm on a Lambda metric").option("--name <name>","Alarm name").option("--env <environment>","Environment",{default:"production"}).option("--function <which>","Function: http | queue | cli",{default:"http"}).option("--metric <metric>","Lambda metric (Errors, Throttles, Duration, Invocations)",{default:"Errors"}).option("--threshold <value>","Threshold value").option("--comparison <op>","GreaterThanThreshold | GreaterThanOrEqualToThreshold | LessThanThreshold",{default:"GreaterThanThreshold"}).option("--statistic <stat>","Sum | Average | Maximum | Minimum",{default:"Sum"}).action(async(t)=>{if(i("Creating CloudWatch Alarm"),!t?.name||t?.threshold==null){r("Missing required options: --name and --threshold"),process.exitCode=1;return}try{let p=await Me(),A=t.env||"production",{region:L,functions:j}=yt(p,A),G=t.function??"http";await new Q(L).putMetricAlarm({AlarmName:t.name,Namespace:"AWS/Lambda",MetricName:t.metric??"Errors",ComparisonOperator:t.comparison??"GreaterThanThreshold",Threshold:Number(t.threshold),EvaluationPeriods:1,Period:300,Statistic:t.statistic??"Sum",Dimensions:[{Name:"FunctionName",Value:j[G]}],AlarmDescription:`ts-cloud: ${j[G]} ${t.metric} ${t.comparison} ${t.threshold}`}),n(`Alarm '${t.name}' created on ${j[G]} ${t.metric}`)}catch(p){r(`Failed to create alarm: ${p.message}`),process.exitCode=1}})}async function Rs(N){let t=await Me(),p=N||"production";return{region:t.environments?.[p]?.region||t.project.region||"us-east-1",prefix:`${t.project.slug}/${p}`}}function Wa(N){N.command("secrets:list","List secrets for an environment").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{i("Secrets");try{let{region:p,prefix:A}=await Rs(t?.env),L=new W(p),{SecretList:j=[]}=await L.listSecrets({Filters:[{Key:"name",Values:[`${A}/`]}],MaxResults:100});if(!j.length){e(`No secrets found under ${A}/`);return}c(["Key","Last Changed"],j.map((G)=>[(G.Name??"").replace(`${A}/`,""),G.LastChangedDate??G.LastAccessedDate??"-"]))}catch(p){r(`Failed to list secrets: ${p.message}`),process.exitCode=1}}),N.command("secrets:set <key> <value>","Set (create or update) a secret").option("--env <environment>","Environment (production, staging, development)").action(async(t,p,A)=>{i(`Setting secret ${t}`);let{region:L,prefix:j}=await Rs(A?.env),G=new W(L),Z=`${j}/${t}`,ee=new o("Storing secret...");ee.start();try{try{await G.putSecretValue({SecretId:Z,SecretString:p})}catch{await G.createSecret({Name:Z,SecretString:p})}ee.succeed(`Secret ${t} stored (${Z})`),a("Redeploy for the new value to take effect in running functions.")}catch(re){ee.fail(`Failed to store secret: ${re.message}`),process.exitCode=1}}),N.command("secrets:get <key>","Reveal a secret value").option("--env <environment>","Environment (production, staging, development)").action(async(t,p)=>{try{let{region:A,prefix:L}=await Rs(p?.env),G=await new W(A).getSecretValue({SecretId:`${L}/${t}`});e(G.SecretString??"")}catch(A){r(`Failed to read secret: ${A.message}`),process.exitCode=1}}),N.command("secrets:delete <key>","Delete a secret").option("--env <environment>","Environment (production, staging, development)").option("--force","Delete immediately without a recovery window").action(async(t,p)=>{try{let{region:A,prefix:L}=await Rs(p?.env);await new W(A).deleteSecret({SecretId:`${L}/${t}`,ForceDeleteWithoutRecovery:p?.force}),n(`Secret ${t} deleted`)}catch(A){r(`Failed to delete secret: ${A.message}`),process.exitCode=1}})}function qa(N){for(let[t,p]of[["firewall:rules","List provider firewall policy"],["firewall:block <ip>","Block an address"],["firewall:unblock <ip>","Unblock an address"],["firewall:countries","Manage geographic policy"]])N.command(t,p).action(async(...A)=>Ar(t.split(" ")[0],{target:String(A[0]??""),message:"No reviewed WAF/IP-set adapter is configured for this legacy command.",nextAction:"Use server firewall configuration or install a provider adapter; no rule was changed."}))}function Ha(N){N.command("ssl:list","List all SSL certificates").option("--region <region>","AWS region (default: us-east-1)").action(async(t)=>{i("SSL Certificates");let p=t?.region||"us-east-1",A=new o("Fetching certificates from ACM...");A.start();try{let L=new q(p),j=await L.listCertificates();if(j.CertificateSummaryList.length===0){A.succeed("No certificates found"),e(`
336
- No SSL certificates found in region ${p}`),e("Use 'cloud domain:ssl <domain>' to request a new certificate");return}let G=await Promise.all(j.CertificateSummaryList.map(async(re)=>await L.describeCertificate({CertificateArn:re.CertificateArn})));A.succeed(`Found ${G.length} certificate(s)`);let Z=(re)=>{if(!re)return"N/A";let oe=typeof re==="string"?Number.parseFloat(re):re,be=oe<1000000000000?oe*1000:oe;return new Date(be).toISOString().split("T")[0]},ee=G.map((re)=>{let oe=Z(re.NotAfter),be=re.Type==="AMAZON_ISSUED"?"Amazon Issued":re.Type||"Unknown",we=re.Status==="ISSUED"?"Available":re.Status||"Unknown";return[re.DomainName,re.Status||"Unknown",oe,be,we]});c(["Domain","Status","Expiry","Type","State"],ee),e(`
337
- ACM certificates are automatically renewed by AWS`),e(`Region: ${p}`)}catch(L){A.fail("Failed to fetch certificates"),r(L.message)}}),N.command("ssl:renew <domain>","Renew SSL certificate").option("--region <region>","AWS region (default: us-east-1)").action(async(t,p)=>{i(`Checking SSL Certificate for ${t}`);let A=p?.region||"us-east-1";e(`Domain: ${t}`),e(`Region: ${A}`);let L=new o("Checking certificate status...");L.start();try{let G=await new q(A).findCertificateByDomain(t);if(!G){L.fail("Certificate not found"),r(`No certificate found for domain: ${t}`),e("Use 'cloud domain:ssl <domain>' to request a new certificate");return}L.succeed("Certificate found"),e(`
332
+ Note: DNS changes may take a few minutes to propagate`)}catch(j){r(`Failed to add record: ${j instanceof Error?j.message:String(j)}`)}}),N.command("dns:delete <domain> <type>","Delete DNS record").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").option("--name <name>","Record name (subdomain)",{default:"@"}).option("--value <value>","Record value (required for multi-value records)").action(async(t,m,A)=>{i("Deleting DNS Record");try{let L=vi(A?.provider),j=ir(A?.provider)?.provider||"unknown",G=A?.name||"@",Z=m.toUpperCase();e(`Provider: ${j}`),e(`Domain: ${t}`),e(`Type: ${Z}`),e(`Name: ${G}`);let ee=new o("Finding record...");ee.start();let be=((await L.listRecords(t)).records||[]).filter((De)=>De.type.toUpperCase()===Z&&(De.name===G||De.name===""&&G==="@"));if(be.length===0){ee.fail("No matching record found");return}if(be.length>1&&!A?.value){ee.warn("Multiple records found"),e(`
333
+ Please specify --value to identify which record to delete:`);for(let De of be)e(` - ${De.content}`);return}let we=A?.value?be.find((De)=>De.content===A.value)||be[0]:be[0];if(e(`Value: ${we.content}`),!await d("Delete this record?",!1)){e("Deletion cancelled");return}ee.text=`Deleting record via ${j}...`,ee.start(),await L.deleteRecord(t,we),ee.succeed("DNS record deleted successfully"),n(`
334
+ Record deleted!`)}catch(L){r(`Failed to delete record: ${L instanceof Error?L.message:String(L)}`)}})}function Wp(){if(process.env.GODADDY_PAT)return"godaddy";if(process.env.GODADDY_API_KEY&&process.env.GODADDY_API_SECRET)return"godaddy";if(process.env.PORKBUN_API_KEY&&process.env.PORKBUN_SECRET_KEY)return"porkbun";return null}var Kl=["postgres","mysql","mariadb","redis","mongodb","libsql"],Jl=["aws_rds","aws_aurora","aws_elasticache","server","container","external"],Yl=["observe","connect","backup","restore","restart","resize","version","rotate","expose","delete","logs","slow_queries","users","databases"];async function qp(N,t){let m=await Ne(),A=y(process.cwd(),m),L=new Gt(A.store),j=!N&&t?L.get(t)??L.list(A.project.id).find((Se)=>Se.name===t):void 0,G=j?.environmentId?A.store.listEnvironments(A.project.id).find((Se)=>Se.id===j.environmentId)?.slug:void 0,Z=N??G??Object.keys(m.environments??{})[0]??"production",ee=A.environments.get(Z);if(!ee)throw A.store.close(),Error(`Environment ${Z} was not found.`);let ne=new b(A.store,{workerId:`cli:${process.pid}`}),oe=new Kt(A.store,k(process.cwd())),be=new Pr(L,ne,oe),we=A.store.getActorByExternalId("system","cli")??A.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"}),Ce=m.project?.region??"us-east-1",De={aws_rds:new En(new Rn(new ue(Ce))),aws_aurora:new wn(new Pn(new ue(Ce))),aws_elasticache:new Cn(new Nn(new te(Ce))),server:new An(new rt),container:new In(new rt)};return{config:m,controlPlane:A,environmentRecord:ee,env:Z,store:L,queue:ne,secrets:oe,lifecycle:be,actor:we,resolveAdapter:(Se)=>De[Se.provider]}}async function oi(N,t,m){let A=await qp(N,m);try{return await t(A)}finally{A.controlPlane.store.close()}}async function Kr(N){try{await N()}catch(t){r(t instanceof Error?t.message:String(t))}}function Ya(N){if(!Kl.includes(N))throw Error(`--engine must be ${Kl.join(", ")}.`);return N}function Za(N){if(!Jl.includes(N))throw Error(`--provider must be ${Jl.join(", ")}.`);return N}function go(N,t){let m=N.store.list(N.controlPlane.project.id,N.environmentRecord.id),A=N.store.get(t)??m.find((L)=>L.name===t);if(!A||A.environmentId!==N.environmentRecord.id)throw Error(`Data service ${t} was not found in ${N.env}.`);return A}function Zl(N){return N.map((t)=>[t.id,t.name,t.engine,t.provider,t.status,t.plan,t.publicExposure?"public allowlist":"private",t.managementEnabled?"managed":"read-only"])}function Gp(N,t){return N!==null&&typeof N==="object"&&!Array.isArray(N)&&N.serviceId===t}function Hp(N){let t={};if(N.plan)t.plan=N.plan;if(N.storage!=null)t.storageGb=Number(N.storage);if(N.engineVersion)t.engineVersion=N.engineVersion;if(N.retention)t.retention=N.retention;if(N.confirm)t.confirm=N.confirm;if(N.backupId)t.backupId=N.backupId;if(N.compatibilityReviewed)t.compatibilityReviewed=!0;if(N.minCapacity!=null)t.minCapacity=Number(N.minCapacity);if(N.maxCapacity!=null)t.maxCapacity=Number(N.maxCapacity);if(N.public!=null)t.publicExposure=N.public;if(N.cidrs)t.allowedCidrs=N.cidrs.split(",").map((m)=>m.trim()).filter(Boolean);return t}function Ql(N){let t=typeof N.observedState.endpoint==="string"?N.observedState.endpoint:N.placement,m=Number(N.observedState.port)||(N.engine==="postgres"?5432:N.engine==="redis"?6379:N.engine==="mongodb"?27017:N.engine==="libsql"?8080:3306),A=typeof N.observedState.database==="string"?N.observedState.database:void 0;return[{type:N.publicExposure?"external":"internal",host:t,port:m,database:A,tls:typeof N.observedState.tls==="boolean"?N.observedState.tls:N.provider.startsWith("aws_")},{type:"tunnel",host:t,port:m,database:A,tls:typeof N.observedState.tls==="boolean"?N.observedState.tls:N.provider.startsWith("aws_")}]}function Xl(N){N.option("--env <environment>","Target environment").option("--engine <engine>","postgres, mysql, mariadb, redis, mongodb, or libsql",{default:"postgres"}).option("--provider <provider>","aws_rds, aws_aurora, aws_elasticache, server, container, or external",{default:"aws_rds"}).option("--placement <id>","Provider identifier or runtime placement").option("--plan <plan>","Provider plan or instance type",{default:"db.t4g.micro"}).option("--engine-version <version>","Engine version").option("--storage <gb>","Allocated storage in GB",{default:"20"}).option("--username <name>","Generated credential username",{default:"app"}).option("--database <name>","Initial logical database").option("--subnet-group <name>","Private subnet group").option("--security-groups <ids>","Comma-separated security group IDs").option("--high-availability","Enable provider high availability").option("--public","Request a public endpoint; requires narrow --cidrs").option("--cidrs <ranges>","Comma-separated narrow public CIDRs")}function Qa(N){N.command("data:list","List first-class databases and caches").option("--env <environment>","Target environment").option("--json","Print structured JSON").action((A)=>Kr(()=>oi(A.env,async(L)=>{let j=L.store.list(L.controlPlane.project.id,L.environmentRecord.id);if(A.json)e(JSON.stringify(j,null,2));else c(["ID","Name","Engine","Provider","Status","Plan","Exposure","Management"],Zl(j))}))),N.command("data:capabilities <engine> <provider>","Explain lifecycle support before taking action").action((A,L)=>Kr(async()=>{e(JSON.stringify(Wt(Ya(A),Za(L)),null,2))}));let t=N.command("data:create <name>","Provision an encrypted, private-by-default data service");Xl(t),t.action((A,L)=>Kr(()=>oi(L.env,async(j)=>{let G=Ya(L.engine),Z=Za(L.provider),ee=j.resolveAdapter({provider:Z});if(!ee||!ee.engines.includes(G))throw Error(`No ${Z}/${G} provisioning adapter is configured. Use data:adopt for read-only inventory.`);let ne={...L.database?{database:L.database}:{},...L.subnetGroup?{subnetGroup:L.subnetGroup}:{},...L.securityGroups?{securityGroupIds:L.securityGroups.split(",").map((be)=>be.trim()).filter(Boolean)}:{}},oe=await j.lifecycle.create({organizationId:j.controlPlane.organization.id,projectId:j.controlPlane.project.id,environmentId:j.environmentRecord.id,name:A,engine:G,provider:Z,placement:L.placement??A,engineVersion:L.engineVersion,plan:L.plan??"db.t4g.micro",storageGb:Number(L.storage)||void 0,highAvailability:L.highAvailability===!0,publicExposure:L.public===!0,allowedCidrs:L.cidrs?L.cidrs.split(",").map((be)=>be.trim()).filter(Boolean):[],desiredState:ne,observedState:{},origin:"managed",managementEnabled:!0,ownerActorId:j.actor.id,username:L.username});if(n(`Queued ${oe.service.name} as operation ${oe.operationId}.`),oe.credential)fr("One-time credential — copy it now:"),e(`Username: ${oe.credential.username}`),e(`Password: ${oe.credential.password}`),e(`Reference: ${oe.credential.secretRef}`)})));let m=N.command("data:adopt <name>","Adopt an existing provider resource as read-only inventory");Xl(m),m.action((A,L)=>Kr(()=>oi(L.env,async(j)=>{let G=j.store.create({organizationId:j.controlPlane.organization.id,projectId:j.controlPlane.project.id,environmentId:j.environmentRecord.id,name:A,engine:Ya(L.engine),provider:Za(L.provider),placement:L.placement??A,engineVersion:L.engineVersion,plan:L.plan??"external",storageGb:Number(L.storage)||void 0,highAvailability:L.highAvailability===!0,publicExposure:L.public===!0,allowedCidrs:L.cidrs?L.cidrs.split(",").map((Z)=>Z.trim()).filter(Boolean):[],desiredState:{},observedState:{},credentialRef:void 0,status:"adopted",origin:"adopted",managementEnabled:!1,ownerActorId:j.actor.id});n(`Adopted ${G.name} (${G.id}) read-only.`)}))),N.command("data:show <service>","Show safe service, capability, dependency, and operation metadata").option("--env <environment>","Target environment").option("--reveal","Reveal the encrypted credential with exact confirmation").option("--confirm <name>","Exact service name required with --reveal").action((A,L)=>Kr(()=>oi(L.env,async(j)=>{let G=go(j,A),Z=j.store.credential(G.id),ee={...G,credentialRef:void 0,credential:Z?{configured:!0,username:Z.username,version:Z.version,rotatedAt:Z.rotatedAt}:void 0,dependencies:j.store.dependencies(G.id).map((ne)=>({resourceId:ne.resourceId,requiresRedeploy:ne.requiresRedeploy})),operations:j.queue.list({projectId:G.projectId,limit:250}).filter((ne)=>Gp(ne.operation.input,G.id))};if(e(JSON.stringify(ee,null,2)),L.reveal){if(L.confirm!==G.name)throw Error(`Credential reveal requires --confirm ${G.name}.`);if(!Z)throw Error("No managed credential is configured.");fr("Credential value follows; it will not be logged or persisted by ts-cloud:"),e(`Username: ${Z.username}`),e(`Password: ${await j.secrets.resolve(Z.secretRef)}`)}},A))),N.command("data:connect <service>","Print secret-free connection and tunnel guidance").option("--env <environment>","Target environment").action((A,L)=>Kr(()=>oi(L.env,async(j)=>{let G=go(j,A);c(["Type","Command","Credential reference"],Tn(G,Ql(G)).map((Z)=>[Z.type,Z.command,Z.secretRef??"—"]))},A))),N.command("data:action <service> <action>","Preview or enqueue a durable lifecycle action").option("--env <environment>","Target environment").option("--execute","Enqueue after validation; default is preview only").option("--plan <plan>","New provider plan").option("--storage <gb>","New storage allocation").option("--engine-version <version>","Target engine version").option("--min-capacity <acu>","Aurora minimum capacity").option("--max-capacity <acu>","Aurora maximum capacity").option("--public","Enable public exposure").option("--cidrs <ranges>","Comma-separated narrow public CIDRs").option("--retention <choice>","final_backup or retain for deletion").option("--confirm <name>","Exact service name for destructive actions").option("--backup-id <id>","Required backup for a major version change").option("--compatibility-reviewed","Confirm major-version compatibility review").action((A,L,j)=>Kr(()=>oi(j.env,async(G)=>{if(!Yl.includes(L))throw Error(`Action must be ${Yl.join(", ")}.`);let Z=go(G,A),ee=L,ne=Hp(j),oe=G.lifecycle.plan(Z,ee,ne);if(e(JSON.stringify({...oe,productionExecutionCreated:!1},null,2)),!j.execute)return;let be=G.lifecycle.enqueue(Z,ee,ne,G.actor.id);n(`Queued ${ee} as ${be}.`)},A))),N.command("data:management <service>","Enable reviewed management of an adopted service").option("--env <environment>","Target environment").option("--confirm <name>","Exact service name").action((A,L)=>Kr(()=>oi(L.env,async(j)=>{let G=go(j,A);if(G.origin!=="adopted"||L.confirm!==G.name)throw Error(`Enabling adopted management requires --confirm ${G.name}.`);let Z=j.store.update(G.id,G.version,{managementEnabled:!0});n(`${Z.name} management is enabled.`)},A))),N.command("data:dependency <service> <resource>","Register or remove an application credential dependency").option("--env <environment>","Target environment").option("--dynamic","Dependency consumes a dynamic reference and does not need redeploy").option("--remove","Remove the dependency").action((A,L,j)=>Kr(()=>oi(j.env,async(G)=>{let Z=go(G,A),ee=G.controlPlane.store.listResources(G.controlPlane.project.id,G.environmentRecord.id).find((ne)=>ne.id===L||ne.slug===L);if(!ee)throw Error(`Resource ${L} was not found in ${G.env}.`);if(j.remove){e(G.store.removeDependency(Z.id,ee.id)?"Dependency removed.":"Dependency was not registered.");return}G.store.addDependency({serviceId:Z.id,resourceId:ee.id,secretRef:Z.credentialRef??`secret://data-services/${Z.projectId}/${Z.name}`,requiresRedeploy:!j.dynamic}),n(`Registered dependency ${ee.slug} -> ${Z.name}.`)},A))),N.command("db:list","Alias for data:list").option("--env <environment>","Target environment").action((A)=>Kr(()=>oi(A.env,async(L)=>c(["ID","Name","Engine","Provider","Status","Plan","Exposure","Management"],Zl(L.store.list(L.controlPlane.project.id,L.environmentRecord.id)))))),N.command("db:connect <service>","Alias for data:connect").option("--env <environment>","Target environment").action((A,L)=>Kr(()=>oi(L.env,async(j)=>{let G=go(j,A);c(["Type","Command","Credential reference"],Tn(G,Ql(G)).map((Z)=>[Z.type,Z.command,Z.secretRef??"—"]))},A)));for(let A of["backup","snapshot"])N.command(`db:${A} <service>`,`Alias for data:action ${A==="backup"?"backup":"backup"}`).option("--env <environment>","Target environment").option("--execute","Enqueue the backup; default previews").action((L,j)=>Kr(()=>oi(j.env,async(G)=>{let Z=go(G,L),ee=G.lifecycle.plan(Z,"backup");if(e(JSON.stringify({...ee,productionExecutionCreated:!1},null,2)),j.execute)n(`Queued backup as ${G.lifecycle.enqueue(Z,"backup",{},G.actor.id)}.`)},L)))}function ed(N,t=15){if(!N)return t*60000;let m=/^(\d+)\s*([smhd])?$/.exec(N.trim());if(!m)return t*60000;let A=Number(m[1]),L=m[2]??"m";return A*({s:1000,m:60000,h:3600000,d:86400000}[L]??60000)}function Kp(N,t){for(let m of N){let A=m.timestamp?new Date(m.timestamp).toISOString():"",L=t?`[${t}] `:"";e(`${A} ${L}${(m.message??"").trimEnd()}`)}}function Xa(N){N.command("logs","Stream serverless application logs (http + queue + cli)").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).option("--function <which>","Which function: http | queue | cli | all",{default:"all"}).option("--tail","Continuously stream new log events").option("--filter <pattern>","CloudWatch filter pattern").option("--since <duration>","Look back (e.g. 1h, 30m, 15)",{default:"15m"}).action(async(t)=>{try{let m=await Ne(),A=t?.env||"production",{region:L,functions:j}=ft(m,A),G=new re(L),Z=(t?.function??"all").toLowerCase();if(Z!=="all"&&!["http","queue","cli"].includes(Z)){r("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let ne=(Z==="all"?["http","queue","cli"]:[Z]).map((we)=>({mode:we,group:`/aws/lambda/${j[we]}`}));i(`Logs — ${m.project.slug} (${A})`);let oe=Date.now()-ed(t?.since),be=async()=>{let we=oe;for(let{mode:Ce,group:De}of ne)try{let{events:Se=[]}=await G.filterLogEvents({logGroupName:De,startTime:oe,filterPattern:t?.filter,limit:200});Kp(Se,ne.length>1?Ce:void 0);for(let xe of Se)if((xe.timestamp??0)>we)we=xe.timestamp??we}catch(Se){if(!/ResourceNotFound/i.test(String(Se?.message)))throw Se}oe=we+1};if(await be(),t?.tail){e(`
335
+ (streaming — Ctrl+C to stop)`);for(;;)await new Promise((we)=>setTimeout(we,3000)),await be()}}catch(m){r(`Failed to read logs: ${m.message}`),process.exitCode=1}}),N.command("metrics","Show serverless function metrics (invocations, errors, duration, throttles)").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).option("--since <duration>","Window (e.g. 1h, 24h)",{default:"1h"}).action(async(t)=>{try{let m=await Ne(),A=t?.env||"production",{region:L,functions:j}=ft(m,A),G=new J(L),Z=ed(t?.since,60),ee=new Date,ne=new Date(ee.getTime()-Z),oe=Math.max(60,Math.round(Z/1000));i(`Metrics — ${m.project.slug} (${A}), last ${t?.since??"1h"}`);let be=[];for(let we of["http","queue","cli"]){let Ce=[{Name:"FunctionName",Value:j[we]}],De=async(Qe,Ke)=>{let Ye=await G.getMetricStatistics({Namespace:"AWS/Lambda",MetricName:Qe,Dimensions:Ce,StartTime:ne,EndTime:ee,Period:oe,Statistics:[Ke]});if(!Ye.length)return 0;if(Ke==="Sum")return Ye.reduce((et,Pt)=>et+(Pt.Sum??0),0);if(Ke==="Maximum")return Math.max(...Ye.map((et)=>et.Maximum??0));return Ye.reduce((et,Pt)=>et+(Pt.Average??0),0)/Ye.length},[Se,xe,Me,Ae,Ee]=await Promise.all([De("Invocations","Sum"),De("Errors","Sum"),De("Throttles","Sum"),De("Duration","Average"),De("Duration","Maximum")]),Te=Se?`${(xe/Se*100).toFixed(2)}%`:"—";be.push([we,String(Se),String(xe),Te,String(Me),`${Ae.toFixed(0)}ms`,`${Ee.toFixed(0)}ms`])}c(["Function","Invocations","Errors","Error rate","Throttles","Avg","Max"],be)}catch(m){r(`Failed to read metrics: ${m.message}`),process.exitCode=1}}),N.command("metrics:dashboard","Print the CloudWatch console URL for this app").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).action(async(t)=>{try{let m=await Ne(),A=t?.env||"production",{region:L,functions:j}=ft(m,A),G=encodeURIComponent(JSON.stringify({metrics:["http","queue","cli"].flatMap((Z)=>[["AWS/Lambda","Invocations","FunctionName",j[Z]],["AWS/Lambda","Errors","FunctionName",j[Z]]]),view:"timeSeries",region:L}));i("CloudWatch dashboard"),e(`https://console.aws.amazon.com/cloudwatch/home?region=${L}#metricsV2:graph=${G}`)}catch(m){r(m.message),process.exitCode=1}}),N.command("alarms","List CloudWatch alarms").option("--prefix <prefix>","Filter by alarm name prefix").option("--region <region>","AWS region").action(async(t)=>{i("CloudWatch Alarms");let m=t?.region;if(!m)try{m=(await Ne()).project.region}catch{}try{let L=await new J(m||process.env.AWS_REGION||"us-east-1").describeAlarms({AlarmNamePrefix:t?.prefix,MaxRecords:100});if(!L.length){e("No alarms found");return}c(["Name","State","Metric","Condition"],L.map((j)=>[j.AlarmName??"-",j.StateValue??"-",j.MetricName??"-",`${j.ComparisonOperator??""} ${j.Threshold??""}`.trim()]))}catch(A){r(`Failed to list alarms: ${A.message}`),process.exitCode=1}}),N.command("alarms:create","Create a CloudWatch alarm on a Lambda metric").option("--name <name>","Alarm name").option("--env <environment>","Environment",{default:"production"}).option("--function <which>","Function: http | queue | cli",{default:"http"}).option("--metric <metric>","Lambda metric (Errors, Throttles, Duration, Invocations)",{default:"Errors"}).option("--threshold <value>","Threshold value").option("--comparison <op>","GreaterThanThreshold | GreaterThanOrEqualToThreshold | LessThanThreshold",{default:"GreaterThanThreshold"}).option("--statistic <stat>","Sum | Average | Maximum | Minimum",{default:"Sum"}).action(async(t)=>{if(i("Creating CloudWatch Alarm"),!t?.name||t?.threshold==null){r("Missing required options: --name and --threshold"),process.exitCode=1;return}try{let m=await Ne(),A=t.env||"production",{region:L,functions:j}=ft(m,A),G=t.function??"http";await new J(L).putMetricAlarm({AlarmName:t.name,Namespace:"AWS/Lambda",MetricName:t.metric??"Errors",ComparisonOperator:t.comparison??"GreaterThanThreshold",Threshold:Number(t.threshold),EvaluationPeriods:1,Period:300,Statistic:t.statistic??"Sum",Dimensions:[{Name:"FunctionName",Value:j[G]}],AlarmDescription:`ts-cloud: ${j[G]} ${t.metric} ${t.comparison} ${t.threshold}`}),n(`Alarm '${t.name}' created on ${j[G]} ${t.metric}`)}catch(m){r(`Failed to create alarm: ${m.message}`),process.exitCode=1}})}async function Os(N){let t=await Ne(),m=N||"production";return{region:t.environments?.[m]?.region||t.project.region||"us-east-1",prefix:`${t.project.slug}/${m}`}}function ec(N){N.command("secrets:list","List secrets for an environment").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{i("Secrets");try{let{region:m,prefix:A}=await Os(t?.env),L=new q(m),{SecretList:j=[]}=await L.listSecrets({Filters:[{Key:"name",Values:[`${A}/`]}],MaxResults:100});if(!j.length){e(`No secrets found under ${A}/`);return}c(["Key","Last Changed"],j.map((G)=>[(G.Name??"").replace(`${A}/`,""),G.LastChangedDate??G.LastAccessedDate??"-"]))}catch(m){r(`Failed to list secrets: ${m.message}`),process.exitCode=1}}),N.command("secrets:set <key> <value>","Set (create or update) a secret").option("--env <environment>","Environment (production, staging, development)").action(async(t,m,A)=>{i(`Setting secret ${t}`);let{region:L,prefix:j}=await Os(A?.env),G=new q(L),Z=`${j}/${t}`,ee=new o("Storing secret...");ee.start();try{try{await G.putSecretValue({SecretId:Z,SecretString:m})}catch{await G.createSecret({Name:Z,SecretString:m})}ee.succeed(`Secret ${t} stored (${Z})`),a("Redeploy for the new value to take effect in running functions.")}catch(ne){ee.fail(`Failed to store secret: ${ne.message}`),process.exitCode=1}}),N.command("secrets:get <key>","Reveal a secret value").option("--env <environment>","Environment (production, staging, development)").action(async(t,m)=>{try{let{region:A,prefix:L}=await Os(m?.env),G=await new q(A).getSecretValue({SecretId:`${L}/${t}`});e(G.SecretString??"")}catch(A){r(`Failed to read secret: ${A.message}`),process.exitCode=1}}),N.command("secrets:delete <key>","Delete a secret").option("--env <environment>","Environment (production, staging, development)").option("--force","Delete immediately without a recovery window").action(async(t,m)=>{try{let{region:A,prefix:L}=await Os(m?.env);await new q(A).deleteSecret({SecretId:`${L}/${t}`,ForceDeleteWithoutRecovery:m?.force}),n(`Secret ${t} deleted`)}catch(A){r(`Failed to delete secret: ${A.message}`),process.exitCode=1}})}function tc(N){for(let[t,m]of[["firewall:rules","List provider firewall policy"],["firewall:block <ip>","Block an address"],["firewall:unblock <ip>","Unblock an address"],["firewall:countries","Manage geographic policy"]])N.command(t,m).action(async(...A)=>Vr(t.split(" ")[0],{target:String(A[0]??""),message:"No reviewed WAF/IP-set adapter is configured for this legacy command.",nextAction:"Use server firewall configuration or install a provider adapter; no rule was changed."}))}function ic(N){N.command("ssl:list","List all SSL certificates").option("--region <region>","AWS region (default: us-east-1)").action(async(t)=>{i("SSL Certificates");let m=t?.region||"us-east-1",A=new o("Fetching certificates from ACM...");A.start();try{let L=new H(m),j=await L.listCertificates();if(j.CertificateSummaryList.length===0){A.succeed("No certificates found"),e(`
336
+ No SSL certificates found in region ${m}`),e("Use 'cloud domain:ssl <domain>' to request a new certificate");return}let G=await Promise.all(j.CertificateSummaryList.map(async(ne)=>await L.describeCertificate({CertificateArn:ne.CertificateArn})));A.succeed(`Found ${G.length} certificate(s)`);let Z=(ne)=>{if(!ne)return"N/A";let oe=typeof ne==="string"?Number.parseFloat(ne):ne,be=oe<1000000000000?oe*1000:oe;return new Date(be).toISOString().split("T")[0]},ee=G.map((ne)=>{let oe=Z(ne.NotAfter),be=ne.Type==="AMAZON_ISSUED"?"Amazon Issued":ne.Type||"Unknown",we=ne.Status==="ISSUED"?"Available":ne.Status||"Unknown";return[ne.DomainName,ne.Status||"Unknown",oe,be,we]});c(["Domain","Status","Expiry","Type","State"],ee),e(`
337
+ ACM certificates are automatically renewed by AWS`),e(`Region: ${m}`)}catch(L){A.fail("Failed to fetch certificates"),r(L.message)}}),N.command("ssl:renew <domain>","Renew SSL certificate").option("--region <region>","AWS region (default: us-east-1)").action(async(t,m)=>{i(`Checking SSL Certificate for ${t}`);let A=m?.region||"us-east-1";e(`Domain: ${t}`),e(`Region: ${A}`);let L=new o("Checking certificate status...");L.start();try{let G=await new H(A).findCertificateByDomain(t);if(!G){L.fail("Certificate not found"),r(`No certificate found for domain: ${t}`),e("Use 'cloud domain:ssl <domain>' to request a new certificate");return}L.succeed("Certificate found"),e(`
338
338
  Certificate is managed by AWS Certificate Manager`),e("ACM certificates are automatically renewed 60 days before expiry"),a(`
339
- No manual renewal needed for ACM certificates`);let Z=(oe)=>{if(!oe)return"N/A";let be=typeof oe==="string"?Number.parseFloat(oe):oe,we=be<1000000000000?be*1000:be;return new Date(we).toISOString().split("T")[0]},ee=Z(G.NotAfter),re=Z(G.IssuedAt);if(e(`
340
- Certificate details:`),e(` - Domain: ${G.DomainName}`),e(` - Status: ${G.Status}`),e(` - Issued: ${re}`),e(` - Expiry: ${ee}`),e(` - Type: ${G.Type||"Unknown"}`),e(` - ARN: ${G.CertificateArn}`),e(` - Auto-renewal: ${G.Type==="AMAZON_ISSUED"?"Enabled":"N/A (imported)"}`),G.SubjectAlternativeNames&&G.SubjectAlternativeNames.length>1)e(` - SANs: ${G.SubjectAlternativeNames.join(", ")}`)}catch(j){L.fail("Failed to check certificate"),r(j.message)}})}function eo(N){return N.toISOString().slice(0,10)}function ql(N){return new Date(Date.UTC(N.getUTCFullYear(),N.getUTCMonth(),N.getUTCDate()))}function Gl(N=new Date){let t=ql(N),p=new Date(Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),1)),A=new Date(t.getTime()+86400000),L=new Date(Date.UTC(t.getUTCFullYear(),t.getUTCMonth()+1,1)),j=p,G=new Date(Date.UTC(t.getUTCFullYear(),t.getUTCMonth()-1,1));return{start:eo(p),end:eo(A),label:p.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"}),daysElapsed:t.getUTCDate(),daysInMonth:Math.round((L.getTime()-p.getTime())/86400000),previous:{start:eo(G),end:eo(j),label:G.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"})}}}function ko(N,t=new Date){if(!Number.isInteger(N)||N<1||N>366)throw Error("days must be an integer from 1 to 366");let p=new Date(ql(t).getTime()+86400000),A=new Date(p.getTime()-N*86400000),L=new Date(A.getTime()-N*86400000);return{current:{start:eo(A),end:eo(p),label:`last ${N} days`},previous:{start:eo(L),end:eo(A),label:`previous ${N} days`}}}function Ja(N,t,p){return t>0?N/t*p:0}function rs(N,t){if(t===0)return N===0?0:null;return(N-t)/t*100}function Ya(N,t){let p=new Map(t.map((A)=>[A.service,A.amount]));return N.map((A)=>{let L=p.get(A.service)??0;return{...A,previousAmount:L,changePercent:rs(A.amount,L)}})}var Bm=/(?:data.?transfer|natgateway.*bytes|aws-(?:in|out)-bytes|regional-bytes|inter.?az|internet)/i;function Is(N){return N.filter((t)=>Bm.test(t.key)).sort((t,p)=>p.amount-t.amount)}function Ro(N){if(N==null)return[];return Array.isArray(N)?N:[N]}function lo(N,t={}){let[,,p="unknown",A="",L="",j=N]=N.split(":"),G=j.includes("/")?"/":":",[Z=p,...ee]=j.split(G),re=ee.join(G)||j;return{arn:N,service:p,type:Z,id:re,name:t.Name||t.name||re,region:A||void 0,accountId:L||void 0,tags:t,metadata:{}}}function Mm(N,t){if(!t)return!0;let p=t.toLowerCase().replace(/^aws[:/]/,"");return[N.service,N.type,`${N.service}:${N.type}`].some((A)=>A.toLowerCase()===p||A.toLowerCase().includes(p))}function _m(N,t){let p=new Map;for(let A of N){let L=`${A.service}:${A.type}:${A.id}`,j=p.get(L);p.set(L,j?{...j,...A,arn:j.accountId?j.arn:A.arn,accountId:j.accountId??A.accountId,tags:{...j.tags,...A.tags},metadata:{...j.metadata,...A.metadata}}:A)}return[...p.values()].filter((A)=>Mm(A,t)).sort((A,L)=>A.service.localeCompare(L.service)||A.name.localeCompare(L.name)||A.arn.localeCompare(L.arn))}class is{client;profile;region;constructor(N,t=process.env.AWS_REGION||"us-east-1"){this.profile=N,this.region=t,this.client=new u(St(N))}async taggedResources(){let N=[],t;do{let p=await this.client.request({service:"tagging",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-amz-json-1.1","x-amz-target":"ResourceGroupsTaggingAPI_20170126.GetResources"},body:JSON.stringify(t?{PaginationToken:t}:{})});for(let A of p?.ResourceTagMappingList??[]){let L=Object.fromEntries((A.Tags??[]).map((j)=>[String(j.Key),String(j.Value)]));N.push(lo(String(A.ResourceARN),L))}t=p?.PaginationToken||void 0}while(t);return N}async ec2Resources(){let N=async(j)=>this.client.request({service:"ec2",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({Action:j,Version:"2016-11-15"}).toString()}),[t,p,A]=await Promise.all([N("DescribeInstances"),N("DescribeVolumes"),N("DescribeAddresses")]),L=[];for(let j of Ro(t?.reservationSet?.item))for(let G of Ro(j?.instancesSet?.item)){let Z=String(G.instanceId);L.push({...lo(`arn:aws:ec2:${this.region}::instance/${Z}`),state:G.instanceState?.name,metadata:{instanceType:G.instanceType}})}for(let j of Ro(p?.volumeSet?.item)){let G=String(j.volumeId);L.push({...lo(`arn:aws:ec2:${this.region}::volume/${G}`),state:j.status,metadata:{sizeGiB:Number(j.size??0),attached:Ro(j.attachmentSet?.item).length>0}})}for(let j of Ro(A?.addressesSet?.item)){let G=String(j.allocationId??j.publicIp);L.push({...lo(`arn:aws:ec2:${this.region}::elastic-ip/${G}`),state:j.associationId?"associated":"available",metadata:{associated:Boolean(j.associationId),publicIp:j.publicIp}})}return L}async rdsResources(){let N=new URLSearchParams({Action:"DescribeDBInstances",Version:"2014-10-31"}).toString(),t=await this.client.request({service:"rds",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-www-form-urlencoded"},body:N}),p=t?.DescribeDBInstancesResult??t;return Ro(p?.DBInstances?.DBInstance).map((A)=>({...lo(A.DBInstanceArn||`arn:aws:rds:${this.region}::db:${String(A.DBInstanceIdentifier)}`),state:A.DBInstanceStatus,metadata:{engine:A.Engine,instanceClass:A.DBInstanceClass}}))}async lambdaResources(){let N=[],t;do{let p=await this.client.request({service:"lambda",region:this.region,method:"GET",path:"/2015-03-31/functions",queryParams:t?{Marker:t}:void 0});for(let A of p?.Functions??[])N.push({...lo(A.FunctionArn),state:A.State,metadata:{runtime:A.Runtime,memoryMiB:Number(A.MemorySize??0)}});t=p?.NextMarker||void 0}while(t);return N}async s3Resources(){return(await new f("us-east-1",this.profile).listBuckets()).Buckets.map((t)=>({...lo(`arn:aws:s3:::${t.Name}`),metadata:{creationDate:t.CreationDate}}))}async discover(N){let t=[["tagging:GetResources",()=>this.taggedResources()],["ec2:Describe*",()=>this.ec2Resources()],["rds:DescribeDBInstances",()=>this.rdsResources()],["lambda:ListFunctions",()=>this.lambdaResources()],["s3:ListAllMyBuckets",()=>this.s3Resources()]],p=await Promise.allSettled(t.map(([,j])=>j())),A=[],L=[];return p.forEach((j,G)=>{if(j.status==="fulfilled")A.push(...j.value);else L.push(`${t[G][0]} unavailable: ${j.reason instanceof Error?j.reason.message:String(j.reason)}`)}),{resources:_m(A,N?.type),warnings:L}}}function ss(N,t){if(N==null)return{currentCost:null,projectedCost:null,monthlySavings:null};let p=N*t;return{currentCost:N,projectedCost:N-p,monthlySavings:p}}function Hl(N,t,p,A=!1){let L=Za(N,t,p);if(L)return[{category:"unused",resource:N,recommendation:L.recommendation,sourceSignal:L.signal,...ss(p,1)}];if((N.service==="ec2"&&N.type==="instance"||N.service==="rds"&&N.type==="db")&&t.cpuAverage!=null&&t.cpuAverage<20)return[{category:"right-size",resource:N,recommendation:"Validate memory, network, and peak demand, then test one smaller instance class.",sourceSignal:`${N.service.toUpperCase()} 30-day average CPU ${t.cpuAverage.toFixed(1)}% (<20%)`,...ss(p,0.4)}];if(A&&(N.service==="ec2"&&N.type==="instance"&&N.state==="running"||N.service==="rds"&&N.type==="db"&&N.state==="available")&&t.cpuAverage!=null&&t.cpuAverage>=20)return[{category:"commitment",resource:N,recommendation:"Compare a one-year no-upfront commitment against this steady workload before purchasing.",sourceSignal:`${N.service.toUpperCase()} remained active with 30-day average CPU ${t.cpuAverage.toFixed(1)}%`,...ss(p,0.3)}];if(N.service==="s3"&&t.storageBytes!=null&&t.storageBytes>=5368709120&&t.requests!=null&&t.requests<9000){let j=t.storageBytes/1073741824;return[{category:"storage",resource:N,recommendation:"Review object age and retrieval needs, then add Intelligent-Tiering or Glacier lifecycle rules.",sourceSignal:`${j.toFixed(1)} GiB stored with ${t.requests.toFixed(0)} requests over 90 days`,...ss(p,0.25)}]}return[]}function zm(N,t){if(N<=0||t.length===0)return null;return{category:"cdn",resource:{arn:"arn:aws:cloudfront::account:distribution/account-wide",service:"cloudfront",type:"distribution",id:"account-wide",name:"account-wide transfer",tags:{},metadata:{}},recommendation:"Enable compression and review cache policies, TTLs, and origin cache headers for high-transfer paths.",sourceSignal:`30-day Cost Explorer transfer line items: ${t.slice(0,3).join(", ")}`,...ss(N,0.2)}}function Um(N){let t=N.map((p)=>p.Average).filter((p)=>p!=null);return t.length>0?t.reduce((p,A)=>p+A,0)/t.length:void 0}function Vm(N){let t=N.map((p)=>p.Sum).filter((p)=>p!=null);return t.length>0?t.reduce((p,A)=>p+A,0):void 0}function Za(N,t,p=null){if(N.service==="ec2"&&N.type==="volume"&&N.metadata.attached===!1)return{resource:N,signal:"EBS volume is detached",recommendation:"Snapshot if needed, then delete the detached volume.",monthlySavings:p};if(N.service==="ec2"&&N.type==="elastic-ip"&&N.metadata.associated===!1)return{resource:N,signal:"Elastic IP is not associated",recommendation:"Release the unassociated Elastic IP.",monthlySavings:p};if(N.service==="ec2"&&N.type==="instance"&&t.cpuAverage!=null&&t.cpuAverage<5)return{resource:N,signal:`30-day average CPU ${t.cpuAverage.toFixed(1)}% (<5%)`,recommendation:"Stop/delete if unused, or right-size after validating memory and network demand.",monthlySavings:p};if(N.service==="rds"&&N.type==="db"&&t.connectionsAverage!=null&&t.iopsAverage!=null&&t.connectionsAverage===0&&t.iopsAverage<1)return{resource:N,signal:`14-day connections 0 and average IOPS ${t.iopsAverage.toFixed(2)} (<1)`,recommendation:"Take a final snapshot, validate ownership, then stop or delete the database.",monthlySavings:p};if(N.service==="lambda"&&N.type==="function"&&t.invocations===0)return{resource:N,signal:"30-day invocations 0",recommendation:"Confirm no event source depends on it, then remove the function.",monthlySavings:p};if(N.service==="s3"&&t.objectCount===0&&t.requests===0)return{resource:N,signal:"Bucket is empty with zero requests over 90 days",recommendation:"Confirm retention policy, then delete the empty bucket.",monthlySavings:p};if(N.service==="elasticache"&&t.connectionsAverage!=null&&t.cacheHitRate!=null&&t.connectionsAverage===0&&t.cacheHitRate<20)return{resource:N,signal:`14-day connections 0 and cache hit rate ${t.cacheHitRate.toFixed(1)}%`,recommendation:"Validate clients and snapshots, then delete the idle cache cluster.",monthlySavings:p};return null}class Ps{inventory;costs;profile;region;constructor(N,t=process.env.AWS_REGION||"us-east-1"){this.profile=N,this.region=t,this.inventory=new is(N,t),this.costs=new Qe(N)}async metric(N,t,p,A,L,j,G,Z=[]){let ee=new Date,re=new Date(ee.getTime()-j*24*60*60*1000),oe=await N.getMetricStatistics({Namespace:p,MetricName:A,Dimensions:[{Name:L,Value:t.id},...Z],StartTime:re,EndTime:ee,Period:86400,Statistics:[G]});return G==="Sum"?Vm(oe):Um(oe)}async signals(N){let t=new Q(N.region??this.region,this.profile);if(N.service==="ec2"&&N.type==="instance")return{cpuAverage:await this.metric(t,N,"AWS/EC2","CPUUtilization","InstanceId",30,"Average")};if(N.service==="rds"&&N.type==="db"){let[p,A,L,j]=await Promise.all([this.metric(t,N,"AWS/RDS","CPUUtilization","DBInstanceIdentifier",30,"Average"),this.metric(t,N,"AWS/RDS","DatabaseConnections","DBInstanceIdentifier",14,"Average"),this.metric(t,N,"AWS/RDS","ReadIOPS","DBInstanceIdentifier",14,"Average"),this.metric(t,N,"AWS/RDS","WriteIOPS","DBInstanceIdentifier",14,"Average")]);return{cpuAverage:p,connectionsAverage:A,iopsAverage:L!=null&&j!=null?L+j:void 0}}if(N.service==="lambda"&&N.type==="function")return{invocations:await this.metric(t,N,"AWS/Lambda","Invocations","FunctionName",30,"Sum")};if(N.service==="s3"){let[p,A,L]=await Promise.all([this.metric(t,N,"AWS/S3","NumberOfObjects","BucketName",90,"Average",[{Name:"StorageType",Value:"AllStorageTypes"}]),this.metric(t,N,"AWS/S3","AllRequests","BucketName",90,"Sum"),this.metric(t,N,"AWS/S3","BucketSizeBytes","BucketName",90,"Average",[{Name:"StorageType",Value:"StandardStorage"}])]);return{objectCount:p,requests:A,storageBytes:L}}if(N.service==="elasticache"&&N.type.includes("cluster")){let[p,A]=await Promise.all([this.metric(t,N,"AWS/ElastiCache","CurrConnections","CacheClusterId",14,"Average"),this.metric(t,N,"AWS/ElastiCache","CacheHitRate","CacheClusterId",14,"Average")]);return{connectionsAverage:p,cacheHitRate:A}}return{}}async resourceCosts(){try{let N=ko(30).current,t=await this.costs.getCostByDimension({start:N.start,end:N.end,dimension:"RESOURCE_ID",granularity:"DAILY"});return new Map(t.map((p)=>[p.key,p.amount]))}catch{return new Map}}resourceCost(N,t){return N.get(t.id)??N.get(t.arn)??null}async cloudFrontTransfer(){try{let N=ko(30).current,t=await this.costs.getCostByDimension({start:N.start,end:N.end,dimension:"USAGE_TYPE",granularity:"DAILY",filter:{Dimensions:{Key:"SERVICE",Values:["Amazon CloudFront"]}}}),p=Is(t);return{cost:p.reduce((A,L)=>A+L.amount,0),usageTypes:p.map((A)=>A.key)}}catch{return{cost:0,usageTypes:[]}}}async unused(N){let[t,p]=await Promise.all([this.inventory.discover(N),this.resourceCosts()]),A=(await Promise.all(t.resources.map(async(L)=>{let j=this.resourceCost(p,L),G=Za(L,{},j);if(G)return G;try{return Za(L,await this.signals(L),j)}catch{return null}}))).filter((L)=>L!==null);return{findings:A,inventory:t,savingsAvailable:A.some((L)=>L.monthlySavings!=null)}}async optimize(N){let t=!N?.type||["cloudfront","cdn"].includes(N.type.toLowerCase()),[p,A,L]=await Promise.all([this.inventory.discover({type:N?.type}),this.resourceCosts(),t?this.cloudFrontTransfer():Promise.resolve({cost:0,usageTypes:[]})]),j=[],G=[...p.warnings];for(let ee of p.resources){let re=this.resourceCost(A,ee),oe=Hl(ee,{},re,N?.includeCommitments);if(oe.length>0){j.push(...oe);continue}try{j.push(...Hl(ee,await this.signals(ee),re,N?.includeCommitments))}catch(be){G.push(`CloudWatch metrics unavailable for ${ee.service}:${ee.type}/${ee.name}: ${be instanceof Error?be.message:String(be)}`)}}let Z=zm(L.cost,L.usageTypes);if(Z)j.push(Z);return j.sort((ee,re)=>(re.monthlySavings??-1)-(ee.monthlySavings??-1)||ee.category.localeCompare(re.category)||ee.resource.name.localeCompare(re.resource.name)),{recommendations:j,inventory:p,savingsAvailable:j.some((ee)=>ee.monthlySavings!=null),warnings:G}}}var Qa="Amazon Simple Storage Service";function Wm(){let N=new Date,t=new Date(Date.UTC(N.getUTCFullYear(),N.getUTCMonth(),1)),p=new Date(t);p.setUTCMonth(p.getUTCMonth()-1);let A=(j)=>j.toISOString().slice(0,10),L=p.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"});return{start:A(p),end:A(t),label:L}}function qn(N){return`$${N.toFixed(2)}`}function Es(N){if(N===null)return"new";if(Math.abs(N)<0.05)return"→ 0.0%";return`${N>0?"↑":"↓"} ${Math.abs(N).toFixed(1)}%`}function Xa(N){r(`Cost Explorer request failed: ${N instanceof Error?N.message:String(N)}`),e(`
341
- This command needs the ce:GetCostAndUsage IAM permission.`)}function qm(N){let{label:t,profile:p,rows:A,total:L,count:j}=N,G=`# AWS Cost Analysis — ${t}`,Z=p?`
342
- _Profile: \`${p}\`_
339
+ No manual renewal needed for ACM certificates`);let Z=(oe)=>{if(!oe)return"N/A";let be=typeof oe==="string"?Number.parseFloat(oe):oe,we=be<1000000000000?be*1000:be;return new Date(we).toISOString().split("T")[0]},ee=Z(G.NotAfter),ne=Z(G.IssuedAt);if(e(`
340
+ Certificate details:`),e(` - Domain: ${G.DomainName}`),e(` - Status: ${G.Status}`),e(` - Issued: ${ne}`),e(` - Expiry: ${ee}`),e(` - Type: ${G.Type||"Unknown"}`),e(` - ARN: ${G.CertificateArn}`),e(` - Auto-renewal: ${G.Type==="AMAZON_ISSUED"?"Enabled":"N/A (imported)"}`),G.SubjectAlternativeNames&&G.SubjectAlternativeNames.length>1)e(` - SANs: ${G.SubjectAlternativeNames.join(", ")}`)}catch(j){L.fail("Failed to check certificate"),r(j.message)}})}function oo(N){return N.toISOString().slice(0,10)}function td(N){return new Date(Date.UTC(N.getUTCFullYear(),N.getUTCMonth(),N.getUTCDate()))}function nd(N=new Date){let t=td(N),m=new Date(Date.UTC(t.getUTCFullYear(),t.getUTCMonth(),1)),A=new Date(t.getTime()+86400000),L=new Date(Date.UTC(t.getUTCFullYear(),t.getUTCMonth()+1,1)),j=m,G=new Date(Date.UTC(t.getUTCFullYear(),t.getUTCMonth()-1,1));return{start:oo(m),end:oo(A),label:m.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"}),daysElapsed:t.getUTCDate(),daysInMonth:Math.round((L.getTime()-m.getTime())/86400000),previous:{start:oo(G),end:oo(j),label:G.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"})}}}function xo(N,t=new Date){if(!Number.isInteger(N)||N<1||N>366)throw Error("days must be an integer from 1 to 366");let m=new Date(td(t).getTime()+86400000),A=new Date(m.getTime()-N*86400000),L=new Date(A.getTime()-N*86400000);return{current:{start:oo(A),end:oo(m),label:`last ${N} days`},previous:{start:oo(L),end:oo(A),label:`previous ${N} days`}}}function oc(N,t,m){return t>0?N/t*m:0}function as(N,t){if(t===0)return N===0?0:null;return(N-t)/t*100}function sc(N,t){let m=new Map(t.map((A)=>[A.service,A.amount]));return N.map((A)=>{let L=m.get(A.service)??0;return{...A,previousAmount:L,changePercent:as(A.amount,L)}})}var Jp=/(?:data.?transfer|natgateway.*bytes|aws-(?:in|out)-bytes|regional-bytes|inter.?az|internet)/i;function Ls(N){return N.filter((t)=>Jp.test(t.key)).sort((t,m)=>m.amount-t.amount)}function Do(N){if(N==null)return[];return Array.isArray(N)?N:[N]}function ho(N,t={}){let[,,m="unknown",A="",L="",j=N]=N.split(":"),G=j.includes("/")?"/":":",[Z=m,...ee]=j.split(G),ne=ee.join(G)||j;return{arn:N,service:m,type:Z,id:ne,name:t.Name||t.name||ne,region:A||void 0,accountId:L||void 0,tags:t,metadata:{}}}function Yp(N,t){if(!t)return!0;let m=t.toLowerCase().replace(/^aws[:/]/,"");return[N.service,N.type,`${N.service}:${N.type}`].some((A)=>A.toLowerCase()===m||A.toLowerCase().includes(m))}function Zp(N,t){let m=new Map;for(let A of N){let L=`${A.service}:${A.type}:${A.id}`,j=m.get(L);m.set(L,j?{...j,...A,arn:j.accountId?j.arn:A.arn,accountId:j.accountId??A.accountId,tags:{...j.tags,...A.tags},metadata:{...j.metadata,...A.metadata}}:A)}return[...m.values()].filter((A)=>Yp(A,t)).sort((A,L)=>A.service.localeCompare(L.service)||A.name.localeCompare(L.name)||A.arn.localeCompare(L.arn))}class cs{client;profile;region;constructor(N,t=process.env.AWS_REGION||"us-east-1"){this.profile=N,this.region=t,this.client=new u(St(N))}async taggedResources(){let N=[],t;do{let m=await this.client.request({service:"tagging",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-amz-json-1.1","x-amz-target":"ResourceGroupsTaggingAPI_20170126.GetResources"},body:JSON.stringify(t?{PaginationToken:t}:{})});for(let A of m?.ResourceTagMappingList??[]){let L=Object.fromEntries((A.Tags??[]).map((j)=>[String(j.Key),String(j.Value)]));N.push(ho(String(A.ResourceARN),L))}t=m?.PaginationToken||void 0}while(t);return N}async ec2Resources(){let N=async(j)=>this.client.request({service:"ec2",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({Action:j,Version:"2016-11-15"}).toString()}),[t,m,A]=await Promise.all([N("DescribeInstances"),N("DescribeVolumes"),N("DescribeAddresses")]),L=[];for(let j of Do(t?.reservationSet?.item))for(let G of Do(j?.instancesSet?.item)){let Z=String(G.instanceId);L.push({...ho(`arn:aws:ec2:${this.region}::instance/${Z}`),state:G.instanceState?.name,metadata:{instanceType:G.instanceType}})}for(let j of Do(m?.volumeSet?.item)){let G=String(j.volumeId);L.push({...ho(`arn:aws:ec2:${this.region}::volume/${G}`),state:j.status,metadata:{sizeGiB:Number(j.size??0),attached:Do(j.attachmentSet?.item).length>0}})}for(let j of Do(A?.addressesSet?.item)){let G=String(j.allocationId??j.publicIp);L.push({...ho(`arn:aws:ec2:${this.region}::elastic-ip/${G}`),state:j.associationId?"associated":"available",metadata:{associated:Boolean(j.associationId),publicIp:j.publicIp}})}return L}async rdsResources(){let N=new URLSearchParams({Action:"DescribeDBInstances",Version:"2014-10-31"}).toString(),t=await this.client.request({service:"rds",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-www-form-urlencoded"},body:N}),m=t?.DescribeDBInstancesResult??t;return Do(m?.DBInstances?.DBInstance).map((A)=>({...ho(A.DBInstanceArn||`arn:aws:rds:${this.region}::db:${String(A.DBInstanceIdentifier)}`),state:A.DBInstanceStatus,metadata:{engine:A.Engine,instanceClass:A.DBInstanceClass}}))}async lambdaResources(){let N=[],t;do{let m=await this.client.request({service:"lambda",region:this.region,method:"GET",path:"/2015-03-31/functions",queryParams:t?{Marker:t}:void 0});for(let A of m?.Functions??[])N.push({...ho(A.FunctionArn),state:A.State,metadata:{runtime:A.Runtime,memoryMiB:Number(A.MemorySize??0)}});t=m?.NextMarker||void 0}while(t);return N}async s3Resources(){return(await new g("us-east-1",this.profile).listBuckets()).Buckets.map((t)=>({...ho(`arn:aws:s3:::${t.Name}`),metadata:{creationDate:t.CreationDate}}))}async discover(N){let t=[["tagging:GetResources",()=>this.taggedResources()],["ec2:Describe*",()=>this.ec2Resources()],["rds:DescribeDBInstances",()=>this.rdsResources()],["lambda:ListFunctions",()=>this.lambdaResources()],["s3:ListAllMyBuckets",()=>this.s3Resources()]],m=await Promise.allSettled(t.map(([,j])=>j())),A=[],L=[];return m.forEach((j,G)=>{if(j.status==="fulfilled")A.push(...j.value);else L.push(`${t[G][0]} unavailable: ${j.reason instanceof Error?j.reason.message:String(j.reason)}`)}),{resources:Zp(A,N?.type),warnings:L}}}function ls(N,t){if(N==null)return{currentCost:null,projectedCost:null,monthlySavings:null};let m=N*t;return{currentCost:N,projectedCost:N-m,monthlySavings:m}}function rd(N,t,m,A=!1){let L=ac(N,t,m);if(L)return[{category:"unused",resource:N,recommendation:L.recommendation,sourceSignal:L.signal,...ls(m,1)}];if((N.service==="ec2"&&N.type==="instance"||N.service==="rds"&&N.type==="db")&&t.cpuAverage!=null&&t.cpuAverage<20)return[{category:"right-size",resource:N,recommendation:"Validate memory, network, and peak demand, then test one smaller instance class.",sourceSignal:`${N.service.toUpperCase()} 30-day average CPU ${t.cpuAverage.toFixed(1)}% (<20%)`,...ls(m,0.4)}];if(A&&(N.service==="ec2"&&N.type==="instance"&&N.state==="running"||N.service==="rds"&&N.type==="db"&&N.state==="available")&&t.cpuAverage!=null&&t.cpuAverage>=20)return[{category:"commitment",resource:N,recommendation:"Compare a one-year no-upfront commitment against this steady workload before purchasing.",sourceSignal:`${N.service.toUpperCase()} remained active with 30-day average CPU ${t.cpuAverage.toFixed(1)}%`,...ls(m,0.3)}];if(N.service==="s3"&&t.storageBytes!=null&&t.storageBytes>=5368709120&&t.requests!=null&&t.requests<9000){let j=t.storageBytes/1073741824;return[{category:"storage",resource:N,recommendation:"Review object age and retrieval needs, then add Intelligent-Tiering or Glacier lifecycle rules.",sourceSignal:`${j.toFixed(1)} GiB stored with ${t.requests.toFixed(0)} requests over 90 days`,...ls(m,0.25)}]}return[]}function Qp(N,t){if(N<=0||t.length===0)return null;return{category:"cdn",resource:{arn:"arn:aws:cloudfront::account:distribution/account-wide",service:"cloudfront",type:"distribution",id:"account-wide",name:"account-wide transfer",tags:{},metadata:{}},recommendation:"Enable compression and review cache policies, TTLs, and origin cache headers for high-transfer paths.",sourceSignal:`30-day Cost Explorer transfer line items: ${t.slice(0,3).join(", ")}`,...ls(N,0.2)}}function Xp(N){let t=N.map((m)=>m.Average).filter((m)=>m!=null);return t.length>0?t.reduce((m,A)=>m+A,0)/t.length:void 0}function em(N){let t=N.map((m)=>m.Sum).filter((m)=>m!=null);return t.length>0?t.reduce((m,A)=>m+A,0):void 0}function ac(N,t,m=null){if(N.service==="ec2"&&N.type==="volume"&&N.metadata.attached===!1)return{resource:N,signal:"EBS volume is detached",recommendation:"Snapshot if needed, then delete the detached volume.",monthlySavings:m};if(N.service==="ec2"&&N.type==="elastic-ip"&&N.metadata.associated===!1)return{resource:N,signal:"Elastic IP is not associated",recommendation:"Release the unassociated Elastic IP.",monthlySavings:m};if(N.service==="ec2"&&N.type==="instance"&&t.cpuAverage!=null&&t.cpuAverage<5)return{resource:N,signal:`30-day average CPU ${t.cpuAverage.toFixed(1)}% (<5%)`,recommendation:"Stop/delete if unused, or right-size after validating memory and network demand.",monthlySavings:m};if(N.service==="rds"&&N.type==="db"&&t.connectionsAverage!=null&&t.iopsAverage!=null&&t.connectionsAverage===0&&t.iopsAverage<1)return{resource:N,signal:`14-day connections 0 and average IOPS ${t.iopsAverage.toFixed(2)} (<1)`,recommendation:"Take a final snapshot, validate ownership, then stop or delete the database.",monthlySavings:m};if(N.service==="lambda"&&N.type==="function"&&t.invocations===0)return{resource:N,signal:"30-day invocations 0",recommendation:"Confirm no event source depends on it, then remove the function.",monthlySavings:m};if(N.service==="s3"&&t.objectCount===0&&t.requests===0)return{resource:N,signal:"Bucket is empty with zero requests over 90 days",recommendation:"Confirm retention policy, then delete the empty bucket.",monthlySavings:m};if(N.service==="elasticache"&&t.connectionsAverage!=null&&t.cacheHitRate!=null&&t.connectionsAverage===0&&t.cacheHitRate<20)return{resource:N,signal:`14-day connections 0 and cache hit rate ${t.cacheHitRate.toFixed(1)}%`,recommendation:"Validate clients and snapshots, then delete the idle cache cluster.",monthlySavings:m};return null}class js{inventory;costs;profile;region;constructor(N,t=process.env.AWS_REGION||"us-east-1"){this.profile=N,this.region=t,this.inventory=new cs(N,t),this.costs=new Fe(N)}async metric(N,t,m,A,L,j,G,Z=[]){let ee=new Date,ne=new Date(ee.getTime()-j*24*60*60*1000),oe=await N.getMetricStatistics({Namespace:m,MetricName:A,Dimensions:[{Name:L,Value:t.id},...Z],StartTime:ne,EndTime:ee,Period:86400,Statistics:[G]});return G==="Sum"?em(oe):Xp(oe)}async signals(N){let t=new J(N.region??this.region,this.profile);if(N.service==="ec2"&&N.type==="instance")return{cpuAverage:await this.metric(t,N,"AWS/EC2","CPUUtilization","InstanceId",30,"Average")};if(N.service==="rds"&&N.type==="db"){let[m,A,L,j]=await Promise.all([this.metric(t,N,"AWS/RDS","CPUUtilization","DBInstanceIdentifier",30,"Average"),this.metric(t,N,"AWS/RDS","DatabaseConnections","DBInstanceIdentifier",14,"Average"),this.metric(t,N,"AWS/RDS","ReadIOPS","DBInstanceIdentifier",14,"Average"),this.metric(t,N,"AWS/RDS","WriteIOPS","DBInstanceIdentifier",14,"Average")]);return{cpuAverage:m,connectionsAverage:A,iopsAverage:L!=null&&j!=null?L+j:void 0}}if(N.service==="lambda"&&N.type==="function")return{invocations:await this.metric(t,N,"AWS/Lambda","Invocations","FunctionName",30,"Sum")};if(N.service==="s3"){let[m,A,L]=await Promise.all([this.metric(t,N,"AWS/S3","NumberOfObjects","BucketName",90,"Average",[{Name:"StorageType",Value:"AllStorageTypes"}]),this.metric(t,N,"AWS/S3","AllRequests","BucketName",90,"Sum"),this.metric(t,N,"AWS/S3","BucketSizeBytes","BucketName",90,"Average",[{Name:"StorageType",Value:"StandardStorage"}])]);return{objectCount:m,requests:A,storageBytes:L}}if(N.service==="elasticache"&&N.type.includes("cluster")){let[m,A]=await Promise.all([this.metric(t,N,"AWS/ElastiCache","CurrConnections","CacheClusterId",14,"Average"),this.metric(t,N,"AWS/ElastiCache","CacheHitRate","CacheClusterId",14,"Average")]);return{connectionsAverage:m,cacheHitRate:A}}return{}}async resourceCosts(){try{let N=xo(30).current,t=await this.costs.getCostByDimension({start:N.start,end:N.end,dimension:"RESOURCE_ID",granularity:"DAILY"});return new Map(t.map((m)=>[m.key,m.amount]))}catch{return new Map}}resourceCost(N,t){return N.get(t.id)??N.get(t.arn)??null}async cloudFrontTransfer(){try{let N=xo(30).current,t=await this.costs.getCostByDimension({start:N.start,end:N.end,dimension:"USAGE_TYPE",granularity:"DAILY",filter:{Dimensions:{Key:"SERVICE",Values:["Amazon CloudFront"]}}}),m=Ls(t);return{cost:m.reduce((A,L)=>A+L.amount,0),usageTypes:m.map((A)=>A.key)}}catch{return{cost:0,usageTypes:[]}}}async unused(N){let[t,m]=await Promise.all([this.inventory.discover(N),this.resourceCosts()]),A=(await Promise.all(t.resources.map(async(L)=>{let j=this.resourceCost(m,L),G=ac(L,{},j);if(G)return G;try{return ac(L,await this.signals(L),j)}catch{return null}}))).filter((L)=>L!==null);return{findings:A,inventory:t,savingsAvailable:A.some((L)=>L.monthlySavings!=null)}}async optimize(N){let t=!N?.type||["cloudfront","cdn"].includes(N.type.toLowerCase()),[m,A,L]=await Promise.all([this.inventory.discover({type:N?.type}),this.resourceCosts(),t?this.cloudFrontTransfer():Promise.resolve({cost:0,usageTypes:[]})]),j=[],G=[...m.warnings];for(let ee of m.resources){let ne=this.resourceCost(A,ee),oe=rd(ee,{},ne,N?.includeCommitments);if(oe.length>0){j.push(...oe);continue}try{j.push(...rd(ee,await this.signals(ee),ne,N?.includeCommitments))}catch(be){G.push(`CloudWatch metrics unavailable for ${ee.service}:${ee.type}/${ee.name}: ${be instanceof Error?be.message:String(be)}`)}}let Z=Qp(L.cost,L.usageTypes);if(Z)j.push(Z);return j.sort((ee,ne)=>(ne.monthlySavings??-1)-(ee.monthlySavings??-1)||ee.category.localeCompare(ne.category)||ee.resource.name.localeCompare(ne.resource.name)),{recommendations:j,inventory:m,savingsAvailable:j.some((ee)=>ee.monthlySavings!=null),warnings:G}}}var cc="Amazon Simple Storage Service";function tm(){let N=new Date,t=new Date(Date.UTC(N.getUTCFullYear(),N.getUTCMonth(),1)),m=new Date(t);m.setUTCMonth(m.getUTCMonth()-1);let A=(j)=>j.toISOString().slice(0,10),L=m.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"});return{start:A(m),end:A(t),label:L}}function qn(N){return`$${N.toFixed(2)}`}function Fs(N){if(N===null)return"new";if(Math.abs(N)<0.05)return"→ 0.0%";return`${N>0?"↑":"↓"} ${Math.abs(N).toFixed(1)}%`}function lc(N){r(`Cost Explorer request failed: ${N instanceof Error?N.message:String(N)}`),e(`
341
+ This command needs the ce:GetCostAndUsage IAM permission.`)}function nm(N){let{label:t,profile:m,rows:A,total:L,count:j}=N,G=`# AWS Cost Analysis — ${t}`,Z=m?`
342
+ _Profile: \`${m}\`_
343
343
  `:`
344
344
  `,ee=`| Service | Resources | Cost | % of Total |
345
- |---|---|---|---|`,re=A.map(([be,we,Ce,Ee])=>`| ${be} | ${we} | ${Ce} | ${Ee} |`).join(`
345
+ |---|---|---|---|`,ne=A.map(([be,we,Ce,De])=>`| ${be} | ${we} | ${Ce} | ${De} |`).join(`
346
346
  `),oe=`
347
347
  **Total: ${qn(L)} across ${j} service${j===1?"":"s"}**`;return`${G}
348
348
  ${Z}
349
349
  | Service | Resources | Cost | % of Total |
350
350
  |---|---|---|---|
351
- ${re}
351
+ ${ne}
352
352
  ${oe}
353
- `}function ec(N){N.command("cost:analyze","Rank AWS services by cost for the last full month").option("--output","Also write a markdown report to ./aws.md").option("--no-cache","Skip the local response cache (always hit AWS — costs $0.01)").action(async(t)=>{let p=t?.profile,A=t?.cache!==!1,{start:L,end:j,label:G}=Wm();i(`Cost Analysis — ${G}${p?` (profile: ${p})`:""}`);let Z=new o("Querying AWS Cost Explorer...");Z.start();let ee=new Qe(p,{useCache:A}),re;try{re=await ee.getCostByService({start:L,end:j})}catch(Se){Z.stop(),r(`Cost Explorer request failed: ${Se?.message??Se}`),e(`
354
- This command needs the ce:GetCostAndUsage IAM permission.`);return}let oe=null,be=!1;if(re.some((Se)=>Se.service===Qa))try{oe=(await new f("us-east-1",p).listBuckets()).Buckets?.length??0}catch{be=!0}if(Z.stop(),re.length===0){e("No billed services in this period.");return}let we=re.reduce((Se,De)=>Se+De.amount,0),Ce=re.map((Se)=>{let De="-";if(Se.service===Qa){if(be)De="unknown (no s3:ListAllMyBuckets)";else if(oe!==null)De=`${oe} bucket${oe===1?"":"s"}`}let _e=we>0?`${(Se.amount/we*100).toFixed(1)}%`:"—";return[Se.service,De,qn(Se.amount),_e]});if(c(["Service","Resources","Cost","% of Total"],Ce),e(`
355
- Total: ${qn(we)} across ${re.length} service${re.length===1?"":"s"}`),ee.lastCacheAgeSeconds!==null)e(`(cached, ${ee.lastCacheAgeSeconds}s old — pass --no-cache to refresh)`);if((re.find((Se)=>Se.service===Qa)?.amount??0)>1&&oe===0)a(`
356
- S3 has spend but listBuckets returned 0. The buckets are likely owned by a different account in your AWS Organization (consolidated billing rolls up to the payer account, but ListBuckets only shows buckets owned by the calling account).`);if(t?.output){let Se=`${process.cwd()}/aws.md`;await Bun.write(Se,qm({label:G,profile:p,rows:Ce,total:we,count:re.length})),n(`
357
- Wrote ${Se}`)}}),N.command("cost:cache:clear","Wipe the local Cost Explorer response cache").option("--all","Wipe entries for every profile (default: just the current profile)").action(async(t)=>{let p=t?.all?void 0:t?.profile,A=Ka(p),L=Ga(p);e(`Cleared ${L.deletedFiles} cached response${L.deletedFiles===1?"":"s"} for ${L.scope}`),e(`(${A})`)}),N.command("cost","Show current month-to-date AWS spend and projected total").option("--env <environment>","Environment (production, staging, development)").option("--no-cache","Skip the local response cache").action(async(t)=>{let p=Gl(),A=new Qe(t?.profile,{useCache:t?.cache!==!1});i(`AWS Cost — ${p.label}${t?.profile?` (profile: ${t.profile})`:""}`);let L=new o("Querying month-to-date AWS spend...");L.start();try{let[j,G]=await Promise.all([A.getCostByService({start:p.start,end:p.end,granularity:"DAILY"}),A.getCostByService({start:p.previous.start,end:p.previous.end,granularity:"MONTHLY"})]);L.stop();let ee=Ya(j,G).map((we)=>{let Ce=Ja(we.amount,p.daysElapsed,p.daysInMonth);return[we.service,qn(we.amount),qn(Ce),qn(we.previousAmount),Es(rs(Ce,we.previousAmount))]});c(["Service","MTD","Projected",p.previous.label,"Change"],ee);let re=j.reduce((we,Ce)=>we+Ce.amount,0),oe=G.reduce((we,Ce)=>we+Ce.amount,0),be=Ja(re,p.daysElapsed,p.daysInMonth);e(`
358
- MTD: ${qn(re)} through day ${p.daysElapsed} of ${p.daysInMonth}`),e(`Projected: ${qn(be)} (${Es(rs(be,oe))} vs ${p.previous.label})`)}catch(j){L.stop(),Xa(j)}}),N.command("cost:breakdown","Cost breakdown by service").option("--env <environment>","Environment (production, staging, development)").option("--days <days>","Number of days to analyze",{default:"30"}).option("--no-cache","Skip the local response cache").action(async(t)=>{let p=Number.parseInt(t?.days??"30",10),A;try{A=ko(p)}catch(G){r(G instanceof Error?G.message:String(G));return}let L=new Qe(t?.profile,{useCache:t?.cache!==!1});i(`AWS Cost Breakdown — ${A.current.label}${t?.profile?` (profile: ${t.profile})`:""}`);let j=new o("Comparing AWS service spend...");j.start();try{let[G,Z]=await Promise.all([L.getCostByService({start:A.current.start,end:A.current.end,granularity:"DAILY"}),L.getCostByService({start:A.previous.start,end:A.previous.end,granularity:"DAILY"})]);j.stop();let ee=Ya(G,Z);c(["Service",`Last ${p}d`,`Previous ${p}d`,"Trend"],ee.map((be)=>[be.service,qn(be.amount),qn(be.previousAmount),Es(be.changePercent)]));let re=G.reduce((be,we)=>be+we.amount,0),oe=Z.reduce((be,we)=>be+we.amount,0);e(`
359
- Total: ${qn(re)} (${Es(rs(re,oe))})`)}catch(G){j.stop(),Xa(G)}}),N.command("cost:egress","Rank AWS data-transfer usage types by cost").option("--days <days>","Number of days to analyze",{default:"30"}).option("--no-cache","Skip the local response cache").action(async(t)=>{let p=Number.parseInt(t?.days??"30",10),A;try{A=ko(p).current}catch(G){r(G instanceof Error?G.message:String(G));return}let L=new Qe(t?.profile,{useCache:t?.cache!==!1});i(`AWS Egress Cost — ${A.label}${t?.profile?` (profile: ${t.profile})`:""}`);let j=new o("Querying data-transfer usage types...");j.start();try{let G=Is(await L.getCostByDimension({start:A.start,end:A.end,dimension:"USAGE_TYPE",granularity:"DAILY"}));if(j.stop(),G.length===0){e("No billed data-transfer usage types were found in this period.");return}c(["Usage type","Cost"],G.map((Z)=>[Z.key,qn(Z.amount)])),e(`
360
- Total ranked egress: ${qn(G.reduce((Z,ee)=>Z+ee.amount,0))}`),e("Usage types classify NAT, internet, inter-AZ, and inter-region transfer; destination correlation requires VPC Flow Logs.")}catch(G){j.stop(),Xa(G)}}),N.command("resources","List all resources").option("--env <environment>","Environment (production, staging, development)").option("--type <type>","Resource type (ec2, rds, s3, lambda, etc.)").option("--region <region>","AWS region for regional resources").action(async(t)=>{i(`AWS Resources${t?.profile?` (profile: ${t.profile})`:""}`);let p=new o("Discovering tagged and untagged resources...");p.start();let A=new is(t?.profile,t?.region);try{let L=await A.discover({type:t?.type});if(p.stop(),L.resources.length===0)e("No matching resources were discovered.");else c(["Type","Name","Region","State","Monthly cost"],L.resources.map((j)=>[`${j.service}:${j.type}`,j.name,j.region??"global",j.state??"unknown","— (requires CUR resource IDs)"])),e(`
361
- ${L.resources.length} resource${L.resources.length===1?"":"s"} discovered.`);for(let j of L.warnings)a(j)}catch(L){p.stop(),r(`Resource discovery failed: ${L instanceof Error?L.message:String(L)}`)}}),N.command("resources:unused","Find unused resources").option("--env <environment>","Environment (production, staging, development)").option("--type <type>","Limit analysis to one resource type").option("--region <region>","AWS region for regional resources").action(async(t)=>{i(`Unused AWS Resources${t?.profile?` (profile: ${t.profile})`:""}`);let p=new o("Evaluating inventory and CloudWatch idle signals...");p.start();try{let A=await new Ps(t?.profile,t?.region).unused({type:t?.type});if(p.stop(),A.findings.length===0)e("No resources met the conservative unused-resource thresholds.");else{c(["Resource","Signal","Recommendation","Monthly savings"],A.findings.map((j)=>[`${j.resource.service}:${j.resource.type}/${j.resource.name}`,j.signal,j.recommendation,j.monthlySavings==null?"—":qn(j.monthlySavings)]));let L=A.findings.reduce((j,G)=>j+(G.monthlySavings??0),0);e(`
362
- Known monthly savings: ${A.savingsAvailable?qn(L):"unavailable (enable CUR resource IDs)"}`)}for(let L of A.inventory.warnings)a(L)}catch(A){p.stop(),r(`Unused-resource analysis failed: ${A instanceof Error?A.message:String(A)}`)}}),N.command("optimize","Suggest cost optimizations").option("--env <environment>","Environment (production, staging, development)").option("--type <type>","Limit analysis to one resource type").option("--region <region>","AWS region for regional resources").option("--include-commitments","Include estimated RI and Savings Plan candidates").action(async(t)=>{i(`AWS Cost Optimizations${t?.profile?` (profile: ${t.profile})`:""}`);let p=new o("Combining inventory, CloudWatch signals, and Cost Explorer line items...");p.start();try{let A=await new Ps(t?.profile,t?.region).optimize({type:t?.type,includeCommitments:t?.includeCommitments});if(p.stop(),A.recommendations.length===0)e("No resources met the conservative optimization thresholds.");else{c(["Category","Resource","Current","Projected","Savings","Source signal","Recommendation"],A.recommendations.map((j)=>[j.category,`${j.resource.service}:${j.resource.type}/${j.resource.name}`,j.currentCost==null?"—":qn(j.currentCost),j.projectedCost==null?"—":qn(j.projectedCost),j.monthlySavings==null?"—":qn(j.monthlySavings),j.sourceSignal,j.recommendation]));let L=A.recommendations.reduce((j,G)=>j+(G.monthlySavings??0),0);e(`
363
- Estimated monthly savings: ${A.savingsAvailable?qn(L):"unavailable (enable CUR resource IDs)"}`)}for(let L of A.warnings)a(L);e("Estimates are directional; validate workload peaks and current AWS pricing before applying changes.")}catch(A){p.stop(),r(`Optimization analysis failed: ${A instanceof Error?A.message:String(A)}`)}})}async function Io(N){let t=await Me(),p=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},p))throw Error(`Environment ${p} was not found`);let A=y(process.cwd(),t);return{config:t,env:p,controlPlane:A,controls:new qt(A.store)}}function Po(N){r(N instanceof Error?N.message:String(N)),process.exitCode=1}function Kl(N){let t=N?.infrastructure?.compute??{};return{ddos:t.ddos===!1?void 0:typeof t.ddos==="object"?t.ddos:{ports:[80,443]},waf:t.waf===!1?void 0:typeof t.waf==="object"?t.waf:{mode:"detection"}}}function tc(N){N.command("protect:status","Show the current edge-protection posture").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{try{let p=await Io(t.env),A=p.controls.current(),L=vi(A);if(t.json){e(JSON.stringify({posture:L,controls:p.controls.raw()},null,2));return}if(i("Edge protection"),L.mitigationPaused||L.attackMode)a(L.summary);else n(L.summary);let j=Kl(p.config);if(e(`L3/L4 mitigation: ${j.ddos?"enabled":"disabled in config"}`),e(`WAF: ${j.waf?`enabled (${j.waf.mode??"detection"})`:"disabled in config"}`),e(`Allowlist: ${A.ipRules.allow.length} entry(s)`),e(`Blocklist: ${A.ipRules.block.length} entry(s)`),A.ipRules.allow.length>0||A.ipRules.block.length>0)c(["List","CIDR"],[...A.ipRules.allow.map((G)=>["allow",G]),...A.ipRules.block.map((G)=>["block",G])])}catch(p){Po(p)}}),N.command("protect:attack-mode <state>","Challenge every visitor for a bounded window (on|off)").option("--hours <hours>",`How long before it lifts itself (default ${an}, max ${zt})`).option("--reason <reason>","Why, for the record").option("--env <environment>","Target environment").action(async(t,p)=>{try{let A=await Io(p.env);if(t==="off"){n(A.controls.disableAttackMode()?"Attack mode is off.":"Attack mode was not on.");return}if(t!=="on")throw Error("State must be 'on' or 'off'.");let L=A.controls.enableAttackMode({hours:p.hours==null?void 0:Number(p.hours),reason:p.reason??"Enabled from the CLI."});a("Attack mode is ON. Every visitor is challenged, including legitimate ones."),e(`It lifts itself at ${L.expiresAt} — you do not need to remember.`)}catch(A){Po(A)}}),N.command("protect:pause <state>","Pause or resume automatic mitigation (on|off)").option("--hours <hours>",`How long (max ${zt})`).option("--reason <reason>","Why — required when pausing").option("--env <environment>","Target environment").action(async(t,p)=>{try{let A=await Io(p.env);if(t==="off"){n(A.controls.resumeMitigations()?"Automatic mitigation resumed.":"Mitigation was not paused.");return}if(t!=="on")throw Error("State must be 'on' or 'off'.");if(!p.reason)throw Error("--reason is required: you are liable for the traffic this admits.");let L=A.controls.pauseMitigations({hours:p.hours==null?void 0:Number(p.hours),reason:p.reason});a("Automatic mitigation is PAUSED. All traffic is served, and billed, including attacks."),e(`It resumes automatically at ${L.expiresAt}.`)}catch(A){Po(A)}}),N.command("protect:allow <cidr>","Never rate-limit or block this IP or CIDR").option("--remove","Remove the entry instead").option("--env <environment>","Target environment").action(async(t,p)=>{try{let A=await Io(p.env),L=p.remove?A.controls.removeIpRule("allow",t):A.controls.addIpRule("allow",t);n(`Allowlist now has ${L.allow.length} entry(s).`),e("It reaches the boxes on the next deploy. `cloud protect:apply` shows exactly what will land.")}catch(A){Po(A)}}),N.command("protect:block <cidr>","Drop traffic from this IP or CIDR at the kernel").option("--remove","Remove the entry instead").option("--env <environment>","Target environment").action(async(t,p)=>{try{let A=await Io(p.env),L=p.remove?A.controls.removeIpRule("block",t):A.controls.addIpRule("block",t);n(`Blocklist now has ${L.block.length} entry(s).`),e("It reaches the boxes on the next deploy. `cloud protect:apply` shows exactly what will land.")}catch(A){Po(A)}}),N.command("protect:apply","Print the ruleset the current controls produce").option("--env <environment>","Target environment").option("--waf","Print the WAF configuration instead of the firewall script").action(async(t)=>{try{let p=await Io(t.env),A=Kl(p.config);if(t.waf){if(!A.waf)throw Error("The WAF is disabled in this configuration.");let L=ei(A.waf);for(let j of L.warnings)a(j);e(L.config);return}if(!A.ddos)throw Error("L3/L4 mitigation is disabled in this configuration.");e(mr(cn(A.ddos,p.controls.current())))}catch(p){Po(p)}})}async function Wr(N){let t=await Me(),p=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},p))throw Error(`Environment ${p} was not found`);let A=y(process.cwd(),t),L=A.environments.get(p),j=new pr(A.store),G=new pt(A.store,{encryptionKey:k(process.cwd())});return{config:t,env:p,controlPlane:A,environmentRecord:L,store:j,alerts:G,service:new mt(j),gate:new xt(A.store),organizationId:A.organization.id,projectId:A.project.id}}function Ts(N){if(N==null)return;let t=String(N).trim().replace(/[$,\s]/g,"");if(!t)return;if(/^\d+c$/i.test(t))return Number.parseInt(t,10);let p=Number(t);if(!Number.isFinite(p)||p<0)throw Error(`Invalid amount: ${N}`);return Math.round(p*100)}function Jl(N){if(!N)return;let t=N.split(",").map((p)=>p.trim()).filter(Boolean);for(let p of t)if(!Ui.includes(p))throw Error(`Unknown enforcement action '${p}'. Valid: ${Ui.join(", ")}`);return t}function Yl(N){if(N==null)return"monthly";if(N==="daily"||N==="weekly"||N==="monthly")return N;throw Error(`Unknown period '${N}'. Valid: daily, weekly, monthly`)}function Gm(N){return Hr[N]?.label??N}function Hm(N,t=24){let p=Math.max(0,Math.min(t,Math.round(N/100*t)));return`${"█".repeat(p)}${"░".repeat(t-p)}`}function Gr(N){r(N instanceof Error?N.message:String(N)),process.exitCode=1}function nc(N){N.command("usage","Show current usage, cost, and remaining budget headroom").option("--env <environment>","Target environment").option("--period <period>","daily, weekly, or monthly (default monthly)").option("--timezone <tz>","IANA timezone for period boundaries (default UTC)").option("--json","Print structured JSON").action(async(t)=>{try{let p=await Wr(t.env),A=p.service.usageReport({organizationId:p.organizationId,projectId:p.projectId,period:Yl(t.period),timezone:t.timezone});if(t.json){e(JSON.stringify(A,null,2));return}if(i(`Usage — ${A.window.label}`),e(`Total: ${X(A.totalCents,A.currency)}`),A.byMeter.length===0)e("No metered usage in this window yet.");else c(["Meter","Provider","Quantity","Cost"],A.byMeter.map((L)=>[Gm(L.meter),L.provider||"-",L.quantity.toLocaleString(void 0,{maximumFractionDigits:3}),X(L.costCents,A.currency)]));if(A.budgets.length===0){a("No budgets are configured. Nothing is capping this project."),e("Create one with: cloud budget:create --name 'Monthly' --hard 200");return}if(i("Budgets"),c(["Name","Period","Spent","Limit","Used","Projected","Time to cap","State"],A.budgets.map((L)=>[L.name+(L.dryRun?" (dry run)":""),L.period,X(L.spentCents,A.currency),L.limitCents==null?"-":X(L.limitCents,A.currency),`${Hm(L.usedPercent)} ${L.usedPercent.toFixed(0)}%`,`${X(L.projectedCents,A.currency)} (${(L.projectionConfidence*100).toFixed(0)}% conf)`,L.timeToCap||"-",L.level])),A.enforcement.strongestAction)a(`Enforcement in force: ${A.enforcement.strongestAction}`)}catch(p){Gr(p)}}),N.command("budget:list","List budgets governing this project").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{try{let p=await Wr(t.env),A=p.store.listBudgets({organizationId:p.organizationId,projectId:p.projectId});if(t.json){e(JSON.stringify(A,null,2));return}if(A.length===0){a("No budgets configured.");return}c(["ID","Name","Scope","Period","Soft","Hard","Mode","Enabled"],A.map((L)=>[L.id.slice(0,8),L.name,L.environmentId?"environment":L.projectId?"project":"organization",L.period,L.softLimitCents==null?"-":X(L.softLimitCents,L.currency),L.hardLimitCents==null?"-":X(L.hardLimitCents,L.currency),L.dryRun?"dry run":"enforcing",L.enabled?"yes":"no"]))}catch(p){Gr(p)}}),N.command("budget:create","Create a spend budget").option("--name <name>","Budget name").option("--soft <amount>","Soft limit in dollars (warn only)").option("--hard <amount>","Hard limit in dollars (enforces)").option("--period <period>","daily, weekly, or monthly (default monthly)").option("--timezone <tz>","IANA timezone for period boundaries (default UTC)").option("--scope <scope>","organization, project, or environment (default project)").option("--actions <list>","Comma-separated actions to run at 100% (default notify,block_builds,block_deployments)").option("--grace <seconds>","Seconds a breach must persist before enforcing").option("--dry-run","Evaluate and report but never enforce").option("--env <environment>","Target environment").action(async(t)=>{try{if(!t.name)throw Error("--name is required.");let p=await Wr(t.env),A=t.scope??"project";if(!["organization","project","environment"].includes(A))throw Error(`Unknown scope '${A}'. Valid: organization, project, environment`);let L=Jl(t.actions),j=p.store.createBudget({organizationId:p.organizationId,projectId:A==="organization"?void 0:p.projectId,environmentId:A==="environment"?p.environmentRecord?.id:void 0,name:t.name,period:Yl(t.period),timezone:t.timezone,softLimitCents:Ts(t.soft),hardLimitCents:Ts(t.hard),graceSeconds:t.grace==null?void 0:Number(t.grace),thresholds:L?[{atPercent:80,actions:["notify"]},{atPercent:100,actions:L}]:[...Fi],dryRun:t.dryRun===!0});n(`Created budget '${j.name}' (${j.id}).`),e(`Mode: ${j.dryRun?"dry run — reports only, enforces nothing":"enforcing"}`),e(`Ladder: ${j.thresholds.map((G)=>`${G.atPercent}% → ${G.actions.join("+")}`).join(", ")}`)}catch(p){Gr(p)}}),N.command("budget:update <budgetId>","Update a budget").option("--soft <amount>","Soft limit in dollars").option("--hard <amount>","Hard limit in dollars").option("--enable","Enable the budget").option("--disable","Disable the budget").option("--enforce","Leave dry run and start enforcing").option("--dry-run","Return to dry run").option("--env <environment>","Target environment").action(async(t,p)=>{try{let A=await Wr(p.env),L=A.store.getBudget(t);if(!L)throw Error(`Budget '${t}' was not found.`);let j=A.store.updateBudget(t,{softLimitCents:Ts(p.soft)??L.softLimitCents,hardLimitCents:Ts(p.hard)??L.hardLimitCents,enabled:p.disable?!1:p.enable?!0:L.enabled,dryRun:p.enforce?!1:p.dryRun?!0:L.dryRun});n(`Updated budget '${j.name}'.`),e(`Mode: ${j.dryRun?"dry run":"enforcing"} · Enabled: ${j.enabled?"yes":"no"}`)}catch(A){Gr(A)}}),N.command("budget:delete <budgetId>","Delete a budget and lift anything it was enforcing").option("--env <environment>","Target environment").action(async(t,p)=>{try{let A=await Wr(p.env),L=A.store.getBudget(t);if(!L)throw Error(`Budget '${t}' was not found.`);let j=A.gate.closeBudget(t);if(A.store.deleteBudget(t),n(`Deleted budget '${L.name}'.`),j>0)e(`Lifted ${j} enforcement action(s) it had in force.`)}catch(A){Gr(A)}}),N.command("spend:check","Run one spend cycle now and report what it would do").option("--env <environment>","Target environment").option("--apply","Actually apply enforcement (default is a preview)").option("--json","Print structured JSON").action(async(t)=>{try{let p=await Wr(t.env),A=new Yn({controlPlane:p.controlPlane.store,store:p.store,alerts:t.apply?p.alerts:void 0});if(!t.apply){let G=p.store.budgetsForScope(p.organizationId,p.projectId).map((Z)=>p.service.status(Z));if(t.json){e(JSON.stringify(G,null,2));return}if(i("Spend check (preview — nothing was applied)"),G.length===0){a("No budgets govern this project.");return}c(["Budget","Used","Projected","Level","Would apply"],G.map((Z)=>[Z.budget.name,`${Z.decision.usedPercent.toFixed(1)}%`,`${Z.decision.projectedPercent.toFixed(1)}%`,Z.decision.level,Z.decision.actions.join(", ")||"-"])),e(`
364
- Run with --apply to enforce.`);return}let L=await A.run({organizationId:p.organizationId,projectId:p.projectId,environmentId:p.environmentRecord?.id,environmentKind:p.environmentRecord?.kind});if(t.json){e(JSON.stringify(L,null,2));return}if(i("Spend cycle"),e(`Evaluated ${L.decisions.length} budget(s) in ${L.durationMs}ms.`),L.applied.length>0)a(`Applied: ${L.applied.join(", ")}`);if(L.released.length>0)n(`Released: ${L.released.join(", ")}`);if(L.withheld.length>0)a(`Withheld pending approval: ${L.withheld.join(", ")}`);if(L.anomalies.length>0)a(`${L.anomalies.length} anomaly(ies) detected.`);e(`Notifications queued: ${L.notificationsSent}`);for(let j of L.warnings)a(j);if(L.applied.length===0&&L.released.length===0)n("Nothing to change.")}catch(p){Gr(p)}}),N.command("spend:work","Run the spend loop continuously: meter, evaluate, enforce, notify").option("--env <environment>","Target environment").option("--interval <seconds>",`Seconds between cycles (default ${Zn})`).option("--once","Run a single cycle and exit").action(async(t)=>{try{let p=await Wr(t.env),A=new Yn({controlPlane:p.controlPlane.store,store:p.store,alerts:p.alerts}),L={organizationId:p.organizationId,projectId:p.projectId,environmentId:p.environmentRecord?.id,environmentKind:p.environmentRecord?.kind};if(t.once){let re=await A.run(L);n(`Cycle finished in ${re.durationMs}ms.`);for(let oe of re.warnings)a(oe);return}let j=Math.max(10,Number(t.interval??Zn)),G=new qi(p.controlPlane.store,{owner:`cli:${process.pid}`});i(`Spend worker (every ${j}s)`),e("Press Ctrl+C to stop.");let Z=os(A,{intervalSeconds:j,lease:G,immediate:!0,onResult:(re)=>{let oe=re.flatMap((Ce)=>Ce.applied),be=re.flatMap((Ce)=>Ce.released),we=re.flatMap((Ce)=>Ce.warnings);if(oe.length>0)a(`Applied: ${oe.join(", ")}`);if(be.length>0)n(`Released: ${be.join(", ")}`);for(let Ce of we)a(Ce)},onSkip:(re)=>e(`Skipped: ${re} holds the loop lease.`),onError:(re)=>r(re instanceof Error?re.message:String(re))}),ee=()=>{Z(),e(`
365
- Spend worker stopped; the loop lease was released.`),process.exit(0)};process.on("SIGINT",ee),process.on("SIGTERM",ee),await new Promise(()=>{})}catch(p){Gr(p)}}),N.command("spend:status","Show enforcement currently in force").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{try{let p=await Wr(t.env),A=p.gate.listUnder({organizationId:p.organizationId});if(t.json){e(JSON.stringify(A,null,2));return}if(A.length===0){n("No spend enforcement is in force.");return}c(["Action","Budget","Scope","Mode","Since","Reason"],A.map((L)=>[L.action,L.budgetId.slice(0,8),L.environmentId?"environment":L.projectId?"project":"organization",L.simulated?"dry run":"enforcing",L.appliedAt,L.reason]))}catch(p){Gr(p)}}),N.command("spend:release <budgetId> <action>","Manually lift one enforcement action").option("--env <environment>","Target environment").action(async(t,p,A)=>{try{let L=await Wr(A.env),j=Jl(p)?.[0];if(!j)throw Error("An action is required.");if(!L.gate.close(t,j))throw Error(`${j} was not in force for budget ${t}.`);n(`Lifted ${j} for budget ${t}.`),a("The next spend cycle will re-apply this if spend is still over the threshold.")}catch(L){Gr(L)}}),N.command("anomaly:config","Tune anomaly detection for a signal").option("--signal <signal>","Signal to configure (default cost)").option("--sensitivity <level>","low, medium, or high").option("--season <points>","Points per season: 24 for daily, 168 for weekly").option("--min-delta <amount>","Absolute change below which nothing is reported, in the signal's units").option("--severity <severity>","info, warning, or critical").option("--disable","Stop detecting this signal").option("--enable","Resume detecting this signal").option("--list","List configured signals instead of changing one").option("--signals","List every detectable signal and its data rules").option("--env <environment>","Target environment").action(async(t)=>{try{if(t.signals){c(["Signal","Source","Empty hour","Min samples","Unit"],kt.map((G)=>[G.key,G.source,G.gapPolicy==="zero"?"counts as zero":"ignored",String(G.minSamples),G.unit]));return}let p=await Wr(t.env),A=p.service.anomalyConfigs;if(t.list){let G=A.list({organizationId:p.organizationId,projectId:p.projectId});if(G.length===0){e("No signals are configured; every one uses its shipped defaults.");return}c(["Signal","Enabled","Sensitivity","Season","Min delta","Severity"],G.map((Z)=>[Z.signal,Z.enabled?"yes":"no",Z.sensitivity,String(Z.seasonLength),String(Z.minAbsoluteDelta),Z.severity]));return}let L=t.sensitivity;if(L&&!["low","medium","high"].includes(L))throw Error("--sensitivity must be low, medium, or high.");let j=A.upsert({organizationId:p.organizationId,projectId:p.projectId,signal:t.signal??"cost",sensitivity:L,seasonLength:t.season==null?void 0:Number(t.season),minAbsoluteDelta:t.minDelta==null?void 0:Number(t.minDelta),severity:t.severity,enabled:t.disable?!1:t.enable?!0:void 0});n(`Anomaly detection for '${j.signal}' updated.`),e(`${j.enabled?"Enabled":"Disabled"} · ${j.sensitivity} sensitivity · season ${j.seasonLength} · floor ${j.minAbsoluteDelta} · ${j.severity}`)}catch(p){Gr(p)}}),N.command("anomaly:silence","Skip detection for a signal, route, or status code").option("--signal <signal>","Signal to silence").option("--route <pattern>","Route glob, e.g. /webhooks/**").option("--status <code>","HTTP status code").option("--reason <reason>","Why — required").option("--until <timestamp>","ISO timestamp the silence expires").option("--list","List active silences").option("--remove <id>","Remove a silence").option("--env <environment>","Target environment").action(async(t)=>{try{let p=await Wr(t.env),A=p.service.anomalyConfigs;if(t.remove){n(A.removeSilence(t.remove)?"Silence removed.":"That silence was not found.");return}if(t.list){let j=A.listSilences({organizationId:p.organizationId,projectId:p.projectId});if(j.length===0){n("No active silences.");return}c(["ID","Signal","Route","Status","Expires","Reason"],j.map((G)=>[G.id.slice(0,8),G.signal??"-",G.routePattern??"-",G.statusCode==null?"-":String(G.statusCode),G.expiresAt??"never",G.reason]));return}if(!t.reason)throw Error("--reason is required: someone will ask why this stopped alerting.");let L=A.silence({organizationId:p.organizationId,projectId:p.projectId,signal:t.signal,routePattern:t.route,statusCode:t.status==null?void 0:Number(t.status),reason:t.reason,expiresAt:t.until});if(n(`Silenced (${L.id.slice(0,8)}).`),!L.expiresAt)a("This silence never expires. Pass --until to bound it, or it will outlive the reason for it.")}catch(p){Gr(p)}}),N.command("spend:anomalies","List detected spend anomalies").option("--env <environment>","Target environment").option("--unacknowledged","Only anomalies nobody has acknowledged").option("--json","Print structured JSON").action(async(t)=>{try{let p=await Wr(t.env),A=p.store.listAnomalies({organizationId:p.organizationId,projectId:p.projectId,unacknowledgedOnly:t.unacknowledged===!0});if(t.json){e(JSON.stringify(A,null,2));return}if(A.length===0){n("No anomalies detected.");return}c(["Hour","Signal","Direction","Observed","Expected","Score","Severity"],A.map((L)=>[L.bucketStart,L.signal,L.direction,X(L.observed),X(L.expected),L.score.toFixed(1),L.severity]))}catch(p){Gr(p)}})}import{readFileSync as Km}from"node:fs";var Jm={github:"GITHUB_TOKEN",gitlab:"GITLAB_TOKEN",bitbucket:"BITBUCKET_TOKEN",gitea:"GITEA_TOKEN",generic_https:"GIT_TOKEN"},Ym={github:"https://github.com",gitlab:"https://gitlab.com",bitbucket:"https://bitbucket.org",gitea:"https://gitea.com"};function rc(N,t={}){if(t.provider&&!["github","gitlab","bitbucket","gitea","generic_https","generic_ssh"].includes(t.provider))throw Error(`Unsupported Git provider: ${t.provider}`);let p=t.cloneUrl??(/^(?:https?:\/\/|ssh:\/\/|git@)/.test(N)?N:void 0),A="",L=p??N;if(p?.startsWith("git@")){let Z=/^git@([^:]+):(.+)$/.exec(p);if(!Z)throw Error("SSH repository URL must use git@host:owner/name.git");A=Z[1],L=Z[2]}else if(p){let Z=new URL(p);A=Z.hostname,L=Z.pathname}if(L=L.replace(/^\/+|\/+$/g,"").replace(/\.git$/,""),!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(L))throw Error("Repository must use owner/name or a supported clone URL");let j=t.provider??(A.includes("github")?"github":A.includes("gitlab")?"gitlab":A.includes("bitbucket")?"bitbucket":A.includes("gitea")?"gitea":p?.startsWith("git@")||p?.startsWith("ssh://")?"generic_ssh":p?"generic_https":"github"),G=t.host??Ym[j]??(A?`https://${A}`:"");if(!G)throw Error("A provider host is required");return{provider:j,host:G,fullName:L,cloneUrl:p}}async function Zm(){let N=await Me(),t=y(process.cwd(),N);return{config:N,controlPlane:t,sources:new Kt(t.store,{encryptionKey:k(process.cwd())})}}async function ji(N){let t=await Zm();try{return await N(t)}finally{t.controlPlane.store.close()}}function as(N,t,p){let A=N.listConnections(t);if(!p)return A.length===1?A[0]:void 0;return A.find((L)=>L.id===p||L.name.toLowerCase()===p.toLowerCase())}function Zl(N,t){if(!N)throw Error(`${t} file is required`);return Km(N,"utf8")}async function Qm(N,t,p,A){if(["github","gitlab","bitbucket","gitea"].includes(t.provider)){let j=N.listRepositories(t.id).find((ee)=>ee.fullName.toLowerCase()===p.fullName.toLowerCase());if(j)return j;let Z=(await Pr(N,t.id,{search:p.fullName,maxPages:20})).find((ee)=>ee.fullName.toLowerCase()===p.fullName.toLowerCase());if(!Z)throw Error(`Repository ${p.fullName} is not granted to this connection`);return Z}let L=A.url??p.cloneUrl;if(!L)throw Error("Generic Git connections require --url <clone-url>");return N.upsertRepository({connectionId:t.id,providerRepositoryId:`manual:${p.fullName}`,fullName:p.fullName,cloneUrl:L,defaultBranch:A.branch??"main",visibility:"unknown",archived:!1,metadata:{source:"cli"}})}async function Ql(N,t,p){let A=rc(t,{provider:p.provider,host:p.host,cloneUrl:p.url}),L=as(N.sources,N.controlPlane.organization.id,p.connection);if(!L){if(p.connection)throw Error(`Source connection ${p.connection} was not found`);let G=p.tokenEnv??Jm[A.provider],Z=G?process.env[G]:void 0;if(["github","gitlab","bitbucket","gitea"].includes(A.provider)&&!Z)throw Error(`Set ${G} or pass --token-env <name>; secrets are never accepted as command arguments`);let ee=A.provider==="generic_ssh"?{publicKey:Zl(p.publicKey,"Public key"),privateKey:Zl(p.privateKey,"Private key"),hostKey:p.hostKey??""}:void 0;N.controlPlane.store.database.transaction(()=>{if(L=N.sources.createConnection({organizationId:N.controlPlane.organization.id,provider:A.provider,name:p.name??`${A.provider.replace("_"," ")} · ${new URL(A.host).host}`,host:A.host,owner:A.fullName.split("/")[0],authKind:A.provider==="generic_ssh"?"deploy_key":Z?"access_token":"none",credential:Z?{token:Z}:void 0}),ee)N.sources.createDeployKey({connectionId:L.id,name:p.deployKey??`${A.fullName} deploy key`,...ee,host:p.sshHost??new URL(A.host).hostname,actorId:void 0})})()}if(!L)throw Error("Source connection could not be created");if(L.status==="disconnected")throw Error("The selected source connection is disconnected");let j=await Qm(N.sources,L,A,p);if(p.site){let G=N.controlPlane.environments.get("production")??[...N.controlPlane.environments.values()][0],Z=N.controlPlane.store.listResources(N.controlPlane.project.id,G?.id).find((re)=>re.kind==="application"&&re.slug===p.site);if(!Z)throw Error(`Application ${p.site} was not found`);let ee=L.provider==="generic_ssh"?N.sources.listDeployKeys(L.id)[0]:void 0;N.sources.createBinding({projectId:N.controlPlane.project.id,environmentId:G?.id,resourceId:Z.id,connectionId:L.id,repositoryId:j.id,repositoryFullName:j.fullName,defaultBranch:p.branch??j.defaultBranch,branchRule:p.tag?void 0:p.branch??j.defaultBranch,tagRule:p.tag,monorepoRoot:p.root??".",includePaths:p.include?.split(",").map((re)=>re.trim()).filter(Boolean),excludePaths:p.exclude?.split(",").map((re)=>re.trim()).filter(Boolean),submodules:p.submodules,cloneDepth:p.fullClone?void 0:20,deployKeyId:ee?.id})}return{connection:L,repository:j}}function Bi(N){r(N instanceof Error?N.message:"Git integration command failed")}function ic(N){N.command("git:connections","List encrypted Git provider connections").action(async()=>{try{await ji(async({sources:t,controlPlane:p})=>{let A=t.listConnections(p.organization.id);c(["ID","Name","Provider","Status","Credential","Last sync"],A.map((L)=>[L.id,L.name,L.provider,L.status,L.credentialConfigured?"encrypted":"none",L.lastSyncedAt??"-"]))})}catch(t){Bi(t)}}),N.command("git:repositories","List repositories granted to a connection").option("--connection <id>","Connection ID or name").action(async(t)=>{try{await ji(async({sources:p,controlPlane:A})=>{let L=as(p,A.organization.id,t.connection);if(!L)throw Error("Choose a connection with --connection <id>");let j=p.listRepositories(L.id);c(["ID","Repository","Default branch","Visibility","Archived"],j.map((G)=>[G.id,G.fullName,G.defaultBranch,G.visibility,G.archived?"yes":"no"]))})}catch(p){Bi(p)}}),N.command("git:add <repo>","Connect and optionally bind a Git repository").option("--branch <branch>","Default branch to deploy",{default:"main"}).option("--connection <id>","Reuse a connection ID or name").option("--provider <provider>","github, gitlab, bitbucket, gitea, generic_https, or generic_ssh").option("--host <url>","Provider API host").option("--name <name>","Connection name").option("--token-env <name>","Environment variable containing the access token").option("--url <clone-url>","Credential-free clone URL").option("--site <slug>","Bind the repository to an application").option("--root <path>","Monorepo root",{default:"."}).option("--include <globs>","Comma-separated watched paths").option("--exclude <globs>","Comma-separated ignored paths").option("--tag <glob>","Deploy matching tags instead of branch pushes").option("--full-clone","Use full history instead of a shallow clone").option("--submodules","Initialize repository submodules").option("--deploy-key <name>","Deploy-key display name").option("--public-key <path>","SSH public-key file").option("--private-key <path>","SSH private-key file").option("--host-key <key>","Pinned SSH host key").option("--ssh-host <host>","SSH clone host").option("--yes","Skip confirmation").action(async(t,p)=>{try{if(!p.yes&&!await d(`Connect ${t}?`,!0))return;let A=await ji((L)=>Ql(L,t,p));if(n(`Connected ${A.repository.fullName}`),e(`Connection: ${A.connection.name} (${A.connection.id})`),p.site)e(`Application binding: ${p.site}`)}catch(A){Bi(A)}}),N.command("git:import","Import site.repository entries from cloud.config.ts").option("--connection <id>","Reuse a connection ID or name").option("--token-env <name>","Environment variable containing provider token").option("--yes","Skip confirmation").action(async(t)=>{try{await ji(async(p)=>{let A=Object.entries(p.config.sites??{}).filter(([,L])=>L.repository?.url);if(!A.length)throw Error("No site.repository entries were found");if(!t.yes&&!await d(`Import ${A.length} repository configuration${A.length===1?"":"s"}?`,!0))return;for(let[L,j]of A){let G=j.repository,Z=G.provider==="custom"?void 0:G.provider;await Ql(p,G.url,{connection:t.connection,tokenEnv:t.tokenEnv,site:L,provider:Z,branch:G.branch??"main",tag:G.strategy==="tag"?G.tag??G.tagPattern??"v*":void 0,url:G.url}),n(`Imported ${L} · ${G.url}`)}})}catch(p){Bi(p)}}),N.command("git:branches <repo>","List branches or tags visible to a connection").option("--connection <id>","Connection ID or name").option("--tags","List tags instead of branches").option("--deploy-key <id>","SSH deploy-key ID").action(async(t,p)=>{try{await ji(async({sources:A,controlPlane:L})=>{let j=as(A,L.organization.id,p.connection);if(!j)throw Error("Choose a connection with --connection <id>");let G=A.listRepositories(j.id).find((ee)=>ee.id===t||ee.fullName.toLowerCase()===t.toLowerCase());if(!G)throw Error(`Repository ${t} was not found`);let Z=await Jt(A,{connectionId:j.id,repository:G.fullName,repositoryId:G.id,type:p.tags?"tags":"branches",deployKeyId:p.deployKey??A.listDeployKeys(j.id)[0]?.id});c([p.tags?"Tag":"Branch","Commit","Protected"],Z.refs.map((ee)=>[ee.name,ee.commitSha,ee.protected?"yes":"no"]))})}catch(A){Bi(A)}}),N.command("git:deploy <branch>","Queue an idempotent deployment from a Git branch").option("--binding <id>","Source binding ID").option("--repo <owner/name>","Bound repository").option("--env <environment>","Target environment",{default:"production"}).action(async(t,p)=>{try{await ji(async({sources:A,controlPlane:L})=>{let j=L.environments.get(p.env??"production");if(!j)throw Error(`Environment ${p.env} was not found`);let G=A.listBindings({projectId:L.project.id,status:"active"}).find((oe)=>(!p.binding||oe.id===p.binding)&&(!p.repo||oe.repositoryFullName.toLowerCase()===p.repo.toLowerCase())&&(!oe.environmentId||oe.environmentId===j.id));if(!G)throw Error("No active source binding matched; use --binding or --repo");let ee=(await Jt(A,{connectionId:G.connectionId,repository:G.repositoryFullName,repositoryId:G.repositoryId,type:"branches",deployKeyId:G.deployKeyId})).refs.find((oe)=>oe.name===t);if(!ee)throw Error(`Branch ${t} was not found`);let re=L.store.createOperation({projectId:G.projectId,environmentId:j.id,resourceId:G.resourceId,kind:"deploy.source",idempotencyKey:`cli-source:${G.id}:${ee.commitSha}`,correlationId:`cli-source:${G.id}:${ee.commitSha}`,input:{source:{connectionId:G.connectionId,bindingId:G.id,repository:G.repositoryFullName,commitSha:ee.commitSha,branch:t,monorepoRoot:G.monorepoRoot,submodules:G.submodules,cloneDepth:G.cloneDepth??null}}});n(`Deployment queued: ${re.id}`),e(`${G.repositoryFullName}@${t} · ${ee.commitSha}`)})}catch(A){Bi(A)}}),N.command("git:webhook:add <repo>","Create and reconcile a signed auto-deploy webhook").option("--connection <id>","Connection ID or name").option("--base-url <url>","Public HTTPS dashboard URL").action(async(t,p)=>{try{await ji(async({sources:A,controlPlane:L})=>{let j=as(A,L.organization.id,p.connection);if(!j)throw Error("Choose a connection with --connection <id>");let G=A.listRepositories(j.id).find((be)=>be.id===t||be.fullName.toLowerCase()===t.toLowerCase());if(!G)throw Error(`Repository ${t} was not found`);let Z=p.baseUrl??process.env.TS_CLOUD_WEBHOOK_BASE_URL;if(!Z)throw Error("Pass --base-url or set TS_CLOUD_WEBHOOK_BASE_URL");let ee=A.createWebhook({connectionId:j.id,repositoryId:G.id,repositoryFullName:G.fullName}),re=Ct(Z,ee.webhook),oe=await Xt(A,ee.webhook.id,Z);n(`Webhook ${oe.status}: ${re}`),e("The signing secret is encrypted and is not printed.")})}catch(A){Bi(A)}}),N.command("git:webhook:remove <webhook>","Disable and remove a provider webhook").option("--yes","Skip confirmation").action(async(t,p)=>{try{await ji(async({sources:A,controlPlane:L})=>{let j=A.listConnections(L.organization.id).flatMap((Z)=>A.listWebhooks(Z.id)).find((Z)=>Z.id===t||Z.repositoryFullName.toLowerCase()===t.toLowerCase());if(!j)throw Error(`Webhook ${t} was not found`);if(!p.yes&&!await d(`Remove webhook for ${j.repositoryFullName}?`,!0))return;let G=await wi(A,j.id);n(`Webhook disabled: ${G.id}`)})}catch(A){Bi(A)}}),N.command("git:disconnect <connection>","Preview and disconnect a Git provider connection").option("--yes","Skip confirmation").action(async(t,p)=>{try{await ji(async({sources:A,controlPlane:L})=>{let j=as(A,L.organization.id,t);if(!j)throw Error(`Source connection ${t} was not found`);let G=A.listBindings({connectionId:j.id,status:"active"});e(`${G.length} active binding${G.length===1?"":"s"} will be disabled.`);for(let Z of G)e(` ${Z.repositoryFullName} · ${Z.branchRule??Z.defaultBranch} · ${Z.monorepoRoot}`);if(!p.yes&&!await d(`Disconnect ${j.name}?`,!1))return;A.disconnectConnection(j.id),n(`Disconnected ${j.name}`)})}catch(A){Bi(A)}})}import{readFileSync as ed,writeFileSync as Xm}from"node:fs";import{resolve as Os}from"node:path";async function ep(){let N=await Me(),t=y(process.cwd(),N),p=k(process.cwd());return{config:N,controlPlane:t,drafts:new Dn(t.store),artifacts:new xn(t.store,{cwd:process.cwd()}),registries:new kn(t.store,{encryptionKey:p}),sources:new Kt(t.store,{encryptionKey:p})}}async function ni(N){let t=await ep();try{return await N(t)}finally{t.controlPlane.store.close()}}function dr(N){r(N instanceof Error?N.message:"Application onboarding command failed")}function Xl(N){return JSON.parse(ed(Os(N),"utf8"))}function tp(N){let t=N.site,p=["laravel","php","statamic","wordpress"].includes(t.type),A=["static","spa"].includes(t.type)||!t.start&&!p,L=A?{kind:"static",publishDirectory:t.root??t.webDirectory??"dist",buildCommand:t.build}:{kind:"server",runtime:p?"php":"bun",runtimeVersion:p?t.phpVersion:void 0,installCommand:p?"composer install --no-dev --prefer-dist --optimize-autoloader":void 0,buildCommand:t.build,startCommand:t.start??(p?"php -S 0.0.0.0:$PORT -t public":"bun run start")};return{schemaVersion:1,name:t.name??N.siteSlug,slug:N.siteSlug,projectId:N.projectId,environmentId:N.environmentId,source:N.source??{kind:"local",root:t.root??"."},build:L,runtime:{target:"server",architecture:t.architecture==="arm64"?"arm64":"x86_64",port:A?void 0:Number(t.port??3000),healthCheck:A?void 0:{protocol:"http",path:t.healthCheck?.path??"/health"}},domain:t.domain?{hostname:t.domain,tls:t.ssl?.provider!=="none"}:void 0}}function oc(N){N.command("app:detect [root]","Detect application frameworks without executing project code").action(async(t=".")=>{try{let p=At(Nn(Os(t)));c(["Framework","Strategy","Confidence","Evidence"],p.map((A)=>[A.framework,A.strategy,`${Math.round(A.confidence*100)}%`,A.evidence.map((L)=>L.path).join(", ")||"manual"]));for(let A of p)e(`${A.framework}: ${A.description}`)}catch(p){dr(p)}}),N.command("app:plan <draft>","Validate and preview a secret-free application draft").option("--secrets <names>","Comma-separated configured secret names").action(async(t,p)=>{try{let A=se(Xl(t),p.secrets?.split(",").map((L)=>L.trim()).filter(Boolean));if(e(JSON.stringify(A,null,2)),!A.valid)dr(Error("Plan is not deployable; resolve the reported issues and missing secrets."))}catch(A){dr(A)}}),N.command("app:draft:save <draft>","Persist or update a resumable application draft").option("--id <id>","Existing draft ID").option("--version <version>","Expected draft version").option("--step <step>","Wizard step",{default:"source"}).option("--secrets <names>","Comma-separated configured secret names").action(async(t,p)=>{try{await ni(async({drafts:A,controlPlane:L})=>{let j=Xl(t),G=p.secrets?.split(",").map((ee)=>ee.trim()).filter(Boolean)??[],Z=p.id?A.update(p.id,Number(p.version),{draft:j,step:p.step,suppliedSecretNames:G}):A.create({organizationId:L.organization.id,projectId:L.project.id,name:j.name,draft:{...j,projectId:L.project.id},step:p.step,suppliedSecretNames:G});n(`Draft saved: ${Z.id} · v${Z.version} · ${Z.status}`)})}catch(A){dr(A)}}),N.command("app:drafts","List resumable application drafts").action(async()=>{try{await ni(async({drafts:t,controlPlane:p})=>{let A=t.list(p.project.id);c(["ID","Application","Step","Status","Version","Updated"],A.map((L)=>[L.id,L.input.name,L.step,L.status,String(L.version),L.updatedAt]))})}catch(t){dr(t)}}),N.command("app:export <draft>","Export a deterministic application manifest").option("--output <path>","Output path",{default:"application.manifest.json"}).action(async(t,p)=>{try{await ni(async({drafts:A})=>{let L=A.get(t);if(!L)throw Error(`Draft ${t} was not found`);let j=se(L.input,L.suppliedSecretNames),G=Os(p.output??"application.manifest.json");Xm(G,j.serializedManifest,{mode:384}),n(`Manifest exported: ${G}`)})}catch(A){dr(A)}}),N.command("app:deploy <draft>","Confirm and queue a validated application plan").option("--confirm <environment>","Exact target environment slug").action(async(t,p)=>{try{await ni(async({drafts:A,controlPlane:L})=>{let j=A.get(t);if(!j)throw Error(`Draft ${t} was not found`);if(!p.confirm)throw Error("Pass --confirm <environment> to acknowledge the target and recurring cost drivers");let G=Nr({controlPlane:L.store,drafts:A,draftId:t,expectedVersion:j.version,confirmEnvironment:p.confirm});n(`Application queued: ${G.operation.id}`),e(`Cost drivers: ${G.plan.costDrivers.join(", ")}`)})}catch(A){dr(A)}}),N.command("app:import <site>","Import an existing cloud.config.ts site as a draft").option("--connection <id>","Git connection ID for repository-backed sites").option("--env <environment>","Target environment",{default:"production"}).action(async(t,p)=>{try{await ni(async({config:A,controlPlane:L,sources:j,drafts:G})=>{let Z=A.sites?.[t];if(!Z)throw Error(`Site ${t} was not found`);let ee=L.environments.get(p.env??"production");if(!ee)throw Error(`Environment ${p.env} was not found`);let re;if(Z.repository?.url){if(!p.connection)throw Error("Repository-backed imports require --connection <id>");let we=j.getConnection(p.connection);if(!we)throw Error("Git connection was not found");let Ce=rc(Z.repository.url),Ee=j.listRepositories(we.id).find((Se)=>Se.fullName.toLowerCase()===Ce.fullName.toLowerCase());if(!Ee)throw Error(`Repository ${Ce.fullName} is not synchronized`);re={kind:"git",connectionId:we.id,repositoryId:Ee.id,repositoryFullName:Ee.fullName,ref:Z.repository.branch??"main"}}let oe=tp({site:Z,siteSlug:t,projectId:L.project.id,environmentId:ee.id,source:re}),be=G.create({organizationId:L.organization.id,projectId:L.project.id,name:`Import ${t}`,draft:oe,step:"review"});n(`Imported draft: ${be.id}`)})}catch(A){dr(A)}}),N.command("app:artifact:add <file>","Inspect and store a bounded ZIP or TAR artifact").action(async(t)=>{try{await ni(async({artifacts:p,controlPlane:A})=>{let L=Os(t),j=p.create({organizationId:A.organization.id,projectId:A.project.id,filename:L.split("/").pop(),bytes:ed(L)});n(`Artifact stored: ${j.id} · ${j.entryCount} entries · ${j.sha256}`)})}catch(p){dr(p)}}),N.command("app:registry:add <host>","Create an encrypted private-registry connection").option("--name <name>","Connection name",{default:"Container registry"}).option("--provider <provider>","docker_hub, ghcr, ecr, gcr, or generic",{default:"generic"}).option("--username-env <name>","Username environment variable",{default:"REGISTRY_USERNAME"}).option("--password-env <name>","Password environment variable",{default:"REGISTRY_PASSWORD"}).option("--token-env <name>","Bearer-token environment variable",{default:"REGISTRY_TOKEN"}).action(async(t,p)=>{try{await ni(async({registries:A,controlPlane:L})=>{let j={username:process.env[p.usernameEnv],password:process.env[p.passwordEnv],token:process.env[p.tokenEnv]},G=A.create({organizationId:L.organization.id,provider:p.provider,name:p.name,host:t,credential:Object.values(j).some(Boolean)?j:void 0});n(`Registry connection created: ${G.id}`)})}catch(A){dr(A)}}),N.command("app:registries","List safe registry connection metadata").action(async()=>{try{await ni(async({registries:t,controlPlane:p})=>c(["ID","Name","Provider","Host","Status"],t.list(p.organization.id).map((A)=>[A.id,A.name,A.provider,A.host,A.status])))}catch(t){dr(t)}}),N.command("app:registry:test <id>","Test registry and optional image pull access").option("--image <image>","Image reference").action(async(t,p)=>{try{await ni(async({registries:A})=>{let L=await A.test(t,{image:p.image});e(`${L.status}: ${L.healthMessage}`)})}catch(A){dr(A)}}),N.command("app:registry:rotate <id>","Rotate write-only registry credentials").option("--username-env <name>","Username environment variable",{default:"REGISTRY_USERNAME"}).option("--password-env <name>","Password environment variable",{default:"REGISTRY_PASSWORD"}).option("--token-env <name>","Bearer-token environment variable",{default:"REGISTRY_TOKEN"}).option("--expires <date>","Credential expiry timestamp").action(async(t,p)=>{try{await ni(async({registries:A})=>{let L={username:process.env[p.usernameEnv],password:process.env[p.passwordEnv],token:process.env[p.tokenEnv]};if(!Object.values(L).some(Boolean))throw Error("Set at least one configured registry credential environment variable");let j=A.rotate(t,L,{expiresAt:p.expires?new Date(p.expires).toISOString():void 0});n(`Registry credential rotated: ${j.id} · ${j.status}`)})}catch(A){dr(A)}}),N.command("app:registry:disconnect <id>","Disconnect and erase registry credentials").option("--yes","Skip confirmation").action(async(t,p)=>{try{await ni(async({registries:A,drafts:L,controlPlane:j})=>{let G=A.get(t);if(!G)throw Error(`Registry ${t} was not found`);let Z=L.list(j.project.id).filter((ee)=>ee.input.source.kind==="image"&&ee.input.source.registryConnectionId===t||ee.input.build.kind==="prebuilt_image"&&ee.input.build.registryConnectionId===t);if(e(`${Z.length} application draft${Z.length===1?"":"s"} reference this registry.`),!p.yes&&!await d(`Disconnect ${G.name}?`,!1))return;A.disconnect(t),n(`Disconnected ${G.name}`)})}catch(A){dr(A)}})}import{existsSync as np,readFileSync as rp,writeFileSync as ip}from"node:fs";function sc(N,t){r(`'${N}' is not implemented yet.`),e(t),process.exitCode=1}function op(N){let t={};for(let p of N.split(`
366
- `)){let A=p.trim();if(!A||A.startsWith("#"))continue;let L=A.indexOf("=");if(L===-1)continue;let j=A.slice(0,L).trim(),G=A.slice(L+1).trim();if(G.startsWith('"')&&G.endsWith('"')||G.startsWith("'")&&G.endsWith("'"))G=G.slice(1,-1);t[j]=G}return t}function sp(N){return Object.entries(N).sort(([p],[A])=>p.localeCompare(A)).map(([p,A])=>{if(!/[\s#"']/.test(A))return`${p}=${A}`;let L='"'+A.replace(/"/g,"\\\"")+'"';return`${p}=${L}`}).join(`
353
+ `}function dc(N){N.command("cost:analyze","Rank AWS services by cost for the last full month").option("--output","Also write a markdown report to ./aws.md").option("--no-cache","Skip the local response cache (always hit AWS — costs $0.01)").action(async(t)=>{let m=t?.profile,A=t?.cache!==!1,{start:L,end:j,label:G}=tm();i(`Cost Analysis — ${G}${m?` (profile: ${m})`:""}`);let Z=new o("Querying AWS Cost Explorer...");Z.start();let ee=new Fe(m,{useCache:A}),ne;try{ne=await ee.getCostByService({start:L,end:j})}catch(Se){Z.stop(),r(`Cost Explorer request failed: ${Se?.message??Se}`),e(`
354
+ This command needs the ce:GetCostAndUsage IAM permission.`);return}let oe=null,be=!1;if(ne.some((Se)=>Se.service===cc))try{oe=(await new g("us-east-1",m).listBuckets()).Buckets?.length??0}catch{be=!0}if(Z.stop(),ne.length===0){e("No billed services in this period.");return}let we=ne.reduce((Se,xe)=>Se+xe.amount,0),Ce=ne.map((Se)=>{let xe="-";if(Se.service===cc){if(be)xe="unknown (no s3:ListAllMyBuckets)";else if(oe!==null)xe=`${oe} bucket${oe===1?"":"s"}`}let Me=we>0?`${(Se.amount/we*100).toFixed(1)}%`:"—";return[Se.service,xe,qn(Se.amount),Me]});if(c(["Service","Resources","Cost","% of Total"],Ce),e(`
355
+ Total: ${qn(we)} across ${ne.length} service${ne.length===1?"":"s"}`),ee.lastCacheAgeSeconds!==null)e(`(cached, ${ee.lastCacheAgeSeconds}s old — pass --no-cache to refresh)`);if((ne.find((Se)=>Se.service===cc)?.amount??0)>1&&oe===0)a(`
356
+ S3 has spend but listBuckets returned 0. The buckets are likely owned by a different account in your AWS Organization (consolidated billing rolls up to the payer account, but ListBuckets only shows buckets owned by the calling account).`);if(t?.output){let Se=`${process.cwd()}/aws.md`;await Bun.write(Se,nm({label:G,profile:m,rows:Ce,total:we,count:ne.length})),n(`
357
+ Wrote ${Se}`)}}),N.command("cost:cache:clear","Wipe the local Cost Explorer response cache").option("--all","Wipe entries for every profile (default: just the current profile)").action(async(t)=>{let m=t?.all?void 0:t?.profile,A=Aa(m),L=Ca(m);e(`Cleared ${L.deletedFiles} cached response${L.deletedFiles===1?"":"s"} for ${L.scope}`),e(`(${A})`)}),N.command("cost","Show current month-to-date AWS spend and projected total").option("--env <environment>","Environment (production, staging, development)").option("--no-cache","Skip the local response cache").action(async(t)=>{let m=nd(),A=new Fe(t?.profile,{useCache:t?.cache!==!1});i(`AWS Cost — ${m.label}${t?.profile?` (profile: ${t.profile})`:""}`);let L=new o("Querying month-to-date AWS spend...");L.start();try{let[j,G]=await Promise.all([A.getCostByService({start:m.start,end:m.end,granularity:"DAILY"}),A.getCostByService({start:m.previous.start,end:m.previous.end,granularity:"MONTHLY"})]);L.stop();let ee=sc(j,G).map((we)=>{let Ce=oc(we.amount,m.daysElapsed,m.daysInMonth);return[we.service,qn(we.amount),qn(Ce),qn(we.previousAmount),Fs(as(Ce,we.previousAmount))]});c(["Service","MTD","Projected",m.previous.label,"Change"],ee);let ne=j.reduce((we,Ce)=>we+Ce.amount,0),oe=G.reduce((we,Ce)=>we+Ce.amount,0),be=oc(ne,m.daysElapsed,m.daysInMonth);e(`
358
+ MTD: ${qn(ne)} through day ${m.daysElapsed} of ${m.daysInMonth}`),e(`Projected: ${qn(be)} (${Fs(as(be,oe))} vs ${m.previous.label})`)}catch(j){L.stop(),lc(j)}}),N.command("cost:breakdown","Cost breakdown by service").option("--env <environment>","Environment (production, staging, development)").option("--days <days>","Number of days to analyze",{default:"30"}).option("--no-cache","Skip the local response cache").action(async(t)=>{let m=Number.parseInt(t?.days??"30",10),A;try{A=xo(m)}catch(G){r(G instanceof Error?G.message:String(G));return}let L=new Fe(t?.profile,{useCache:t?.cache!==!1});i(`AWS Cost Breakdown — ${A.current.label}${t?.profile?` (profile: ${t.profile})`:""}`);let j=new o("Comparing AWS service spend...");j.start();try{let[G,Z]=await Promise.all([L.getCostByService({start:A.current.start,end:A.current.end,granularity:"DAILY"}),L.getCostByService({start:A.previous.start,end:A.previous.end,granularity:"DAILY"})]);j.stop();let ee=sc(G,Z);c(["Service",`Last ${m}d`,`Previous ${m}d`,"Trend"],ee.map((be)=>[be.service,qn(be.amount),qn(be.previousAmount),Fs(be.changePercent)]));let ne=G.reduce((be,we)=>be+we.amount,0),oe=Z.reduce((be,we)=>be+we.amount,0);e(`
359
+ Total: ${qn(ne)} (${Fs(as(ne,oe))})`)}catch(G){j.stop(),lc(G)}}),N.command("cost:egress","Rank AWS data-transfer usage types by cost").option("--days <days>","Number of days to analyze",{default:"30"}).option("--no-cache","Skip the local response cache").action(async(t)=>{let m=Number.parseInt(t?.days??"30",10),A;try{A=xo(m).current}catch(G){r(G instanceof Error?G.message:String(G));return}let L=new Fe(t?.profile,{useCache:t?.cache!==!1});i(`AWS Egress Cost — ${A.label}${t?.profile?` (profile: ${t.profile})`:""}`);let j=new o("Querying data-transfer usage types...");j.start();try{let G=Ls(await L.getCostByDimension({start:A.start,end:A.end,dimension:"USAGE_TYPE",granularity:"DAILY"}));if(j.stop(),G.length===0){e("No billed data-transfer usage types were found in this period.");return}c(["Usage type","Cost"],G.map((Z)=>[Z.key,qn(Z.amount)])),e(`
360
+ Total ranked egress: ${qn(G.reduce((Z,ee)=>Z+ee.amount,0))}`),e("Usage types classify NAT, internet, inter-AZ, and inter-region transfer; destination correlation requires VPC Flow Logs.")}catch(G){j.stop(),lc(G)}}),N.command("resources","List all resources").option("--env <environment>","Environment (production, staging, development)").option("--type <type>","Resource type (ec2, rds, s3, lambda, etc.)").option("--region <region>","AWS region for regional resources").action(async(t)=>{i(`AWS Resources${t?.profile?` (profile: ${t.profile})`:""}`);let m=new o("Discovering tagged and untagged resources...");m.start();let A=new cs(t?.profile,t?.region);try{let L=await A.discover({type:t?.type});if(m.stop(),L.resources.length===0)e("No matching resources were discovered.");else c(["Type","Name","Region","State","Monthly cost"],L.resources.map((j)=>[`${j.service}:${j.type}`,j.name,j.region??"global",j.state??"unknown","— (requires CUR resource IDs)"])),e(`
361
+ ${L.resources.length} resource${L.resources.length===1?"":"s"} discovered.`);for(let j of L.warnings)a(j)}catch(L){m.stop(),r(`Resource discovery failed: ${L instanceof Error?L.message:String(L)}`)}}),N.command("resources:unused","Find unused resources").option("--env <environment>","Environment (production, staging, development)").option("--type <type>","Limit analysis to one resource type").option("--region <region>","AWS region for regional resources").action(async(t)=>{i(`Unused AWS Resources${t?.profile?` (profile: ${t.profile})`:""}`);let m=new o("Evaluating inventory and CloudWatch idle signals...");m.start();try{let A=await new js(t?.profile,t?.region).unused({type:t?.type});if(m.stop(),A.findings.length===0)e("No resources met the conservative unused-resource thresholds.");else{c(["Resource","Signal","Recommendation","Monthly savings"],A.findings.map((j)=>[`${j.resource.service}:${j.resource.type}/${j.resource.name}`,j.signal,j.recommendation,j.monthlySavings==null?"—":qn(j.monthlySavings)]));let L=A.findings.reduce((j,G)=>j+(G.monthlySavings??0),0);e(`
362
+ Known monthly savings: ${A.savingsAvailable?qn(L):"unavailable (enable CUR resource IDs)"}`)}for(let L of A.inventory.warnings)a(L)}catch(A){m.stop(),r(`Unused-resource analysis failed: ${A instanceof Error?A.message:String(A)}`)}}),N.command("optimize","Suggest cost optimizations").option("--env <environment>","Environment (production, staging, development)").option("--type <type>","Limit analysis to one resource type").option("--region <region>","AWS region for regional resources").option("--include-commitments","Include estimated RI and Savings Plan candidates").action(async(t)=>{i(`AWS Cost Optimizations${t?.profile?` (profile: ${t.profile})`:""}`);let m=new o("Combining inventory, CloudWatch signals, and Cost Explorer line items...");m.start();try{let A=await new js(t?.profile,t?.region).optimize({type:t?.type,includeCommitments:t?.includeCommitments});if(m.stop(),A.recommendations.length===0)e("No resources met the conservative optimization thresholds.");else{c(["Category","Resource","Current","Projected","Savings","Source signal","Recommendation"],A.recommendations.map((j)=>[j.category,`${j.resource.service}:${j.resource.type}/${j.resource.name}`,j.currentCost==null?"—":qn(j.currentCost),j.projectedCost==null?"—":qn(j.projectedCost),j.monthlySavings==null?"—":qn(j.monthlySavings),j.sourceSignal,j.recommendation]));let L=A.recommendations.reduce((j,G)=>j+(G.monthlySavings??0),0);e(`
363
+ Estimated monthly savings: ${A.savingsAvailable?qn(L):"unavailable (enable CUR resource IDs)"}`)}for(let L of A.warnings)a(L);e("Estimates are directional; validate workload peaks and current AWS pricing before applying changes.")}catch(A){m.stop(),r(`Optimization analysis failed: ${A instanceof Error?A.message:String(A)}`)}})}async function Eo(N){let t=await Ne(),m=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},m))throw Error(`Environment ${m} was not found`);let A=y(process.cwd(),t);return{config:t,env:m,controlPlane:A,controls:new qt(A.store)}}function No(N){r(N instanceof Error?N.message:String(N)),process.exitCode=1}function id(N){let t=N?.infrastructure?.compute??{};return{ddos:t.ddos===!1?void 0:typeof t.ddos==="object"?t.ddos:{ports:[80,443]},waf:t.waf===!1?void 0:typeof t.waf==="object"?t.waf:{mode:"detection"}}}function uc(N){N.command("protect:status","Show the current edge-protection posture").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{try{let m=await Eo(t.env),A=m.controls.current(),L=wi(A);if(t.json){e(JSON.stringify({posture:L,controls:m.controls.raw()},null,2));return}if(i("Edge protection"),L.mitigationPaused||L.attackMode)a(L.summary);else n(L.summary);let j=id(m.config);if(e(`L3/L4 mitigation: ${j.ddos?"enabled":"disabled in config"}`),e(`WAF: ${j.waf?`enabled (${j.waf.mode??"detection"})`:"disabled in config"}`),e(`Allowlist: ${A.ipRules.allow.length} entry(s)`),e(`Blocklist: ${A.ipRules.block.length} entry(s)`),A.ipRules.allow.length>0||A.ipRules.block.length>0)c(["List","CIDR"],[...A.ipRules.allow.map((G)=>["allow",G]),...A.ipRules.block.map((G)=>["block",G])])}catch(m){No(m)}}),N.command("protect:attack-mode <state>","Challenge every visitor for a bounded window (on|off)").option("--hours <hours>",`How long before it lifts itself (default ${cn}, max ${Ht})`).option("--reason <reason>","Why, for the record").option("--env <environment>","Target environment").action(async(t,m)=>{try{let A=await Eo(m.env);if(t==="off"){n(A.controls.disableAttackMode()?"Attack mode is off.":"Attack mode was not on.");return}if(t!=="on")throw Error("State must be 'on' or 'off'.");let L=A.controls.enableAttackMode({hours:m.hours==null?void 0:Number(m.hours),reason:m.reason??"Enabled from the CLI."});a("Attack mode is ON. Every visitor is challenged, including legitimate ones."),e(`It lifts itself at ${L.expiresAt} — you do not need to remember.`)}catch(A){No(A)}}),N.command("protect:pause <state>","Pause or resume automatic mitigation (on|off)").option("--hours <hours>",`How long (max ${Ht})`).option("--reason <reason>","Why — required when pausing").option("--env <environment>","Target environment").action(async(t,m)=>{try{let A=await Eo(m.env);if(t==="off"){n(A.controls.resumeMitigations()?"Automatic mitigation resumed.":"Mitigation was not paused.");return}if(t!=="on")throw Error("State must be 'on' or 'off'.");if(!m.reason)throw Error("--reason is required: you are liable for the traffic this admits.");let L=A.controls.pauseMitigations({hours:m.hours==null?void 0:Number(m.hours),reason:m.reason});a("Automatic mitigation is PAUSED. All traffic is served, and billed, including attacks."),e(`It resumes automatically at ${L.expiresAt}.`)}catch(A){No(A)}}),N.command("protect:allow <cidr>","Never rate-limit or block this IP or CIDR").option("--remove","Remove the entry instead").option("--env <environment>","Target environment").action(async(t,m)=>{try{let A=await Eo(m.env),L=m.remove?A.controls.removeIpRule("allow",t):A.controls.addIpRule("allow",t);n(`Allowlist now has ${L.allow.length} entry(s).`),e("It reaches the boxes on the next deploy. `cloud protect:apply` shows exactly what will land.")}catch(A){No(A)}}),N.command("protect:block <cidr>","Drop traffic from this IP or CIDR at the kernel").option("--remove","Remove the entry instead").option("--env <environment>","Target environment").action(async(t,m)=>{try{let A=await Eo(m.env),L=m.remove?A.controls.removeIpRule("block",t):A.controls.addIpRule("block",t);n(`Blocklist now has ${L.block.length} entry(s).`),e("It reaches the boxes on the next deploy. `cloud protect:apply` shows exactly what will land.")}catch(A){No(A)}}),N.command("protect:apply","Print the ruleset the current controls produce").option("--env <environment>","Target environment").option("--waf","Print the WAF configuration instead of the firewall script").action(async(t)=>{try{let m=await Eo(t.env),A=id(m.config);if(t.waf){if(!A.waf)throw Error("The WAF is disabled in this configuration.");let L=ri(A.waf);for(let j of L.warnings)a(j);e(L.config);return}if(!A.ddos)throw Error("L3/L4 mitigation is disabled in this configuration.");e(gr(ln(A.ddos,m.controls.current())))}catch(m){No(m)}})}async function Jr(N){let t=await Ne(),m=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},m))throw Error(`Environment ${m} was not found`);let A=y(process.cwd(),t),L=A.environments.get(m),j=new mr(A.store),G=new pt(A.store,{encryptionKey:k(process.cwd())});return{config:t,env:m,controlPlane:A,environmentRecord:L,store:j,alerts:G,service:new mt(j),gate:new _t(A.store),organizationId:A.organization.id,projectId:A.project.id}}function Bs(N){if(N==null)return;let t=String(N).trim().replace(/[$,\s]/g,"");if(!t)return;if(/^\d+c$/i.test(t))return Number.parseInt(t,10);let m=Number(t);if(!Number.isFinite(m)||m<0)throw Error(`Invalid amount: ${N}`);return Math.round(m*100)}function od(N){if(!N)return;let t=N.split(",").map((m)=>m.trim()).filter(Boolean);for(let m of t)if(!ji.includes(m))throw Error(`Unknown enforcement action '${m}'. Valid: ${ji.join(", ")}`);return t}function sd(N){if(N==null)return"monthly";if(N==="daily"||N==="weekly"||N==="monthly")return N;throw Error(`Unknown period '${N}'. Valid: daily, weekly, monthly`)}function rm(N){return Gr[N]?.label??N}function im(N,t=24){let m=Math.max(0,Math.min(t,Math.round(N/100*t)));return`${"█".repeat(m)}${"░".repeat(t-m)}`}function Yr(N){r(N instanceof Error?N.message:String(N)),process.exitCode=1}function pc(N){N.command("usage","Show current usage, cost, and remaining budget headroom").option("--env <environment>","Target environment").option("--period <period>","daily, weekly, or monthly (default monthly)").option("--timezone <tz>","IANA timezone for period boundaries (default UTC)").option("--json","Print structured JSON").action(async(t)=>{try{let m=await Jr(t.env),A=m.service.usageReport({organizationId:m.organizationId,projectId:m.projectId,period:sd(t.period),timezone:t.timezone});if(t.json){e(JSON.stringify(A,null,2));return}if(i(`Usage — ${A.window.label}`),e(`Total: ${Q(A.totalCents,A.currency)}`),A.byMeter.length===0)e("No metered usage in this window yet.");else c(["Meter","Provider","Quantity","Cost"],A.byMeter.map((L)=>[rm(L.meter),L.provider||"-",L.quantity.toLocaleString(void 0,{maximumFractionDigits:3}),Q(L.costCents,A.currency)]));if(A.budgets.length===0){a("No budgets are configured. Nothing is capping this project."),e("Create one with: cloud budget:create --name 'Monthly' --hard 200");return}if(i("Budgets"),c(["Name","Period","Spent","Limit","Used","Projected","Time to cap","State"],A.budgets.map((L)=>[L.name+(L.dryRun?" (dry run)":""),L.period,Q(L.spentCents,A.currency),L.limitCents==null?"-":Q(L.limitCents,A.currency),`${im(L.usedPercent)} ${L.usedPercent.toFixed(0)}%`,`${Q(L.projectedCents,A.currency)} (${(L.projectionConfidence*100).toFixed(0)}% conf)`,L.timeToCap||"-",L.level])),A.enforcement.strongestAction)a(`Enforcement in force: ${A.enforcement.strongestAction}`)}catch(m){Yr(m)}}),N.command("budget:list","List budgets governing this project").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{try{let m=await Jr(t.env),A=m.store.listBudgets({organizationId:m.organizationId,projectId:m.projectId});if(t.json){e(JSON.stringify(A,null,2));return}if(A.length===0){a("No budgets configured.");return}c(["ID","Name","Scope","Period","Soft","Hard","Mode","Enabled"],A.map((L)=>[L.id.slice(0,8),L.name,L.environmentId?"environment":L.projectId?"project":"organization",L.period,L.softLimitCents==null?"-":Q(L.softLimitCents,L.currency),L.hardLimitCents==null?"-":Q(L.hardLimitCents,L.currency),L.dryRun?"dry run":"enforcing",L.enabled?"yes":"no"]))}catch(m){Yr(m)}}),N.command("budget:create","Create a spend budget").option("--name <name>","Budget name").option("--soft <amount>","Soft limit in dollars (warn only)").option("--hard <amount>","Hard limit in dollars (enforces)").option("--period <period>","daily, weekly, or monthly (default monthly)").option("--timezone <tz>","IANA timezone for period boundaries (default UTC)").option("--scope <scope>","organization, project, or environment (default project)").option("--actions <list>","Comma-separated actions to run at 100% (default notify,block_builds,block_deployments)").option("--grace <seconds>","Seconds a breach must persist before enforcing").option("--dry-run","Evaluate and report but never enforce").option("--env <environment>","Target environment").action(async(t)=>{try{if(!t.name)throw Error("--name is required.");let m=await Jr(t.env),A=t.scope??"project";if(!["organization","project","environment"].includes(A))throw Error(`Unknown scope '${A}'. Valid: organization, project, environment`);let L=od(t.actions),j=m.store.createBudget({organizationId:m.organizationId,projectId:A==="organization"?void 0:m.projectId,environmentId:A==="environment"?m.environmentRecord?.id:void 0,name:t.name,period:sd(t.period),timezone:t.timezone,softLimitCents:Bs(t.soft),hardLimitCents:Bs(t.hard),graceSeconds:t.grace==null?void 0:Number(t.grace),thresholds:L?[{atPercent:80,actions:["notify"]},{atPercent:100,actions:L}]:[...zi],dryRun:t.dryRun===!0});n(`Created budget '${j.name}' (${j.id}).`),e(`Mode: ${j.dryRun?"dry run — reports only, enforces nothing":"enforcing"}`),e(`Ladder: ${j.thresholds.map((G)=>`${G.atPercent}% → ${G.actions.join("+")}`).join(", ")}`)}catch(m){Yr(m)}}),N.command("budget:update <budgetId>","Update a budget").option("--soft <amount>","Soft limit in dollars").option("--hard <amount>","Hard limit in dollars").option("--enable","Enable the budget").option("--disable","Disable the budget").option("--enforce","Leave dry run and start enforcing").option("--dry-run","Return to dry run").option("--env <environment>","Target environment").action(async(t,m)=>{try{let A=await Jr(m.env),L=A.store.getBudget(t);if(!L)throw Error(`Budget '${t}' was not found.`);let j=A.store.updateBudget(t,{softLimitCents:Bs(m.soft)??L.softLimitCents,hardLimitCents:Bs(m.hard)??L.hardLimitCents,enabled:m.disable?!1:m.enable?!0:L.enabled,dryRun:m.enforce?!1:m.dryRun?!0:L.dryRun});n(`Updated budget '${j.name}'.`),e(`Mode: ${j.dryRun?"dry run":"enforcing"} · Enabled: ${j.enabled?"yes":"no"}`)}catch(A){Yr(A)}}),N.command("budget:delete <budgetId>","Delete a budget and lift anything it was enforcing").option("--env <environment>","Target environment").action(async(t,m)=>{try{let A=await Jr(m.env),L=A.store.getBudget(t);if(!L)throw Error(`Budget '${t}' was not found.`);let j=A.gate.closeBudget(t);if(A.store.deleteBudget(t),n(`Deleted budget '${L.name}'.`),j>0)e(`Lifted ${j} enforcement action(s) it had in force.`)}catch(A){Yr(A)}}),N.command("spend:check","Run one spend cycle now and report what it would do").option("--env <environment>","Target environment").option("--apply","Actually apply enforcement (default is a preview)").option("--json","Print structured JSON").action(async(t)=>{try{let m=await Jr(t.env),A=new ei({controlPlane:m.controlPlane.store,store:m.store,alerts:t.apply?m.alerts:void 0});if(!t.apply){let G=m.store.budgetsForScope(m.organizationId,m.projectId).map((Z)=>m.service.status(Z));if(t.json){e(JSON.stringify(G,null,2));return}if(i("Spend check (preview — nothing was applied)"),G.length===0){a("No budgets govern this project.");return}c(["Budget","Used","Projected","Level","Would apply"],G.map((Z)=>[Z.budget.name,`${Z.decision.usedPercent.toFixed(1)}%`,`${Z.decision.projectedPercent.toFixed(1)}%`,Z.decision.level,Z.decision.actions.join(", ")||"-"])),e(`
364
+ Run with --apply to enforce.`);return}let L=await A.run({organizationId:m.organizationId,projectId:m.projectId,environmentId:m.environmentRecord?.id,environmentKind:m.environmentRecord?.kind});if(t.json){e(JSON.stringify(L,null,2));return}if(i("Spend cycle"),e(`Evaluated ${L.decisions.length} budget(s) in ${L.durationMs}ms.`),L.applied.length>0)a(`Applied: ${L.applied.join(", ")}`);if(L.released.length>0)n(`Released: ${L.released.join(", ")}`);if(L.withheld.length>0)a(`Withheld pending approval: ${L.withheld.join(", ")}`);if(L.anomalies.length>0)a(`${L.anomalies.length} anomaly(ies) detected.`);e(`Notifications queued: ${L.notificationsSent}`);for(let j of L.warnings)a(j);if(L.applied.length===0&&L.released.length===0)n("Nothing to change.")}catch(m){Yr(m)}}),N.command("spend:work","Run the spend loop continuously: meter, evaluate, enforce, notify").option("--env <environment>","Target environment").option("--interval <seconds>",`Seconds between cycles (default ${ti})`).option("--once","Run a single cycle and exit").action(async(t)=>{try{let m=await Jr(t.env),A=new ei({controlPlane:m.controlPlane.store,store:m.store,alerts:m.alerts}),L={organizationId:m.organizationId,projectId:m.projectId,environmentId:m.environmentRecord?.id,environmentKind:m.environmentRecord?.kind};if(t.once){let ne=await A.run(L);n(`Cycle finished in ${ne.durationMs}ms.`);for(let oe of ne.warnings)a(oe);return}let j=Math.max(10,Number(t.interval??ti)),G=new Gi(m.controlPlane.store,{owner:`cli:${process.pid}`});i(`Spend worker (every ${j}s)`),e("Press Ctrl+C to stop.");let Z=ds(A,{intervalSeconds:j,lease:G,immediate:!0,onResult:(ne)=>{let oe=ne.flatMap((Ce)=>Ce.applied),be=ne.flatMap((Ce)=>Ce.released),we=ne.flatMap((Ce)=>Ce.warnings);if(oe.length>0)a(`Applied: ${oe.join(", ")}`);if(be.length>0)n(`Released: ${be.join(", ")}`);for(let Ce of we)a(Ce)},onSkip:(ne)=>e(`Skipped: ${ne} holds the loop lease.`),onError:(ne)=>r(ne instanceof Error?ne.message:String(ne))}),ee=()=>{Z(),e(`
365
+ Spend worker stopped; the loop lease was released.`),process.exit(0)};process.on("SIGINT",ee),process.on("SIGTERM",ee),await new Promise(()=>{})}catch(m){Yr(m)}}),N.command("spend:status","Show enforcement currently in force").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{try{let m=await Jr(t.env),A=m.gate.listUnder({organizationId:m.organizationId});if(t.json){e(JSON.stringify(A,null,2));return}if(A.length===0){n("No spend enforcement is in force.");return}c(["Action","Budget","Scope","Mode","Since","Reason"],A.map((L)=>[L.action,L.budgetId.slice(0,8),L.environmentId?"environment":L.projectId?"project":"organization",L.simulated?"dry run":"enforcing",L.appliedAt,L.reason]))}catch(m){Yr(m)}}),N.command("spend:release <budgetId> <action>","Manually lift one enforcement action").option("--env <environment>","Target environment").action(async(t,m,A)=>{try{let L=await Jr(A.env),j=od(m)?.[0];if(!j)throw Error("An action is required.");if(!L.gate.close(t,j))throw Error(`${j} was not in force for budget ${t}.`);n(`Lifted ${j} for budget ${t}.`),a("The next spend cycle will re-apply this if spend is still over the threshold.")}catch(L){Yr(L)}}),N.command("anomaly:config","Tune anomaly detection for a signal").option("--signal <signal>","Signal to configure (default cost)").option("--sensitivity <level>","low, medium, or high").option("--season <points>","Points per season: 24 for daily, 168 for weekly").option("--min-delta <amount>","Absolute change below which nothing is reported, in the signal's units").option("--severity <severity>","info, warning, or critical").option("--disable","Stop detecting this signal").option("--enable","Resume detecting this signal").option("--list","List configured signals instead of changing one").option("--signals","List every detectable signal and its data rules").option("--env <environment>","Target environment").action(async(t)=>{try{if(t.signals){c(["Signal","Source","Empty hour","Min samples","Unit"],kt.map((G)=>[G.key,G.source,G.gapPolicy==="zero"?"counts as zero":"ignored",String(G.minSamples),G.unit]));return}let m=await Jr(t.env),A=m.service.anomalyConfigs;if(t.list){let G=A.list({organizationId:m.organizationId,projectId:m.projectId});if(G.length===0){e("No signals are configured; every one uses its shipped defaults.");return}c(["Signal","Enabled","Sensitivity","Season","Min delta","Severity"],G.map((Z)=>[Z.signal,Z.enabled?"yes":"no",Z.sensitivity,String(Z.seasonLength),String(Z.minAbsoluteDelta),Z.severity]));return}let L=t.sensitivity;if(L&&!["low","medium","high"].includes(L))throw Error("--sensitivity must be low, medium, or high.");let j=A.upsert({organizationId:m.organizationId,projectId:m.projectId,signal:t.signal??"cost",sensitivity:L,seasonLength:t.season==null?void 0:Number(t.season),minAbsoluteDelta:t.minDelta==null?void 0:Number(t.minDelta),severity:t.severity,enabled:t.disable?!1:t.enable?!0:void 0});n(`Anomaly detection for '${j.signal}' updated.`),e(`${j.enabled?"Enabled":"Disabled"} · ${j.sensitivity} sensitivity · season ${j.seasonLength} · floor ${j.minAbsoluteDelta} · ${j.severity}`)}catch(m){Yr(m)}}),N.command("anomaly:silence","Skip detection for a signal, route, or status code").option("--signal <signal>","Signal to silence").option("--route <pattern>","Route glob, e.g. /webhooks/**").option("--status <code>","HTTP status code").option("--reason <reason>","Why — required").option("--until <timestamp>","ISO timestamp the silence expires").option("--list","List active silences").option("--remove <id>","Remove a silence").option("--env <environment>","Target environment").action(async(t)=>{try{let m=await Jr(t.env),A=m.service.anomalyConfigs;if(t.remove){n(A.removeSilence(t.remove)?"Silence removed.":"That silence was not found.");return}if(t.list){let j=A.listSilences({organizationId:m.organizationId,projectId:m.projectId});if(j.length===0){n("No active silences.");return}c(["ID","Signal","Route","Status","Expires","Reason"],j.map((G)=>[G.id.slice(0,8),G.signal??"-",G.routePattern??"-",G.statusCode==null?"-":String(G.statusCode),G.expiresAt??"never",G.reason]));return}if(!t.reason)throw Error("--reason is required: someone will ask why this stopped alerting.");let L=A.silence({organizationId:m.organizationId,projectId:m.projectId,signal:t.signal,routePattern:t.route,statusCode:t.status==null?void 0:Number(t.status),reason:t.reason,expiresAt:t.until});if(n(`Silenced (${L.id.slice(0,8)}).`),!L.expiresAt)a("This silence never expires. Pass --until to bound it, or it will outlive the reason for it.")}catch(m){Yr(m)}}),N.command("spend:anomalies","List detected spend anomalies").option("--env <environment>","Target environment").option("--unacknowledged","Only anomalies nobody has acknowledged").option("--json","Print structured JSON").action(async(t)=>{try{let m=await Jr(t.env),A=m.store.listAnomalies({organizationId:m.organizationId,projectId:m.projectId,unacknowledgedOnly:t.unacknowledged===!0});if(t.json){e(JSON.stringify(A,null,2));return}if(A.length===0){n("No anomalies detected.");return}c(["Hour","Signal","Direction","Observed","Expected","Score","Severity"],A.map((L)=>[L.bucketStart,L.signal,L.direction,Q(L.observed),Q(L.expected),L.score.toFixed(1),L.severity]))}catch(m){Yr(m)}})}import{readFileSync as om}from"node:fs";var sm={github:"GITHUB_TOKEN",gitlab:"GITLAB_TOKEN",bitbucket:"BITBUCKET_TOKEN",gitea:"GITEA_TOKEN",generic_https:"GIT_TOKEN"},am={github:"https://github.com",gitlab:"https://gitlab.com",bitbucket:"https://bitbucket.org",gitea:"https://gitea.com"};function mc(N,t={}){if(t.provider&&!["github","gitlab","bitbucket","gitea","generic_https","generic_ssh"].includes(t.provider))throw Error(`Unsupported Git provider: ${t.provider}`);let m=t.cloneUrl??(/^(?:https?:\/\/|ssh:\/\/|git@)/.test(N)?N:void 0),A="",L=m??N;if(m?.startsWith("git@")){let Z=/^git@([^:]+):(.+)$/.exec(m);if(!Z)throw Error("SSH repository URL must use git@host:owner/name.git");A=Z[1],L=Z[2]}else if(m){let Z=new URL(m);A=Z.hostname,L=Z.pathname}if(L=L.replace(/^\/+|\/+$/g,"").replace(/\.git$/,""),!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(L))throw Error("Repository must use owner/name or a supported clone URL");let j=t.provider??(A.includes("github")?"github":A.includes("gitlab")?"gitlab":A.includes("bitbucket")?"bitbucket":A.includes("gitea")?"gitea":m?.startsWith("git@")||m?.startsWith("ssh://")?"generic_ssh":m?"generic_https":"github"),G=t.host??am[j]??(A?`https://${A}`:"");if(!G)throw Error("A provider host is required");return{provider:j,host:G,fullName:L,cloneUrl:m}}async function cm(){let N=await Ne(),t=y(process.cwd(),N);return{config:N,controlPlane:t,sources:new Jt(t.store,{encryptionKey:k(process.cwd())})}}async function Vi(N){let t=await cm();try{return await N(t)}finally{t.controlPlane.store.close()}}function us(N,t,m){let A=N.listConnections(t);if(!m)return A.length===1?A[0]:void 0;return A.find((L)=>L.id===m||L.name.toLowerCase()===m.toLowerCase())}function ad(N,t){if(!N)throw Error(`${t} file is required`);return om(N,"utf8")}async function lm(N,t,m,A){if(["github","gitlab","bitbucket","gitea"].includes(t.provider)){let j=N.listRepositories(t.id).find((ee)=>ee.fullName.toLowerCase()===m.fullName.toLowerCase());if(j)return j;let Z=(await Nr(N,t.id,{search:m.fullName,maxPages:20})).find((ee)=>ee.fullName.toLowerCase()===m.fullName.toLowerCase());if(!Z)throw Error(`Repository ${m.fullName} is not granted to this connection`);return Z}let L=A.url??m.cloneUrl;if(!L)throw Error("Generic Git connections require --url <clone-url>");return N.upsertRepository({connectionId:t.id,providerRepositoryId:`manual:${m.fullName}`,fullName:m.fullName,cloneUrl:L,defaultBranch:A.branch??"main",visibility:"unknown",archived:!1,metadata:{source:"cli"}})}async function cd(N,t,m){let A=mc(t,{provider:m.provider,host:m.host,cloneUrl:m.url}),L=us(N.sources,N.controlPlane.organization.id,m.connection);if(!L){if(m.connection)throw Error(`Source connection ${m.connection} was not found`);let G=m.tokenEnv??sm[A.provider],Z=G?process.env[G]:void 0;if(["github","gitlab","bitbucket","gitea"].includes(A.provider)&&!Z)throw Error(`Set ${G} or pass --token-env <name>; secrets are never accepted as command arguments`);let ee=A.provider==="generic_ssh"?{publicKey:ad(m.publicKey,"Public key"),privateKey:ad(m.privateKey,"Private key"),hostKey:m.hostKey??""}:void 0;N.controlPlane.store.database.transaction(()=>{if(L=N.sources.createConnection({organizationId:N.controlPlane.organization.id,provider:A.provider,name:m.name??`${A.provider.replace("_"," ")} · ${new URL(A.host).host}`,host:A.host,owner:A.fullName.split("/")[0],authKind:A.provider==="generic_ssh"?"deploy_key":Z?"access_token":"none",credential:Z?{token:Z}:void 0}),ee)N.sources.createDeployKey({connectionId:L.id,name:m.deployKey??`${A.fullName} deploy key`,...ee,host:m.sshHost??new URL(A.host).hostname,actorId:void 0})})()}if(!L)throw Error("Source connection could not be created");if(L.status==="disconnected")throw Error("The selected source connection is disconnected");let j=await lm(N.sources,L,A,m);if(m.site){let G=N.controlPlane.environments.get("production")??[...N.controlPlane.environments.values()][0],Z=N.controlPlane.store.listResources(N.controlPlane.project.id,G?.id).find((ne)=>ne.kind==="application"&&ne.slug===m.site);if(!Z)throw Error(`Application ${m.site} was not found`);let ee=L.provider==="generic_ssh"?N.sources.listDeployKeys(L.id)[0]:void 0;N.sources.createBinding({projectId:N.controlPlane.project.id,environmentId:G?.id,resourceId:Z.id,connectionId:L.id,repositoryId:j.id,repositoryFullName:j.fullName,defaultBranch:m.branch??j.defaultBranch,branchRule:m.tag?void 0:m.branch??j.defaultBranch,tagRule:m.tag,monorepoRoot:m.root??".",includePaths:m.include?.split(",").map((ne)=>ne.trim()).filter(Boolean),excludePaths:m.exclude?.split(",").map((ne)=>ne.trim()).filter(Boolean),submodules:m.submodules,cloneDepth:m.fullClone?void 0:20,deployKeyId:ee?.id})}return{connection:L,repository:j}}function Wi(N){r(N instanceof Error?N.message:"Git integration command failed")}function fc(N){N.command("git:connections","List encrypted Git provider connections").action(async()=>{try{await Vi(async({sources:t,controlPlane:m})=>{let A=t.listConnections(m.organization.id);c(["ID","Name","Provider","Status","Credential","Last sync"],A.map((L)=>[L.id,L.name,L.provider,L.status,L.credentialConfigured?"encrypted":"none",L.lastSyncedAt??"-"]))})}catch(t){Wi(t)}}),N.command("git:repositories","List repositories granted to a connection").option("--connection <id>","Connection ID or name").action(async(t)=>{try{await Vi(async({sources:m,controlPlane:A})=>{let L=us(m,A.organization.id,t.connection);if(!L)throw Error("Choose a connection with --connection <id>");let j=m.listRepositories(L.id);c(["ID","Repository","Default branch","Visibility","Archived"],j.map((G)=>[G.id,G.fullName,G.defaultBranch,G.visibility,G.archived?"yes":"no"]))})}catch(m){Wi(m)}}),N.command("git:add <repo>","Connect and optionally bind a Git repository").option("--branch <branch>","Default branch to deploy",{default:"main"}).option("--connection <id>","Reuse a connection ID or name").option("--provider <provider>","github, gitlab, bitbucket, gitea, generic_https, or generic_ssh").option("--host <url>","Provider API host").option("--name <name>","Connection name").option("--token-env <name>","Environment variable containing the access token").option("--url <clone-url>","Credential-free clone URL").option("--site <slug>","Bind the repository to an application").option("--root <path>","Monorepo root",{default:"."}).option("--include <globs>","Comma-separated watched paths").option("--exclude <globs>","Comma-separated ignored paths").option("--tag <glob>","Deploy matching tags instead of branch pushes").option("--full-clone","Use full history instead of a shallow clone").option("--submodules","Initialize repository submodules").option("--deploy-key <name>","Deploy-key display name").option("--public-key <path>","SSH public-key file").option("--private-key <path>","SSH private-key file").option("--host-key <key>","Pinned SSH host key").option("--ssh-host <host>","SSH clone host").option("--yes","Skip confirmation").action(async(t,m)=>{try{if(!m.yes&&!await d(`Connect ${t}?`,!0))return;let A=await Vi((L)=>cd(L,t,m));if(n(`Connected ${A.repository.fullName}`),e(`Connection: ${A.connection.name} (${A.connection.id})`),m.site)e(`Application binding: ${m.site}`)}catch(A){Wi(A)}}),N.command("git:import","Import site.repository entries from cloud.config.ts").option("--connection <id>","Reuse a connection ID or name").option("--token-env <name>","Environment variable containing provider token").option("--yes","Skip confirmation").action(async(t)=>{try{await Vi(async(m)=>{let A=Object.entries(m.config.sites??{}).filter(([,L])=>L.repository?.url);if(!A.length)throw Error("No site.repository entries were found");if(!t.yes&&!await d(`Import ${A.length} repository configuration${A.length===1?"":"s"}?`,!0))return;for(let[L,j]of A){let G=j.repository,Z=G.provider==="custom"?void 0:G.provider;await cd(m,G.url,{connection:t.connection,tokenEnv:t.tokenEnv,site:L,provider:Z,branch:G.branch??"main",tag:G.strategy==="tag"?G.tag??G.tagPattern??"v*":void 0,url:G.url}),n(`Imported ${L} · ${G.url}`)}})}catch(m){Wi(m)}}),N.command("git:branches <repo>","List branches or tags visible to a connection").option("--connection <id>","Connection ID or name").option("--tags","List tags instead of branches").option("--deploy-key <id>","SSH deploy-key ID").action(async(t,m)=>{try{await Vi(async({sources:A,controlPlane:L})=>{let j=us(A,L.organization.id,m.connection);if(!j)throw Error("Choose a connection with --connection <id>");let G=A.listRepositories(j.id).find((ee)=>ee.id===t||ee.fullName.toLowerCase()===t.toLowerCase());if(!G)throw Error(`Repository ${t} was not found`);let Z=await Xt(A,{connectionId:j.id,repository:G.fullName,repositoryId:G.id,type:m.tags?"tags":"branches",deployKeyId:m.deployKey??A.listDeployKeys(j.id)[0]?.id});c([m.tags?"Tag":"Branch","Commit","Protected"],Z.refs.map((ee)=>[ee.name,ee.commitSha,ee.protected?"yes":"no"]))})}catch(A){Wi(A)}}),N.command("git:deploy <branch>","Queue an idempotent deployment from a Git branch").option("--binding <id>","Source binding ID").option("--repo <owner/name>","Bound repository").option("--env <environment>","Target environment",{default:"production"}).action(async(t,m)=>{try{await Vi(async({sources:A,controlPlane:L})=>{let j=L.environments.get(m.env??"production");if(!j)throw Error(`Environment ${m.env} was not found`);let G=A.listBindings({projectId:L.project.id,status:"active"}).find((oe)=>(!m.binding||oe.id===m.binding)&&(!m.repo||oe.repositoryFullName.toLowerCase()===m.repo.toLowerCase())&&(!oe.environmentId||oe.environmentId===j.id));if(!G)throw Error("No active source binding matched; use --binding or --repo");let ee=(await Xt(A,{connectionId:G.connectionId,repository:G.repositoryFullName,repositoryId:G.repositoryId,type:"branches",deployKeyId:G.deployKeyId})).refs.find((oe)=>oe.name===t);if(!ee)throw Error(`Branch ${t} was not found`);let ne=L.store.createOperation({projectId:G.projectId,environmentId:j.id,resourceId:G.resourceId,kind:"deploy.source",idempotencyKey:`cli-source:${G.id}:${ee.commitSha}`,correlationId:`cli-source:${G.id}:${ee.commitSha}`,input:{source:{connectionId:G.connectionId,bindingId:G.id,repository:G.repositoryFullName,commitSha:ee.commitSha,branch:t,monorepoRoot:G.monorepoRoot,submodules:G.submodules,cloneDepth:G.cloneDepth??null}}});n(`Deployment queued: ${ne.id}`),e(`${G.repositoryFullName}@${t} · ${ee.commitSha}`)})}catch(A){Wi(A)}}),N.command("git:webhook:add <repo>","Create and reconcile a signed auto-deploy webhook").option("--connection <id>","Connection ID or name").option("--base-url <url>","Public HTTPS dashboard URL").action(async(t,m)=>{try{await Vi(async({sources:A,controlPlane:L})=>{let j=us(A,L.organization.id,m.connection);if(!j)throw Error("Choose a connection with --connection <id>");let G=A.listRepositories(j.id).find((be)=>be.id===t||be.fullName.toLowerCase()===t.toLowerCase());if(!G)throw Error(`Repository ${t} was not found`);let Z=m.baseUrl??process.env.TS_CLOUD_WEBHOOK_BASE_URL;if(!Z)throw Error("Pass --base-url or set TS_CLOUD_WEBHOOK_BASE_URL");let ee=A.createWebhook({connectionId:j.id,repositoryId:G.id,repositoryFullName:G.fullName}),ne=Ct(Z,ee.webhook),oe=await Qt(A,ee.webhook.id,Z);n(`Webhook ${oe.status}: ${ne}`),e("The signing secret is encrypted and is not printed.")})}catch(A){Wi(A)}}),N.command("git:webhook:remove <webhook>","Disable and remove a provider webhook").option("--yes","Skip confirmation").action(async(t,m)=>{try{await Vi(async({sources:A,controlPlane:L})=>{let j=A.listConnections(L.organization.id).flatMap((Z)=>A.listWebhooks(Z.id)).find((Z)=>Z.id===t||Z.repositoryFullName.toLowerCase()===t.toLowerCase());if(!j)throw Error(`Webhook ${t} was not found`);if(!m.yes&&!await d(`Remove webhook for ${j.repositoryFullName}?`,!0))return;let G=await Ii(A,j.id);n(`Webhook disabled: ${G.id}`)})}catch(A){Wi(A)}}),N.command("git:disconnect <connection>","Preview and disconnect a Git provider connection").option("--yes","Skip confirmation").action(async(t,m)=>{try{await Vi(async({sources:A,controlPlane:L})=>{let j=us(A,L.organization.id,t);if(!j)throw Error(`Source connection ${t} was not found`);let G=A.listBindings({connectionId:j.id,status:"active"});e(`${G.length} active binding${G.length===1?"":"s"} will be disabled.`);for(let Z of G)e(` ${Z.repositoryFullName} · ${Z.branchRule??Z.defaultBranch} · ${Z.monorepoRoot}`);if(!m.yes&&!await d(`Disconnect ${j.name}?`,!1))return;A.disconnectConnection(j.id),n(`Disconnected ${j.name}`)})}catch(A){Wi(A)}})}import{readFileSync as dd,writeFileSync as dm}from"node:fs";import{resolve as Ms}from"node:path";async function um(){let N=await Ne(),t=y(process.cwd(),N),m=k(process.cwd());return{config:N,controlPlane:t,drafts:new kn(t.store),artifacts:new xn(t.store,{cwd:process.cwd()}),registries:new _n(t.store,{encryptionKey:m}),sources:new Jt(t.store,{encryptionKey:m})}}async function ci(N){let t=await um();try{return await N(t)}finally{t.controlPlane.store.close()}}function pr(N){r(N instanceof Error?N.message:"Application onboarding command failed")}function ld(N){return JSON.parse(dd(Ms(N),"utf8"))}function pm(N){let t=N.site,m=["laravel","php","statamic","wordpress"].includes(t.type),A=["static","spa"].includes(t.type)||!t.start&&!m,L=A?{kind:"static",publishDirectory:t.root??t.webDirectory??"dist",buildCommand:t.build}:{kind:"server",runtime:m?"php":"bun",runtimeVersion:m?t.phpVersion:void 0,installCommand:m?"composer install --no-dev --prefer-dist --optimize-autoloader":void 0,buildCommand:t.build,startCommand:t.start??(m?"php -S 0.0.0.0:$PORT -t public":"bun run start")};return{schemaVersion:1,name:t.name??N.siteSlug,slug:N.siteSlug,projectId:N.projectId,environmentId:N.environmentId,source:N.source??{kind:"local",root:t.root??"."},build:L,runtime:{target:"server",architecture:t.architecture==="arm64"?"arm64":"x86_64",port:A?void 0:Number(t.port??3000),healthCheck:A?void 0:{protocol:"http",path:t.healthCheck?.path??"/health"}},domain:t.domain?{hostname:t.domain,tls:t.ssl?.provider!=="none"}:void 0}}function gc(N){N.command("app:detect [root]","Detect application frameworks without executing project code").action(async(t=".")=>{try{let m=At(Dn(Ms(t)));c(["Framework","Strategy","Confidence","Evidence"],m.map((A)=>[A.framework,A.strategy,`${Math.round(A.confidence*100)}%`,A.evidence.map((L)=>L.path).join(", ")||"manual"]));for(let A of m)e(`${A.framework}: ${A.description}`)}catch(m){pr(m)}}),N.command("app:plan <draft>","Validate and preview a secret-free application draft").option("--secrets <names>","Comma-separated configured secret names").action(async(t,m)=>{try{let A=se(ld(t),m.secrets?.split(",").map((L)=>L.trim()).filter(Boolean));if(e(JSON.stringify(A,null,2)),!A.valid)pr(Error("Plan is not deployable; resolve the reported issues and missing secrets."))}catch(A){pr(A)}}),N.command("app:draft:save <draft>","Persist or update a resumable application draft").option("--id <id>","Existing draft ID").option("--version <version>","Expected draft version").option("--step <step>","Wizard step",{default:"source"}).option("--secrets <names>","Comma-separated configured secret names").action(async(t,m)=>{try{await ci(async({drafts:A,controlPlane:L})=>{let j=ld(t),G=m.secrets?.split(",").map((ee)=>ee.trim()).filter(Boolean)??[],Z=m.id?A.update(m.id,Number(m.version),{draft:j,step:m.step,suppliedSecretNames:G}):A.create({organizationId:L.organization.id,projectId:L.project.id,name:j.name,draft:{...j,projectId:L.project.id},step:m.step,suppliedSecretNames:G});n(`Draft saved: ${Z.id} · v${Z.version} · ${Z.status}`)})}catch(A){pr(A)}}),N.command("app:drafts","List resumable application drafts").action(async()=>{try{await ci(async({drafts:t,controlPlane:m})=>{let A=t.list(m.project.id);c(["ID","Application","Step","Status","Version","Updated"],A.map((L)=>[L.id,L.input.name,L.step,L.status,String(L.version),L.updatedAt]))})}catch(t){pr(t)}}),N.command("app:export <draft>","Export a deterministic application manifest").option("--output <path>","Output path",{default:"application.manifest.json"}).action(async(t,m)=>{try{await ci(async({drafts:A})=>{let L=A.get(t);if(!L)throw Error(`Draft ${t} was not found`);let j=se(L.input,L.suppliedSecretNames),G=Ms(m.output??"application.manifest.json");dm(G,j.serializedManifest,{mode:384}),n(`Manifest exported: ${G}`)})}catch(A){pr(A)}}),N.command("app:deploy <draft>","Confirm and queue a validated application plan").option("--confirm <environment>","Exact target environment slug").action(async(t,m)=>{try{await ci(async({drafts:A,controlPlane:L})=>{let j=A.get(t);if(!j)throw Error(`Draft ${t} was not found`);if(!m.confirm)throw Error("Pass --confirm <environment> to acknowledge the target and recurring cost drivers");let G=Dr({controlPlane:L.store,drafts:A,draftId:t,expectedVersion:j.version,confirmEnvironment:m.confirm});n(`Application queued: ${G.operation.id}`),e(`Cost drivers: ${G.plan.costDrivers.join(", ")}`)})}catch(A){pr(A)}}),N.command("app:import <site>","Import an existing cloud.config.ts site as a draft").option("--connection <id>","Git connection ID for repository-backed sites").option("--env <environment>","Target environment",{default:"production"}).action(async(t,m)=>{try{await ci(async({config:A,controlPlane:L,sources:j,drafts:G})=>{let Z=A.sites?.[t];if(!Z)throw Error(`Site ${t} was not found`);let ee=L.environments.get(m.env??"production");if(!ee)throw Error(`Environment ${m.env} was not found`);let ne;if(Z.repository?.url){if(!m.connection)throw Error("Repository-backed imports require --connection <id>");let we=j.getConnection(m.connection);if(!we)throw Error("Git connection was not found");let Ce=mc(Z.repository.url),De=j.listRepositories(we.id).find((Se)=>Se.fullName.toLowerCase()===Ce.fullName.toLowerCase());if(!De)throw Error(`Repository ${Ce.fullName} is not synchronized`);ne={kind:"git",connectionId:we.id,repositoryId:De.id,repositoryFullName:De.fullName,ref:Z.repository.branch??"main"}}let oe=pm({site:Z,siteSlug:t,projectId:L.project.id,environmentId:ee.id,source:ne}),be=G.create({organizationId:L.organization.id,projectId:L.project.id,name:`Import ${t}`,draft:oe,step:"review"});n(`Imported draft: ${be.id}`)})}catch(A){pr(A)}}),N.command("app:artifact:add <file>","Inspect and store a bounded ZIP or TAR artifact").action(async(t)=>{try{await ci(async({artifacts:m,controlPlane:A})=>{let L=Ms(t),j=m.create({organizationId:A.organization.id,projectId:A.project.id,filename:L.split("/").pop(),bytes:dd(L)});n(`Artifact stored: ${j.id} · ${j.entryCount} entries · ${j.sha256}`)})}catch(m){pr(m)}}),N.command("app:registry:add <host>","Create an encrypted private-registry connection").option("--name <name>","Connection name",{default:"Container registry"}).option("--provider <provider>","docker_hub, ghcr, ecr, gcr, or generic",{default:"generic"}).option("--username-env <name>","Username environment variable",{default:"REGISTRY_USERNAME"}).option("--password-env <name>","Password environment variable",{default:"REGISTRY_PASSWORD"}).option("--token-env <name>","Bearer-token environment variable",{default:"REGISTRY_TOKEN"}).action(async(t,m)=>{try{await ci(async({registries:A,controlPlane:L})=>{let j={username:process.env[m.usernameEnv],password:process.env[m.passwordEnv],token:process.env[m.tokenEnv]},G=A.create({organizationId:L.organization.id,provider:m.provider,name:m.name,host:t,credential:Object.values(j).some(Boolean)?j:void 0});n(`Registry connection created: ${G.id}`)})}catch(A){pr(A)}}),N.command("app:registries","List safe registry connection metadata").action(async()=>{try{await ci(async({registries:t,controlPlane:m})=>c(["ID","Name","Provider","Host","Status"],t.list(m.organization.id).map((A)=>[A.id,A.name,A.provider,A.host,A.status])))}catch(t){pr(t)}}),N.command("app:registry:test <id>","Test registry and optional image pull access").option("--image <image>","Image reference").action(async(t,m)=>{try{await ci(async({registries:A})=>{let L=await A.test(t,{image:m.image});e(`${L.status}: ${L.healthMessage}`)})}catch(A){pr(A)}}),N.command("app:registry:rotate <id>","Rotate write-only registry credentials").option("--username-env <name>","Username environment variable",{default:"REGISTRY_USERNAME"}).option("--password-env <name>","Password environment variable",{default:"REGISTRY_PASSWORD"}).option("--token-env <name>","Bearer-token environment variable",{default:"REGISTRY_TOKEN"}).option("--expires <date>","Credential expiry timestamp").action(async(t,m)=>{try{await ci(async({registries:A})=>{let L={username:process.env[m.usernameEnv],password:process.env[m.passwordEnv],token:process.env[m.tokenEnv]};if(!Object.values(L).some(Boolean))throw Error("Set at least one configured registry credential environment variable");let j=A.rotate(t,L,{expiresAt:m.expires?new Date(m.expires).toISOString():void 0});n(`Registry credential rotated: ${j.id} · ${j.status}`)})}catch(A){pr(A)}}),N.command("app:registry:disconnect <id>","Disconnect and erase registry credentials").option("--yes","Skip confirmation").action(async(t,m)=>{try{await ci(async({registries:A,drafts:L,controlPlane:j})=>{let G=A.get(t);if(!G)throw Error(`Registry ${t} was not found`);let Z=L.list(j.project.id).filter((ee)=>ee.input.source.kind==="image"&&ee.input.source.registryConnectionId===t||ee.input.build.kind==="prebuilt_image"&&ee.input.build.registryConnectionId===t);if(e(`${Z.length} application draft${Z.length===1?"":"s"} reference this registry.`),!m.yes&&!await d(`Disconnect ${G.name}?`,!1))return;A.disconnect(t),n(`Disconnected ${G.name}`)})}catch(A){pr(A)}})}import{existsSync as mm,readFileSync as fm,writeFileSync as gm}from"node:fs";function hc(N,t){r(`'${N}' is not implemented yet.`),e(t),process.exitCode=1}function hm(N){let t={};for(let m of N.split(`
366
+ `)){let A=m.trim();if(!A||A.startsWith("#"))continue;let L=A.indexOf("=");if(L===-1)continue;let j=A.slice(0,L).trim(),G=A.slice(L+1).trim();if(G.startsWith('"')&&G.endsWith('"')||G.startsWith("'")&&G.endsWith("'"))G=G.slice(1,-1);t[j]=G}return t}function ym(N){return Object.entries(N).sort(([m],[A])=>m.localeCompare(A)).map(([m,A])=>{if(!/[\s#"']/.test(A))return`${m}=${A}`;let L='"'+A.replace(/"/g,"\\\"")+'"';return`${m}=${L}`}).join(`
367
367
  `)+`
368
- `}function ac(N){N.command("env:list","List configured environments and their deployed stack status").action(async()=>{i("Environments");try{let t=await Me(),p=Object.keys(t.environments??{});if(!p.length){e("No environments configured. Add environments.<env> to your cloud.config.");return}let A=[];for(let L of p){let j=t.environments[L],G=j?.region||t.project.region||"us-east-1",Z=j?.app?`serverless (${j.app.kind})`:"server/static",ee="not deployed";if(j?.app)try{let re=new v(G),{Stacks:oe}=await re.describeStacks({stackName:Pe(t,L)});ee=oe?.[0]?.StackStatus??"not deployed"}catch{ee="not deployed"}A.push([L,j?.type??L,Z,G,ee])}c(["Environment","Type","Kind","Region","Stack status"],A)}catch(t){r(`Failed to list environments: ${t.message}`),process.exitCode=1}}),N.command("env:compare <env1> <env2>","Compare two environments' app configuration").action(async(t,p)=>{i(`Comparing ${t} ↔ ${p}`);try{let A=await Me(),L=A.environments?.[t],j=A.environments?.[p];if(!L||!j){r(`Both environments must exist in config (have: ${Object.keys(A.environments??{}).join(", ")||"none"}).`),process.exitCode=1;return}let G=(be,we="")=>{let Ce={};for(let[Ee,Se]of Object.entries(be??{})){let De=we?`${we}.${Ee}`:Ee;if(Se&&typeof Se==="object"&&!Array.isArray(Se))Object.assign(Ce,G(Se,De));else Ce[De]=Array.isArray(Se)?JSON.stringify(Se):String(Se)}return Ce},Z=G(L),ee=G(j),oe=[...new Set([...Object.keys(Z),...Object.keys(ee)])].sort().filter((be)=>Z[be]!==ee[be]).map((be)=>[be,Z[be]??"—",ee[be]??"—"]);if(!oe.length){n("Identical — no configuration differences.");return}c(["Setting",t,p],oe),e(`
369
- ${oe.length} difference(s)`)}catch(A){r(`Comparison failed: ${A.message}`),process.exitCode=1}}),N.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 p=await Me(),A=t?.env||"production",{region:L,functions:j}=yt(p,A),G=t?.function??"http",re=(await new P(L).getFunction(j[G])).Configuration?.Environment?.Variables??{},oe=t?.file??`.env.${A}`;ip(oe,sp(re)),n(`Wrote ${Object.keys(re).length} variable(s) from ${j[G]} → ${oe}`)}catch(p){r(`env:pull failed: ${p.message}`),process.exitCode=1}}),N.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 p=await Me(),A=t?.env||"production",{region:L,functions:j}=yt(p,A),G=t?.file??`.env.${A}`;if(!np(G)){r(`File not found: ${G}`),process.exitCode=1;return}let Z=op(rp(G,"utf8")),ee=(t?.function??"all").toLowerCase();if(ee!=="all"&&!["http","queue","cli"].includes(ee)){r("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let re=ee==="all"?["http","queue","cli"]:[ee],oe=(p.environments?.[A]?.app?.provisionedConcurrency??0)>0,be=new P(L);for(let we of re){let Ce=j[we],Ee=Z;if(!t?.replace)Ee={...(await be.getFunction(Ce)).Configuration?.Environment?.Variables??{},...Z};if(go(Ce,Ee),await be.updateFunctionConfiguration({FunctionName:Ce,Environment:{Variables:Ee}}),await be.waitForFunctionActive(Ce,120),oe){let Se=await be.publishVersion({FunctionName:Ce});if(Se.Version)await be.updateAlias({FunctionName:Ce,Name:"live",FunctionVersion:Se.Version})}n(`Pushed ${Object.keys(Z).length} var(s) → ${Ce}${t?.replace?" (replaced)":" (merged)"}${oe?" (published + flipped live alias)":""}`)}e("Note: a subsequent `cloud deploy:serverless` re-injects infra/secret env from config.")}catch(p){r(`env:push failed: ${p.message}`),process.exitCode=1}}),N.command("env:create <name>","Create new environment").action(async(t)=>{sc(`env:create ${t}`,"Add an `environments.<name>` block to your cloud.config and run `cloud deploy:serverless --env <name>`.")}),N.command("env:switch <name>","Switch active environment").action(async(t)=>{sc(`env:switch ${t}`,'ts-cloud has no persistent "active" environment — pass --env <name> to each command.')});for(let t of["clone","promote","sync"])N.command(`env:${t} <source> <target>`,`(${t}) environment configuration`).action(async(p,A)=>{sc(`env:${t} ${p} ${A}`,`Copy the \`environments.${p}\` block to \`environments.${A}\` in cloud.config, then \`cloud deploy:serverless --env ${A}\`.`)});N.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,p)=>{let A;try{let L=await Me(),j=p.env||"production",G=p.site||Object.keys(L.sites??{})[0];if(!G)throw Error("No application site is configured; pass --site after adding one");A=y(process.cwd(),L);let Z=A.environments.get(j),ee=Z?A.store.listResources(A.project.id,Z.id).find((Se)=>Se.kind==="application"&&Se.slug===G):void 0;if(!Z||!ee)throw Error(`Application ${G} was not found in base environment ${j}`);let re=new qe(A.store),oe=re.previews.getDefinitionForResource(ee.id);if(!oe){let Se=L.sites?.[G]?.domain,De=p.domain||(Se?`https://{name}.preview.${Se}`:void 0);if(!De)throw Error("Pass --domain with an HTTPS pattern containing {name} to configure previews");oe=re.previews.createDefinition({projectId:A.project.id,resourceId:ee.id,baseEnvironmentId:Z.id,domainPattern:De,ttlHours:Number(p.ttl)||void 0})}let be=Number(p.pr)||void 0,we=be?re.previews.findForPullRequest(oe.id,"local",be):re.previews.findForBranch(oe.id,"local",t);if(p.getUrl){if(!we)throw Error("Preview was not found");e(we.url??"URL unavailable");return}if(p.extend){if(!we)throw Error("Preview was not found");let Se=re.previews.extend(we.id,Number(p.extend));n(`Extended ${Se.name} until ${Se.expiresAt}`);return}if(p.destroy){if(!we)throw Error("Preview was not found");if(!p.yes&&!await d(`Destroy ${we.name} and only its tagged resources?`,!1))return;let Se=re.enqueueDestroy(we,"manual");n(`Preview teardown queued: ${Se.id}`);return}if(p.rebuild){if(!we)throw Error("Preview was not found");let Se=re.enqueueDeploy(we,{reason:"manual_rebuild"});n(`Preview rebuild queued: ${Se.id}`);return}if(!p.sha)throw Error("Pass --sha with the exact immutable commit to deploy");let Ce=re.previews.upsert({definitionId:oe.id,repository:"local",branch:t,pullRequestNumber:be,commitSha:p.sha}),Ee=re.enqueueDeploy(Ce.preview,{created:Ce.created});n(`${Ce.created?"Preview creation":"Preview update"} queued: ${Ee.id}`),e(`${Ce.preview.url} · expires ${Ce.preview.expiresAt}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A?.store.close()}}),N.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 p;try{let A=await Me();p=y(process.cwd(),A);let j=new qe(p.store).cleanup({dryRun:t.dryRun,maxAgeHours:Number(t.maxAge)||void 0,keepCount:Number(t.keep)||void 0});c(["Preview","Status","Expires","Reason"],j.candidates.map((G)=>[G.preview.name,G.preview.status,G.preview.expiresAt,G.reasons.join(", ")])),n(t.dryRun?`${j.candidates.length} cleanup candidate(s)`:`${j.operations.length} teardown job(s) queued`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{p?.store.close()}}),N.command("env:previews","List persistent preview environments").option("--site <name>","Filter application site").action(async(t)=>{let p;try{let A=await Me();p=y(process.cwd(),A);let L=new qe(p.store),j=t.site?new Set(p.store.listResources(p.project.id).filter((Z)=>Z.slug===t.site).map((Z)=>Z.id)):void 0,G=L.previews.listInstances({projectId:p.project.id}).filter((Z)=>!j||j.has(Z.resourceId));c(["ID","Name","Status","Branch / PR","Commit","URL","Expires","Cost"],G.map((Z)=>[Z.id,Z.name,Z.status,Z.pullRequestNumber?`PR #${Z.pullRequestNumber}`:Z.branch,Z.commitSha.slice(0,12),Z.url??"—",Z.expiresAt,Z.costEstimate==null?"—":`$${Z.costEstimate.toFixed(2)}`]))}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{p?.store.close()}})}import{readFileSync as Ls}from"node:fs";async function ri(){let N=await Me(),t=y(process.cwd(),N);return{config:N,controlPlane:t,applications:new ut(t.store)}}async function cc(N,t,p){let A=await ri();try{let L=A.applications.get(N);if(!L)throw Error("Compose application was not found");let j=t??A.controlPlane.store.listEnvironments(L.projectId).find((Z)=>Z.id===L.environmentId)?.slug;if(!j)throw Error("Compose environment was not found");let G=await Qt(A.config,j,p(L));if(!G.ok)throw Error(G.stderr||G.error||"Remote Compose command failed");if(G.stdout)e(G.stdout)}finally{A.controlPlane.store.close()}}function lc(N){N.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,p)=>{let A=await ri();try{let L=A.controlPlane.environments.get(p.env??"production");if(!L)throw Error("Environment was not found");let j=A.applications.preview(Ls(t,"utf8"),{name:p.name??"Compose application",projectId:A.controlPlane.project.id,environmentId:L.id});if(c(["Severity","Path","Code","Message"],j.diagnostics.map((G)=>[G.severity,G.path,G.code,G.message])),!j.valid)process.exitCode=1;else n(`${Object.keys(j.manifest.spec.services).length} services are valid`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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,p)=>{let A=await ri();try{let L=A.controlPlane.environments.get(p.env??"production");if(!L)throw Error("Environment was not found");let j=p.name??t.split("/").at(-2)??"Compose application",G=A.applications.import(Ls(t,"utf8"),{name:j,projectId:A.controlPlane.project.id,environmentId:L.id});n(`Imported ${G.application.name} (${G.application.id})`),e(`${Object.keys(G.application.manifest.spec.services).length} services · ${G.parsed.diagnostics.length} diagnostic(s)`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.command("compose:templates","List pinned verified Compose templates").action(()=>c(["ID","Version","Category","Architecture","Verified"],cr().map((t)=>[t.id,t.version,t.category,t.architecture,t.lastVerifiedAt]))),N.command("compose:catalog <file>","Validate and inspect a local/custom template catalog").option("--env <environment>","Target environment",{default:"production"}).action(async(t,p)=>{let A=await ri();try{let L=A.controlPlane.environments.get(p.env??"production");if(!L)throw Error("Environment was not found");let j=ha(Ls(t,"utf8"),{projectId:A.controlPlane.project.id,environmentId:L.id});if(c(["ID","Version","Category","Services","Checksum"],j.templates.map((G)=>[G.template.id,G.template.version,G.template.category,String(Object.keys(G.parsed.manifest.spec.services).length),G.template.checksum.slice(0,12)])),j.diagnostics.length)c(["Severity","Path","Message"],j.diagnostics.map((G)=>[G.severity,G.path,G.message]));if(!j.valid)process.exitCode=1}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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,p)=>{let A=await ri();try{let L=A.controlPlane.environments.get(p.env??"production");if(!L)throw Error("Environment was not found");let j=A.applications.fromTemplate(t,{domain:p.domain??""},{name:p.name??t,projectId:A.controlPlane.project.id,environmentId:L.id,version:p.version});n(`Created ${j.application.name} from ${t}@${j.application.templateVersion}`),e(`Export with cloud compose:export ${j.application.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.command("compose:list","List Compose applications").action(async()=>{let t=await ri();try{c(["ID","Name","Status","Services","Source","Updated"],t.applications.list({projectId:t.controlPlane.project.id}).map((p)=>[p.id,p.name,p.status,String(Object.keys(p.manifest.spec.services).length),p.templateId?`${p.templateId}@${p.templateVersion}`:"compose",p.updatedAt]))}finally{t.controlPlane.store.close()}}),N.command("compose:export <application>","Print a normalized editable Compose file").action(async(t)=>{let p=await ri();try{let A=p.applications.get(t);if(!A)throw Error("Compose application was not found");e(Hn(A.manifest))}finally{p.controlPlane.store.close()}}),N.command("compose:diff <application> <file>","Compare persisted and proposed Compose manifests").action(async(t,p)=>{let A=await ri();try{let L=A.applications.get(t);if(!L)throw Error("Compose application was not found");let j=Nt(Ls(p,"utf8"),L.manifest.metadata);c(["Path","Before","After"],Go(L.manifest,j.manifest).map((G)=>[G.path,JSON.stringify(G.before),JSON.stringify(G.after)]))}finally{A.controlPlane.store.close()}});for(let t of["deploy","redeploy","start","stop"])N.command(`compose:${t} <application>`,`${t} a Compose application through the durable queue`).action(async(p)=>{let A=await ri();try{let L=A.applications.get(p);if(!L)throw Error("Compose application was not found");let j=new ze(A.controlPlane.store).enqueue(L,t);n(`${t} queued: ${j.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}});N.command("compose:scale <application> <service> <replicas>","Scale one capable Compose service").action(async(t,p,A)=>{let L=await ri();try{let j=L.applications.get(t);if(!j)throw Error("Compose application was not found");let G=new ze(L.controlPlane.store).enqueue(j,"scale",{service:p,replicas:Number(A)});n(`Scale queued: ${G.id}`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{L.controlPlane.store.close()}}),N.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,p)=>{let A=await ri();try{let L=A.applications.get(t);if(!L)throw Error("Compose application was not found");let j=new ze(A.controlPlane.store).enqueue(L,"delete",{removeVolumes:p.removeVolumes,confirmation:p.confirm});n(`Deletion queued: ${j.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.command("compose:logs <application> <service>","Read ordered service logs").option("--lines <count>","Tail line count",{default:"200"}).option("--env <environment>","Target environment").action((t,p,A)=>cc(t,A.env,(L)=>Wn(L.manifest,p,Number(A.lines)||200))),N.command("compose:shell <application> <service> [command...]","Run a command in one service").option("--env <environment>","Target environment").action((t,p,A,L)=>cc(t,L.env,(j)=>Gn(j.manifest,p,A?.length?A:["sh"]))),N.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,p)=>cc(t,p.env,(A)=>{if(p.action==="scale")return Sa(A.manifest,p.service??"",Number(p.replicas));return ya(A.manifest,p.action??"deploy",{service:p.service,removeVolumes:p.removeVolumes})}))}import{readFileSync as ap}from"node:fs";async function si(){let N=await Me(),t=y(process.cwd(),N);return{controlPlane:t,releases:new nt(t.store)}}function dc(N){if(!N)return{};return JSON.parse(ap(N,"utf8"))}function uc(N){N.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,p)=>{let A=await si();try{let L=A.releases.releases.registerArtifact({organizationId:A.controlPlane.organization.id,digest:String(p.digest??""),kind:p.kind??"compute",uri:t,size:Number(p.size),mediaType:p.mediaType});n(`Artifact registered: ${L.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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,p,A)=>{let L=await si();try{let j=L.controlPlane.environments.get(A.env??"production"),G=j?L.controlPlane.store.listResources(L.controlPlane.project.id,j.id).find((ee)=>ee.slug===t):void 0;if(!j||!G)throw Error("Release resource was not found");let Z=L.releases.releases.create({organizationId:L.controlPlane.organization.id,projectId:L.controlPlane.project.id,environmentId:j.id,resourceId:G.id,artifactId:p,kind:A.kind??"compute",sourceSha:A.sourceSha,config:dc(A.config),manifest:dc(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: ${Z.id} (${Z.status})`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{L.controlPlane.store.close()}}),N.command("release:list","List unified release history").option("--env <environment>","Filter environment").action(async(t)=>{let p=await si();try{let A=t.env?p.controlPlane.environments.get(t.env):void 0;c(["ID","Resource","Status","Kind","Artifact","Strategy","Source","Created"],p.releases.releases.list({projectId:p.controlPlane.project.id,environmentId:A?.id}).map((L)=>[L.id,p.controlPlane.store.getResource(L.resourceId)?.slug??L.resourceId,L.status,L.kind,L.artifactId.slice(0,12),L.strategy,L.sourceSha?.slice(0,12)??"—",L.createdAt]))}finally{p.controlPlane.store.close()}}),N.command("release:show <release>","Show provenance, health, approvals, and transitions").action(async(t)=>{let p=await si();try{let A=p.releases.releases.get(t);if(!A)throw Error("Release was not found");e(JSON.stringify({release:A,artifact:p.releases.releases.getArtifact(A.artifactId),approvals:p.releases.releases.approvals(t),transitions:p.releases.releases.transitions(t)},null,2))}finally{p.controlPlane.store.close()}}),N.command("release:compare <left> <right>","Compare artifact, source, config, manifest, and data caveats").action(async(t,p)=>{let A=await si();try{let L=A.releases.releases.compare(t,p);e(JSON.stringify(L,null,2))}finally{A.controlPlane.store.close()}}),N.command("release:capabilities <kind>","Explain supported deployment strategies").option("--health","Readiness gate is configured").option("--replicas <count>","Replica count",{default:"1"}).action((t,p)=>c(["Strategy","Supported","Capacity","Cost","Explanation","Rollback"],Yt({kind:t,hasHealthGate:!!p.health,replicas:Number(p.replicas)}).map((A)=>[A.strategy,A.supported?"yes":"no",`${A.capacityMultiplier}x`,A.costImpact,A.explanation,A.rollback]))),N.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,p,A)=>{let L=await si();try{let j=L.controlPlane.environments.get(String(A.env??"")),G=j?L.controlPlane.store.listResources(L.controlPlane.project.id,j.id).find((ee)=>ee.slug===p):void 0;if(!j||!G)throw Error("Promotion target was not found");let Z=L.releases.releases.promote(t,{targetEnvironmentId:j.id,targetResourceId:G.id,config:dc(A.config),approvalRequired:A.approval});n(`Promoted without rebuild: ${Z.id} (${Z.status})`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{L.controlPlane.store.close()}}),N.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,p)=>{let A=await si();try{if(!p.actor)throw Error("--actor is required");let L=A.releases.releases.approve(t,{actorId:p.actor,decision:p.reject?"rejected":"approved",comment:p.comment});n(`Release is ${L.status}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.command("release:activate <release>","Queue immutable artifact activation").action(async(t)=>{let p=await si();try{let A=p.releases.releases.get(t);if(!A)throw Error("Release was not found");let L=p.releases.enqueueActivation(A);n(`Activation queued: ${L.id}`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{p.controlPlane.store.close()}}),N.command("release:rollback <release>","Restore the preserved prior immutable release").option("--to <release>","Explicit preserved release ID").action(async(t,p)=>{let A=await si();try{let L=A.releases.releases.get(t);if(!L)throw Error("Release was not found");let j=A.releases.enqueueRollback(L,{targetReleaseId:p.to});n(`Rollback queued: ${j.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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,p)=>{let A=await si();try{let L=A.releases.completeHealthGate(t,{healthy:!!p.healthy,operationId:p.operation,message:p.message});n(L.rollbackOperation?`Health failed; rollback queued: ${L.rollbackOperation.id}`:`Release is ${L.release.status}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.command("release:pin <release>","Pin or unpin a release against retention").option("--remove","Remove pin").option("--reason <text>","Pin reason").action(async(t,p)=>{let A=await si();try{let L=A.releases.releases.pin(t,!p.remove,p.reason);n(L.pinned?"Release pinned.":"Release unpinned.")}finally{A.controlPlane.store.close()}})}import{existsSync as cp}from"node:fs";function mc(N){N.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 p=await Me(),A=t?.env||"production",j=p.environments?.[A]?.app?.build??[];if(!j.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:G}=await import("node:child_process");for(let Z of j)m(`$ ${Z}`),G(Z,{stdio:"inherit"});n("Build hooks complete")}catch(p){r(`Build failed: ${p.message}`),process.exitCode=1}});for(let t of["assets:optimize:images","images:optimize"])N.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});N.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 Me()).project.region||"us-east-1",L=t?.source||"dist",j=t?.bucket,G=t?.prefix,Z=t?.delete||!1,ee=t?.cacheControl||"public, max-age=31536000";if(!j){r("--bucket is required");return}if(!cp(L)){r(`Source directory not found: ${L}`);return}if(e(`Source: ${L}`),e(`Bucket: s3://${j}${G?`/${G}`:""}`),e(`Cache-Control: ${ee}`),Z)a("Delete mode enabled - files not in source will be removed");if(!await d(`
370
- Deploy assets now?`,!0)){e("Deployment cancelled");return}let oe=new f(A),be=new o("Uploading assets to S3...");be.start(),await oe.sync({source:L,bucket:j,prefix:G,delete:Z,cacheControl:ee,acl:"public-read"}),be.succeed("Assets deployed successfully!");let Ce=(await oe.getBucketSize(j,G)/1024/1024).toFixed(2);n(`
368
+ `}function yc(N){N.command("env:list","List configured environments and their deployed stack status").action(async()=>{i("Environments");try{let t=await Ne(),m=Object.keys(t.environments??{});if(!m.length){e("No environments configured. Add environments.<env> to your cloud.config.");return}let A=[];for(let L of m){let j=t.environments[L],G=j?.region||t.project.region||"us-east-1",Z=j?.app?`serverless (${j.app.kind})`:"server/static",ee="not deployed";if(j?.app)try{let ne=new v(G),{Stacks:oe}=await ne.describeStacks({stackName:Pe(t,L)});ee=oe?.[0]?.StackStatus??"not deployed"}catch{ee="not deployed"}A.push([L,j?.type??L,Z,G,ee])}c(["Environment","Type","Kind","Region","Stack status"],A)}catch(t){r(`Failed to list environments: ${t.message}`),process.exitCode=1}}),N.command("env:compare <env1> <env2>","Compare two environments' app configuration").action(async(t,m)=>{i(`Comparing ${t} ↔ ${m}`);try{let A=await Ne(),L=A.environments?.[t],j=A.environments?.[m];if(!L||!j){r(`Both environments must exist in config (have: ${Object.keys(A.environments??{}).join(", ")||"none"}).`),process.exitCode=1;return}let G=(be,we="")=>{let Ce={};for(let[De,Se]of Object.entries(be??{})){let xe=we?`${we}.${De}`:De;if(Se&&typeof Se==="object"&&!Array.isArray(Se))Object.assign(Ce,G(Se,xe));else Ce[xe]=Array.isArray(Se)?JSON.stringify(Se):String(Se)}return Ce},Z=G(L),ee=G(j),oe=[...new Set([...Object.keys(Z),...Object.keys(ee)])].sort().filter((be)=>Z[be]!==ee[be]).map((be)=>[be,Z[be]??"—",ee[be]??"—"]);if(!oe.length){n("Identical — no configuration differences.");return}c(["Setting",t,m],oe),e(`
369
+ ${oe.length} difference(s)`)}catch(A){r(`Comparison failed: ${A.message}`),process.exitCode=1}}),N.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 Ne(),A=t?.env||"production",{region:L,functions:j}=ft(m,A),G=t?.function??"http",ne=(await new R(L).getFunction(j[G])).Configuration?.Environment?.Variables??{},oe=t?.file??`.env.${A}`;gm(oe,ym(ne)),n(`Wrote ${Object.keys(ne).length} variable(s) from ${j[G]} → ${oe}`)}catch(m){r(`env:pull failed: ${m.message}`),process.exitCode=1}}),N.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 Ne(),A=t?.env||"production",{region:L,functions:j}=ft(m,A),G=t?.file??`.env.${A}`;if(!mm(G)){r(`File not found: ${G}`),process.exitCode=1;return}let Z=hm(fm(G,"utf8")),ee=(t?.function??"all").toLowerCase();if(ee!=="all"&&!["http","queue","cli"].includes(ee)){r("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let ne=ee==="all"?["http","queue","cli"]:[ee],oe=(m.environments?.[A]?.app?.provisionedConcurrency??0)>0,be=new R(L);for(let we of ne){let Ce=j[we],De=Z;if(!t?.replace)De={...(await be.getFunction(Ce)).Configuration?.Environment?.Variables??{},...Z};if(ao(Ce,De),await be.updateFunctionConfiguration({FunctionName:Ce,Environment:{Variables:De}}),await be.waitForFunctionActive(Ce,120),oe){let Se=await be.publishVersion({FunctionName:Ce});if(Se.Version)await be.updateAlias({FunctionName:Ce,Name:"live",FunctionVersion:Se.Version})}n(`Pushed ${Object.keys(Z).length} var(s) → ${Ce}${t?.replace?" (replaced)":" (merged)"}${oe?" (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}}),N.command("env:create <name>","Create new environment").action(async(t)=>{hc(`env:create ${t}`,"Add an `environments.<name>` block to your cloud.config and run `cloud deploy:serverless --env <name>`.")}),N.command("env:switch <name>","Switch active environment").action(async(t)=>{hc(`env:switch ${t}`,'ts-cloud has no persistent "active" environment — pass --env <name> to each command.')});for(let t of["clone","promote","sync"])N.command(`env:${t} <source> <target>`,`(${t}) environment configuration`).action(async(m,A)=>{hc(`env:${t} ${m} ${A}`,`Copy the \`environments.${m}\` block to \`environments.${A}\` in cloud.config, then \`cloud deploy:serverless --env ${A}\`.`)});N.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 L=await Ne(),j=m.env||"production",G=m.site||Object.keys(L.sites??{})[0];if(!G)throw Error("No application site is configured; pass --site after adding one");A=y(process.cwd(),L);let Z=A.environments.get(j),ee=Z?A.store.listResources(A.project.id,Z.id).find((Se)=>Se.kind==="application"&&Se.slug===G):void 0;if(!Z||!ee)throw Error(`Application ${G} was not found in base environment ${j}`);let ne=new qe(A.store),oe=ne.previews.getDefinitionForResource(ee.id);if(!oe){let Se=L.sites?.[G]?.domain,xe=m.domain||(Se?`https://{name}.preview.${Se}`:void 0);if(!xe)throw Error("Pass --domain with an HTTPS pattern containing {name} to configure previews");oe=ne.previews.createDefinition({projectId:A.project.id,resourceId:ee.id,baseEnvironmentId:Z.id,domainPattern:xe,ttlHours:Number(m.ttl)||void 0})}let be=Number(m.pr)||void 0,we=be?ne.previews.findForPullRequest(oe.id,"local",be):ne.previews.findForBranch(oe.id,"local",t);if(m.getUrl){if(!we)throw Error("Preview was not found");e(we.url??"URL unavailable");return}if(m.extend){if(!we)throw Error("Preview was not found");let Se=ne.previews.extend(we.id,Number(m.extend));n(`Extended ${Se.name} until ${Se.expiresAt}`);return}if(m.destroy){if(!we)throw Error("Preview was not found");if(!m.yes&&!await d(`Destroy ${we.name} and only its tagged resources?`,!1))return;let Se=ne.enqueueDestroy(we,"manual");n(`Preview teardown queued: ${Se.id}`);return}if(m.rebuild){if(!we)throw Error("Preview was not found");let Se=ne.enqueueDeploy(we,{reason:"manual_rebuild"});n(`Preview rebuild queued: ${Se.id}`);return}if(!m.sha)throw Error("Pass --sha with the exact immutable commit to deploy");let Ce=ne.previews.upsert({definitionId:oe.id,repository:"local",branch:t,pullRequestNumber:be,commitSha:m.sha}),De=ne.enqueueDeploy(Ce.preview,{created:Ce.created});n(`${Ce.created?"Preview creation":"Preview update"} queued: ${De.id}`),e(`${Ce.preview.url} · expires ${Ce.preview.expiresAt}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A?.store.close()}}),N.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 Ne();m=y(process.cwd(),A);let j=new qe(m.store).cleanup({dryRun:t.dryRun,maxAgeHours:Number(t.maxAge)||void 0,keepCount:Number(t.keep)||void 0});c(["Preview","Status","Expires","Reason"],j.candidates.map((G)=>[G.preview.name,G.preview.status,G.preview.expiresAt,G.reasons.join(", ")])),n(t.dryRun?`${j.candidates.length} cleanup candidate(s)`:`${j.operations.length} teardown job(s) queued`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m?.store.close()}}),N.command("env:previews","List persistent preview environments").option("--site <name>","Filter application site").action(async(t)=>{let m;try{let A=await Ne();m=y(process.cwd(),A);let L=new qe(m.store),j=t.site?new Set(m.store.listResources(m.project.id).filter((Z)=>Z.slug===t.site).map((Z)=>Z.id)):void 0,G=L.previews.listInstances({projectId:m.project.id}).filter((Z)=>!j||j.has(Z.resourceId));c(["ID","Name","Status","Branch / PR","Commit","URL","Expires","Cost"],G.map((Z)=>[Z.id,Z.name,Z.status,Z.pullRequestNumber?`PR #${Z.pullRequestNumber}`:Z.branch,Z.commitSha.slice(0,12),Z.url??"—",Z.expiresAt,Z.costEstimate==null?"—":`$${Z.costEstimate.toFixed(2)}`]))}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m?.store.close()}})}import{readFileSync as _s}from"node:fs";async function li(){let N=await Ne(),t=y(process.cwd(),N);return{config:N,controlPlane:t,applications:new ut(t.store)}}async function vc(N,t,m){let A=await li();try{let L=A.applications.get(N);if(!L)throw Error("Compose application was not found");let j=t??A.controlPlane.store.listEnvironments(L.projectId).find((Z)=>Z.id===L.environmentId)?.slug;if(!j)throw Error("Compose environment was not found");let G=await Yt(A.config,j,m(L));if(!G.ok)throw Error(G.stderr||G.error||"Remote Compose command failed");if(G.stdout)e(G.stdout)}finally{A.controlPlane.store.close()}}function bc(N){N.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 li();try{let L=A.controlPlane.environments.get(m.env??"production");if(!L)throw Error("Environment was not found");let j=A.applications.preview(_s(t,"utf8"),{name:m.name??"Compose application",projectId:A.controlPlane.project.id,environmentId:L.id});if(c(["Severity","Path","Code","Message"],j.diagnostics.map((G)=>[G.severity,G.path,G.code,G.message])),!j.valid)process.exitCode=1;else n(`${Object.keys(j.manifest.spec.services).length} services are valid`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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 li();try{let L=A.controlPlane.environments.get(m.env??"production");if(!L)throw Error("Environment was not found");let j=m.name??t.split("/").at(-2)??"Compose application",G=A.applications.import(_s(t,"utf8"),{name:j,projectId:A.controlPlane.project.id,environmentId:L.id});n(`Imported ${G.application.name} (${G.application.id})`),e(`${Object.keys(G.application.manifest.spec.services).length} services · ${G.parsed.diagnostics.length} diagnostic(s)`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.command("compose:templates","List pinned verified Compose templates").action(()=>c(["ID","Version","Category","Architecture","Verified"],lr().map((t)=>[t.id,t.version,t.category,t.architecture,t.lastVerifiedAt]))),N.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 li();try{let L=A.controlPlane.environments.get(m.env??"production");if(!L)throw Error("Environment was not found");let j=Da(_s(t,"utf8"),{projectId:A.controlPlane.project.id,environmentId:L.id});if(c(["ID","Version","Category","Services","Checksum"],j.templates.map((G)=>[G.template.id,G.template.version,G.template.category,String(Object.keys(G.parsed.manifest.spec.services).length),G.template.checksum.slice(0,12)])),j.diagnostics.length)c(["Severity","Path","Message"],j.diagnostics.map((G)=>[G.severity,G.path,G.message]));if(!j.valid)process.exitCode=1}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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 li();try{let L=A.controlPlane.environments.get(m.env??"production");if(!L)throw Error("Environment was not found");let j=A.applications.fromTemplate(t,{domain:m.domain??""},{name:m.name??t,projectId:A.controlPlane.project.id,environmentId:L.id,version:m.version});n(`Created ${j.application.name} from ${t}@${j.application.templateVersion}`),e(`Export with cloud compose:export ${j.application.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.command("compose:list","List Compose applications").action(async()=>{let t=await li();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()}}),N.command("compose:export <application>","Print a normalized editable Compose file").action(async(t)=>{let m=await li();try{let A=m.applications.get(t);if(!A)throw Error("Compose application was not found");e(Gn(A.manifest))}finally{m.controlPlane.store.close()}}),N.command("compose:diff <application> <file>","Compare persisted and proposed Compose manifests").action(async(t,m)=>{let A=await li();try{let L=A.applications.get(t);if(!L)throw Error("Compose application was not found");let j=Nt(_s(m,"utf8"),L.manifest.metadata);c(["Path","Before","After"],Yo(L.manifest,j.manifest).map((G)=>[G.path,JSON.stringify(G.before),JSON.stringify(G.after)]))}finally{A.controlPlane.store.close()}});for(let t of["deploy","redeploy","start","stop"])N.command(`compose:${t} <application>`,`${t} a Compose application through the durable queue`).action(async(m)=>{let A=await li();try{let L=A.applications.get(m);if(!L)throw Error("Compose application was not found");let j=new He(A.controlPlane.store).enqueue(L,t);n(`${t} queued: ${j.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}});N.command("compose:scale <application> <service> <replicas>","Scale one capable Compose service").action(async(t,m,A)=>{let L=await li();try{let j=L.applications.get(t);if(!j)throw Error("Compose application was not found");let G=new He(L.controlPlane.store).enqueue(j,"scale",{service:m,replicas:Number(A)});n(`Scale queued: ${G.id}`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{L.controlPlane.store.close()}}),N.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 li();try{let L=A.applications.get(t);if(!L)throw Error("Compose application was not found");let j=new He(A.controlPlane.store).enqueue(L,"delete",{removeVolumes:m.removeVolumes,confirmation:m.confirm});n(`Deletion queued: ${j.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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)=>vc(t,A.env,(L)=>Kn(L.manifest,m,Number(A.lines)||200))),N.command("compose:shell <application> <service> [command...]","Run a command in one service").option("--env <environment>","Target environment").action((t,m,A,L)=>vc(t,L.env,(j)=>Jn(j.manifest,m,A?.length?A:["sh"]))),N.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)=>vc(t,m.env,(A)=>{if(m.action==="scale")return _a(A.manifest,m.service??"",Number(m.replicas));return ka(A.manifest,m.action??"deploy",{service:m.service,removeVolumes:m.removeVolumes})}))}import{readFileSync as vm}from"node:fs";async function di(){let N=await Ne(),t=y(process.cwd(),N);return{controlPlane:t,releases:new nt(t.store)}}function wc(N){if(!N)return{};return JSON.parse(vm(N,"utf8"))}function Sc(N){N.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 di();try{let L=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: ${L.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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 L=await di();try{let j=L.controlPlane.environments.get(A.env??"production"),G=j?L.controlPlane.store.listResources(L.controlPlane.project.id,j.id).find((ee)=>ee.slug===t):void 0;if(!j||!G)throw Error("Release resource was not found");let Z=L.releases.releases.create({organizationId:L.controlPlane.organization.id,projectId:L.controlPlane.project.id,environmentId:j.id,resourceId:G.id,artifactId:m,kind:A.kind??"compute",sourceSha:A.sourceSha,config:wc(A.config),manifest:wc(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: ${Z.id} (${Z.status})`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{L.controlPlane.store.close()}}),N.command("release:list","List unified release history").option("--env <environment>","Filter environment").action(async(t)=>{let m=await di();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((L)=>[L.id,m.controlPlane.store.getResource(L.resourceId)?.slug??L.resourceId,L.status,L.kind,L.artifactId.slice(0,12),L.strategy,L.sourceSha?.slice(0,12)??"—",L.createdAt]))}finally{m.controlPlane.store.close()}}),N.command("release:show <release>","Show provenance, health, approvals, and transitions").action(async(t)=>{let m=await di();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()}}),N.command("release:compare <left> <right>","Compare artifact, source, config, manifest, and data caveats").action(async(t,m)=>{let A=await di();try{let L=A.releases.releases.compare(t,m);e(JSON.stringify(L,null,2))}finally{A.controlPlane.store.close()}}),N.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"],Zt({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]))),N.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 L=await di();try{let j=L.controlPlane.environments.get(String(A.env??"")),G=j?L.controlPlane.store.listResources(L.controlPlane.project.id,j.id).find((ee)=>ee.slug===m):void 0;if(!j||!G)throw Error("Promotion target was not found");let Z=L.releases.releases.promote(t,{targetEnvironmentId:j.id,targetResourceId:G.id,config:wc(A.config),approvalRequired:A.approval});n(`Promoted without rebuild: ${Z.id} (${Z.status})`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{L.controlPlane.store.close()}}),N.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 di();try{if(!m.actor)throw Error("--actor is required");let L=A.releases.releases.approve(t,{actorId:m.actor,decision:m.reject?"rejected":"approved",comment:m.comment});n(`Release is ${L.status}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.command("release:activate <release>","Queue immutable artifact activation").action(async(t)=>{let m=await di();try{let A=m.releases.releases.get(t);if(!A)throw Error("Release was not found");let L=m.releases.enqueueActivation(A);n(`Activation queued: ${L.id}`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}finally{m.controlPlane.store.close()}}),N.command("release:rollback <release>","Restore the preserved prior immutable release").option("--to <release>","Explicit preserved release ID").action(async(t,m)=>{let A=await di();try{let L=A.releases.releases.get(t);if(!L)throw Error("Release was not found");let j=A.releases.enqueueRollback(L,{targetReleaseId:m.to});n(`Rollback queued: ${j.id}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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 di();try{let L=A.releases.completeHealthGate(t,{healthy:!!m.healthy,operationId:m.operation,message:m.message});n(L.rollbackOperation?`Health failed; rollback queued: ${L.rollbackOperation.id}`:`Release is ${L.release.status}`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}finally{A.controlPlane.store.close()}}),N.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 di();try{let L=A.releases.releases.pin(t,!m.remove,m.reason);n(L.pinned?"Release pinned.":"Release unpinned.")}finally{A.controlPlane.store.close()}})}import{existsSync as bm}from"node:fs";function $c(N){N.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 Ne(),A=t?.env||"production",j=m.environments?.[A]?.app?.build??[];if(!j.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:G}=await import("node:child_process");for(let Z of j)p(`$ ${Z}`),G(Z,{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"])N.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});N.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 Ne()).project.region||"us-east-1",L=t?.source||"dist",j=t?.bucket,G=t?.prefix,Z=t?.delete||!1,ee=t?.cacheControl||"public, max-age=31536000";if(!j){r("--bucket is required");return}if(!bm(L)){r(`Source directory not found: ${L}`);return}if(e(`Source: ${L}`),e(`Bucket: s3://${j}${G?`/${G}`:""}`),e(`Cache-Control: ${ee}`),Z)a("Delete mode enabled - files not in source will be removed");if(!await d(`
370
+ Deploy assets now?`,!0)){e("Deployment cancelled");return}let oe=new g(A),be=new o("Uploading assets to S3...");be.start(),await oe.sync({source:L,bucket:j,prefix:G,delete:Z,cacheControl:ee,acl:"public-read"}),be.succeed("Assets deployed successfully!");let Ce=(await oe.getBucketSize(j,G)/1024/1024).toFixed(2);n(`
371
371
  Deployment complete!`),e(`Total size: ${Ce} MB`),e(`
372
- Assets URL: https://${j}.s3.${A}.amazonaws.com${G?`/${G}`:""}`)}catch(p){r(`Deployment failed: ${p.message}`)}}),N.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 p=t?.distribution;if(!p){r("--distribution is required");return}let L=(t?.paths||"/*").split(",").map((oe)=>oe.trim()),j=t?.wait||!1;if(e(`Distribution: ${p}`),e(`Paths: ${L.join(", ")}`),!await d(`
373
- Invalidate cache now?`,!0)){e("Invalidation cancelled");return}let Z=new w,ee=new o("Creating invalidation...");ee.start();let re=await Z.invalidatePaths(p,L);if(ee.succeed("Invalidation created"),n(`
374
- Invalidation ID: ${re.Id}`),e(`Status: ${re.Status}`),e(`Created: ${new Date(re.CreateTime).toLocaleString()}`),j){let oe=new o("Waiting for invalidation to complete...");oe.start(),await Z.waitForInvalidation(p,re.Id),oe.succeed("Invalidation completed!")}else e(`
375
- Invalidation is in progress. Use --wait to wait for completion.`)}catch(p){r(`Invalidation failed: ${p.message}`)}})}function pc(N){for(let[t,p]of[["team:add <email> <role>","Invite an organization member"],["team:list","List organization members"],["team:remove <email>","Revoke organization membership"]])N.command(t,p).action(async(...A)=>Ar(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 yp,execSync as Cc}from"node:child_process";import{copyFileSync as ms,existsSync as fr,mkdirSync as sd,readFileSync as $c,writeFileSync as vp}from"node:fs";import{tmpdir as ad}from"node:os";import{dirname as bp,join as cd,resolve as wp}from"node:path";import{existsSync as fc,readdirSync as td,statSync as gc}from"node:fs";import{join as Fs}from"node:path";var lp=new Set([".DS_Store","Thumbs.db",".gitkeep",".keep"]);function nd(N){return lp.has(N)}function rd(N,t=0){if(t>8)return!0;let p;try{p=td(N)}catch{return!1}for(let A of p){if(nd(A))continue;let L=Fs(N,A),j;try{j=gc(L)}catch{continue}if(j.isFile())return!0;if(j.isDirectory()&&rd(L,t+1))return!0}return!1}function dp(N){let t;try{t=td(N)}catch{return[]}let p=[];for(let A of t){if(nd(A))continue;let L=Fs(N,A);try{if(gc(L).isDirectory()&&fc(Fs(L,"index.html")))p.push(A)}catch{}}return p.sort((A,L)=>A.localeCompare(L))}function id(N){let{root:t,siteName:p,expectIndex:A}=N,L=[];if(!fc(t))return L.push({level:"error",message:`Build output not found at ${t} for site '${p}'`}),L;if(!gc(t).isDirectory())return L.push({level:"error",message:`Site '${p}' has root ${t}, which is a file, not a directory`}),L;if(!rd(t))return L.push({level:"error",message:`Site '${p}' would ship an empty release: ${t} contains no files. Check that the build ran and wrote where 'root' points.`}),L;if(!A)return L;if(fc(Fs(t,"index.html")))return L;let j=dp(t);if(j.length===1){let G=`${t.replace(/\/+$/,"")}/${j[0]}`;return L.push({level:"error",message:`Site '${p}' has no index.html at the top of ${t}, but ${G} has one. The generator wrote one level down, so this release would serve 404s at the domain root. Set root: '${G}'.`}),L}if(j.length>1)return L.push({level:"warning",message:`Site '${p}' has no index.html at the top of ${t}. These subdirectories have one: ${j.join(", ")}. The domain root will 404.`}),L;return L.push({level:"warning",message:`Site '${p}' has no index.html anywhere under ${t}. The domain root will 404.`}),L}function up(N){if(!N)return[];if(Array.isArray(N))return N.map(String);let t=N,p=t.Items??t.Item;if(!p)return[];if(Array.isArray(p))return p.map(String);if(typeof p==="object"&&p!==null){let A=p;if("Item"in A){let L=A.Item;if(!L)return[];return Array.isArray(L)?L.map(String):[String(L)]}if("CNAME"in A){let L=A.CNAME;if(!L)return[];return Array.isArray(L)?L.map(String):[String(L)]}return Object.values(A).flatMap((L)=>{if(Array.isArray(L))return L.map(String);return typeof L==="string"?[L]:[]})}return[String(p)]}async function Bs(N){if(N.length===0)return;let t=new w,p=await t.listDistributions(),A=[...new Set(N.map((L)=>L.toLowerCase()))];for(let L of A){let j=p.find((Z)=>up(Z.Aliases).some((re)=>re.toLowerCase()===L));if(!j?.Id){a(`No CloudFront distribution found for ${L} — skipping dynamic HTTP method sync`);continue}if(await t.ensureDynamicHttpMethods(j.Id))n(`CloudFront ${j.Id} (${L}): enabled POST/PUT/PATCH/DELETE on dynamic paths`);else e(`CloudFront ${j.Id} (${L}): already allows dynamic HTTP methods`)}}import{execSync as mp}from"node:child_process";async function pp(N,t,p,A={},L=process.cwd()){if(!N)return;if(A.step?.(`Running ${p} hook`),typeof N==="string"){mp(N,{stdio:"inherit",cwd:L});return}await N(t)}async function cs(N,t,p={},A){try{return await pp(N.hooks?.[t],N,t,p,A),!0}catch(L){return p.error?.(`${t} hook failed: ${L?.message||L}`),!1}}var bi=(N)=>N===void 0?void 0:N?"on":"off";function hc(N){let t={ssl:N.ssl,always_use_https:bi(N.alwaysUseHttps),automatic_https_rewrites:bi(N.automaticHttpsRewrites),min_tls_version:N.minTlsVersion,tls_1_3:bi(N.tls13),brotli:bi(N.brotli),http3:bi(N.http3),"0rtt":bi(N.zeroRtt),early_hints:bi(N.earlyHints),browser_cache_ttl:N.browserCacheTtl,always_online:bi(N.alwaysOnline),websockets:bi(N.websockets),email_obfuscation:bi(N.emailObfuscation)};if(N.hsts)t.security_header={strict_transport_security:{enabled:N.hsts.enabled,max_age:N.hsts.maxAge??31536000,include_subdomains:N.hsts.includeSubdomains??!1,preload:N.hsts.preload??!1,nosniff:N.hsts.noSniff??!0}};for(let p of Object.keys(t))if(t[p]===void 0)delete t[p];return{...t,...N.raw??{}}}var ls={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 fp}from"node:tls";async function yc(N){let{address:t,serverName:p,port:A=443,timeoutMs:L=5000}=N;return new Promise((j)=>{let G=!1,Z=(re)=>{if(G)return;G=!0;try{ee.destroy()}catch{}j(re)},ee=fp({host:t,port:A,servername:p,rejectUnauthorized:!1,timeout:L});ee.once("secureConnect",()=>{if(ee.authorized){Z({reachable:!0,trusted:!0});return}Z({reachable:!0,trusted:!1,reason:ee.authorizationError?String(ee.authorizationError):"origin certificate is not trusted for this hostname"})}),ee.once("timeout",()=>{Z({reachable:!1,trusted:!1,reason:`no TLS handshake within ${L}ms`})}),ee.once("error",(re)=>{Z({reachable:!1,trusted:!1,reason:re.message})})})}function gp(N,t){let p=t.replace(/\.$/,"").toLowerCase();return N.some((A)=>{let L=A.replace(/\.$/,"").toLowerCase();if(L===p)return!0;if(!L.startsWith("*."))return!1;let j=L.slice(2);if(!p.endsWith(`.${j}`))return!1;return!p.slice(0,p.length-j.length-1).includes(".")})}async function vc(N){let{provider:t,zone:p,hosts:A,ipv4:L,ipv6:j,proxied:G=!0,settings:Z=ls,cache:ee,originGuard:re,purge:oe=!0,ttl:be=300,skipOriginProbe:we=!1,skipEdgeCertificateCheck:Ce=!1}=N,Ee={records:[],settingsChanged:[],cacheRules:0,originGuard:!1,purged:!1,deferredProxy:[],warnings:[]};if(A.length===0)return Ee;let Se=[],De=G&&!Ce?await t.listEdgeCertificateHosts(p):null;for(let Ae of A){let Te=G;if(Te&&De&&!gp(De,Ae)){Te=!1;let Xe=De.length===0?"the zone has no active edge certificate yet":`no active edge certificate covers it (${De.join(", ")})`;Ee.deferredProxy.push({host:Ae,reason:Xe}),Ee.warnings.push(`${Ae}: publishing DNS-only — ${Xe}. 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(Te&&!we){let Xe=await yc({address:L,serverName:Ae});if(!Xe.trusted){Te=!1;let Ge=Xe.reason||"origin is not serving a trusted certificate";Ee.deferredProxy.push({host:Ae,reason:Ge}),Ee.warnings.push(`${Ae}: publishing DNS-only for now — the origin does not yet serve a certificate this proxy can verify (${Ge}). 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(Te)Se.push(Ae);let Ne=[{type:"A",content:L}];if(j&&Ai(Ae))Ne.push({type:"AAAA",content:j});for(let{type:Xe,content:Ge}of Ne){let Ye=await t.upsertRecord(p,{name:Ae,type:Xe,content:Ge,ttl:be,proxied:Te});if(Ye.success)Ee.records.push({host:Ae,type:Xe,content:Ge,proxied:Te});else Ee.warnings.push(`${Ae} ${Xe} → ${Ge} failed: ${Ye.message||"unknown provider error"}`)}}if(Se.length===0){if(G)Ee.warnings.push("No host is proxied yet, so zone settings, cache rules and purge were skipped — they only take effect through the proxy.");return Ee}try{let Ae=hc(Z),Te=await t.applyZoneSettings(p,Ae);Ee.settingsChanged=Te.changed;for(let Ne of Te.failed)Ee.warnings.push(`zone setting '${Ne.id}' could not be applied: ${Ne.error}`)}catch(Ae){Ee.warnings.push(`zone settings could not be read: ${Ae instanceof Error?Ae.message:String(Ae)}`)}let _e=ds(Se,ee);if(_e.length>0){let Ae=await t.putManagedPhaseRules(p,"http_request_cache_settings",_e);if(Ae.success)Ee.cacheRules=_e.length;else Ee.warnings.push(`cache rules could not be written: ${Ae.message}`)}if(re){let Ae=us(Se,re.header,re.value);if(Ae.length>0){let Te=await t.putManagedPhaseRules(p,"http_request_late_transform",Ae);if(Te.success)Ee.originGuard=!0;else Ee.warnings.push(`origin guard header could not be written: ${Te.message}. `+"The gateway will reject requests that lack it — disable the guard or grant the token Transform Rules: Edit.")}}if(oe){if(Ee.purged=await t.purgeCache(p,{hosts:Se}),!Ee.purged)Ee.warnings.push("edge cache purge failed — new content may take until TTL expiry to appear.")}return Ee}var hp=(N)=>process.env[N];function od(N,t){let p=N.replace(/\.$/,"").toLowerCase();if(t){let A=t.replace(/\.$/,"").toLowerCase();if(p===A||p.endsWith(`.${A}`))return A}return p.split(".").slice(-2).join(".")}function bc(N,t={}){let p=t.env??hp,A=N.infrastructure?.compute?.proxy?.cdn,L=[];if(!A||A.provider!=="cloudflare")return{plan:null,errors:L};if(A.originDomain)L.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 j=p("CLOUDFLARE_API_TOKEN");if(!j)return L.push("CLOUDFLARE_API_TOKEN is not set — the Cloudflare CDN cannot be reconciled."),{plan:null,errors:L};let G=N.sites??{},Z=A.frontedHosts?.length?[...new Set(A.frontedHosts.map((we)=>we.toLowerCase()))]:Pi(G,{autoWww:N.infrastructure?.compute?.proxy?.autoWww!==!1});if(Z.length===0)return L.push("No hostnames to front — set compute.proxy.cdn.frontedHosts, or give a site a domain."),{plan:null,errors:L};let ee=N.infrastructure?.dns?.domain,re=new Set(Z.map((we)=>od(we,ee)));if(re.size>1)return L.push(`Fronted hosts span more than one zone (${[...re].join(", ")}). A Cloudflare zone is configured as a unit, so run one CDN config per zone.`),{plan:null,errors:L};let oe=A.cloudflare??{},be=A.secret?{header:A.secretHeader??"X-Origin-Verify",value:A.secret}:void 0;return{plan:{zone:[...re][0],hosts:Z,apiToken:j,zoneId:oe.zoneId??p("CLOUDFLARE_ZONE_ID"),accountId:oe.accountId??p("CLOUDFLARE_ACCOUNT_ID"),proxied:oe.proxied!==!1,settings:oe.settings??ls,cache:oe.cache,originGuard:be,purge:oe.purgeOnDeploy!==!1,skipOriginProbe:oe.skipOriginProbe===!0},errors:L}}async function Sp(N){let t=_s();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 st(N).getCallerIdentity();if(A.Arn)e(`AWS identity: ${A.Arn}`)}catch(p){a(`Could not verify AWS identity: ${p.message}`)}}function $p(){try{return Cc("git rev-parse --short HEAD",{stdio:["ignore","pipe","ignore"]}).toString().trim()}catch{return Date.now().toString(36)}}async function wc(N,t,p,A){let{ensureManagementDashboard:L}=await import("./chunk-fddwwac7.js");L(N,{logger:{info:e,warn:a}});let j=N.sites||{},G=Object.entries(j);if(G.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&&!j[A])return r(`Site '${A}' was not found. Configured sites: ${G.map(([_e])=>_e).join(", ")}`),!1;let Z=G.filter(([_e,Ae])=>{if(!Ae)return!1;if(A&&_e!==A)return!1;if(R(Ae)==="bucket")return a(`Site '${_e}' targets a bucket — skipping compute deploy (handled by the static-site path).`),!1;return ys(Ae)});if(A)e(`Deploying only '${A}'. Other sites keep their current release; proxy routes are still regenerated in full.`);let ee=N.project.slug,oe=(N.infrastructure?.compute||{}).runtime||"bun",be=x({config:N}),we=$p(),Ce=new Map,Ee=new Map,Se={step:(_e)=>m(_e),error:(_e)=>r(_e)};if(!await cs(N,"beforeDeploy",Se))return!1;if(!await cs(N,"beforeBuild",Se))return!1;for(let[_e,Ae]of Z){let Te=R(Ae);if(i(`Preparing site: ${_e}`),e(`Domain: ${Ae.domain||"(none)"}`),e(`Source: ${Ae.root}`),e(`Kind: ${Te==="server-static"?"static (shipped to /var/www)":"app (systemd service)"}`),Te==="server-app")e(`Start: ${Ae.start}`),e(`Port: ${Ae.port??"(unset)"}`);if(Ae.build){m(`Running build: ${Ae.build}`);try{Cc(Ae.build,{stdio:"inherit",cwd:process.cwd()})}catch(et){return r(`Build failed for site '${_e}': ${et.message}`),!1}}let Ne=id({root:Ae.root,siteName:_e,expectIndex:Te==="server-static"});for(let et of Ne)if(et.level==="error")r(et.message);else a(et.message);if(Ne.some((et)=>et.level==="error"))return!1;let Xe=Ae.build?void 0:JSON.stringify([wp(Ae.root),[...Ae.exclude||[]].sort()]),Ge=Xe?Ee.get(Xe):void 0;if(Ge){m(`Reusing package for ${Ae.root} → ${Ge}`),Ce.set(_e,Ge);continue}let Ye=cd(ad(),`${ee}-${_e}-${we}.tar.gz`);m(`Packaging ${Ae.root} → ${Ye}`);try{let et=(Ae.exclude||[]).map((Pt)=>`--exclude=${Pt}`);yp("tar",["czf",Ye,"-C",Ae.root,...et,"."],{stdio:"inherit",env:{...process.env,COPYFILE_DISABLE:"1"}})}catch(et){return r(`Failed to package '${_e}': ${et.message}`),!1}if(Ce.set(_e,Ye),Xe)Ee.set(Xe,Ye)}if(!await cs(N,"afterBuild",Se))return!1;let De=await $i({config:A?{...N,sites:Object.fromEntries(Z)}:N,rpxConfig:A?N:void 0,environment:t,driver:be,sha:we,runtime:oe,tarballForSite:(_e)=>{let Ae=Ce.get(_e);if(!Ae)throw Error(`Missing tarball for site '${_e}'`);return Ae},logger:{info:(_e)=>e(_e),warn:(_e)=>a(_e),error:(_e)=>r(_e),step:(_e)=>m(_e),success:(_e)=>n(_e)}});if(De&&!await cs(N,"afterDeploy",Se))return!1;return De}async function Ms(N){let t=new xr;if(m("Running pre-deployment security scan..."),N.exclude?.length)e(`Excluding from the scan: ${N.exclude.join(", ")}`);let p=await t.scan({directory:N.sourceDir,failOnSeverity:N.failOnSeverity||"critical",skipPatterns:N.skipPatterns,exclude:N.exclude});return{passed:p.passed,result:p}}function Cp(){let N=cd(ad(),`ts-cloud-empty-scan-${process.pid}`);return sd(N,{recursive:!0}),N}function qs(N){let{summary:t,findings:p,scannedFiles:A,duration:L}=N;if(e(`
376
- Scanned ${A} files in ${L}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}`),p.length>0){e(`
377
- Findings:`);let j=p.filter((oe)=>oe.pattern.severity==="critical"),G=p.filter((oe)=>oe.pattern.severity==="high"),Z=p.filter((oe)=>oe.pattern.severity==="medium"),ee=p.filter((oe)=>oe.pattern.severity==="low"),re=(oe,be,we)=>{if(oe.length>0){console.log(`
378
- ${Mt(`[${be}]`,we)}`);for(let Ce of oe.slice(0,10))e(` ${Ce.pattern.name}`),e(` File: ${Ce.file}:${Ce.line}`),e(` Match: ${Ce.match}`);if(oe.length>10)e(` ... and ${oe.length-10} more ${be.toLowerCase()} findings`)}};re(j,"CRITICAL","red"),re(G,"HIGH","yellow"),re(Z,"MEDIUM","blue"),re(ee,"LOW","gray")}}function ld(N,t){let p=Object.keys(N.environments??{});if(t&&p.includes(t))return t;if(p.includes("production"))return"production";if(p[0])return p[0];throw Error("A configured environment is required for security policy evaluation")}function Sc(N,t,p){let A=ld(N,t),L=y(process.cwd(),N);try{let j=new Rt(L.store);$n(L);let G=ae(L,A);if(p)da(j,G,p);else ua(j,G);let Z=Zt(j,{scope:G,desiredConfigHash:L.project.desiredConfigHash});return e(`Security policy: ${Z.decision.policyId} v${Z.decision.policyVersion} — ${Z.decision.outcome.toUpperCase()}`),e(`Security decision: ${Z.decision.explanation}`),{allowed:Z.decision.outcome!=="block",explanation:Z.decision.explanation}}finally{L.store.close()}}async function Ap(N,t){let p=ld(N),A=y(process.cwd(),N);try{let L=new Rt(A.store);$n(A);let j=ae(A,p),G=t.imageSha256?`${t.imageRef}@sha256:${t.imageSha256.replace(/^sha256:/,"")}`:t.imageRef;m("Scanning the built image and producing release security artifacts...");let Z=await pa(L,{scope:j,releaseId:G,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: ${Z.scan.run.status} (${Z.scan.findings.length} findings)`),e(`Release SBOM: ${Z.sbomSource==="image"?"full image inventory":Z.sbomSource==="manifest"?"package-manifest fallback":"unavailable"}`);let ee=Zt(L,{scope:j,operationId:t.invocationId,desiredConfigHash:A.project.desiredConfigHash});return e(`Release policy: ${ee.decision.policyId} v${ee.decision.policyVersion} — ${ee.decision.outcome.toUpperCase()}`),e(`Release decision: ${ee.decision.explanation}`),{allowed:ee.decision.outcome!=="block",explanation:ee.decision.explanation}}finally{A.store.close()}}function kp(N){try{let t=y(process.cwd(),N),p=new qt(t.store).current();if(!p.attackMode&&!p.mitigationPause&&p.ipRules.allow.length===0&&p.ipRules.block.length===0)return N;let A=_o(N,p);return e(`Applying protection controls: ${p.ipRules.allow.length} allow, ${p.ipRules.block.length} block${p.attackMode?", attack mode ON":""}${p.mitigationPause?", mitigation PAUSED":""}`),A}catch{return N}}function Ac(N){N.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 p=t?.source||".",A=t?.failOn||"critical",L=t?.skipPatterns?.split(",").map((Z)=>Z.trim())||[];if(!fr(p)){r(`Source directory not found: ${p}`);return}e(`Source: ${p}`),e(`Fail on: ${A} or higher severity`);let{passed:j,result:G}=await Ms({sourceDir:p,failOnSeverity:A,skipPatterns:L});if(qs(G),j)n(`
372
+ Assets URL: https://${j}.s3.${A}.amazonaws.com${G?`/${G}`:""}`)}catch(m){r(`Deployment failed: ${m.message}`)}}),N.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 L=(t?.paths||"/*").split(",").map((oe)=>oe.trim()),j=t?.wait||!1;if(e(`Distribution: ${m}`),e(`Paths: ${L.join(", ")}`),!await d(`
373
+ Invalidate cache now?`,!0)){e("Invalidation cancelled");return}let Z=new w,ee=new o("Creating invalidation...");ee.start();let ne=await Z.invalidatePaths(m,L);if(ee.succeed("Invalidation created"),n(`
374
+ Invalidation ID: ${ne.Id}`),e(`Status: ${ne.Status}`),e(`Created: ${new Date(ne.CreateTime).toLocaleString()}`),j){let oe=new o("Waiting for invalidation to complete...");oe.start(),await Z.waitForInvalidation(m,ne.Id),oe.succeed("Invalidation completed!")}else e(`
375
+ Invalidation is in progress. Use --wait to wait for completion.`)}catch(m){r(`Invalidation failed: ${m.message}`)}})}function Cc(N){for(let[t,m]of[["team:add <email> <role>","Invite an organization member"],["team:list","List organization members"],["team:remove <email>","Revoke organization membership"]])N.command(t,m).action(async(...A)=>Vr(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 Pm,execSync as Nc}from"node:child_process";import{copyFileSync as gs,existsSync as yr,mkdirSync as hd,readFileSync as Tc,writeFileSync as xm}from"node:fs";import{tmpdir as yd}from"node:os";import{dirname as Dm,join as vd,resolve as Em}from"node:path";import{existsSync as Ac,readdirSync as ud,statSync as kc}from"node:fs";import{join as Vs}from"node:path";var wm=new Set([".DS_Store","Thumbs.db",".gitkeep",".keep"]);function pd(N){return wm.has(N)}function md(N,t=0){if(t>8)return!0;let m;try{m=ud(N)}catch{return!1}for(let A of m){if(pd(A))continue;let L=Vs(N,A),j;try{j=kc(L)}catch{continue}if(j.isFile())return!0;if(j.isDirectory()&&md(L,t+1))return!0}return!1}function Sm(N){let t;try{t=ud(N)}catch{return[]}let m=[];for(let A of t){if(pd(A))continue;let L=Vs(N,A);try{if(kc(L).isDirectory()&&Ac(Vs(L,"index.html")))m.push(A)}catch{}}return m.sort((A,L)=>A.localeCompare(L))}function fd(N){let{root:t,siteName:m,expectIndex:A}=N,L=[];if(!Ac(t))return L.push({level:"error",message:`Build output not found at ${t} for site '${m}'`}),L;if(!kc(t).isDirectory())return L.push({level:"error",message:`Site '${m}' has root ${t}, which is a file, not a directory`}),L;if(!md(t))return L.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.`}),L;if(!A)return L;if(Ac(Vs(t,"index.html")))return L;let j=Sm(t);if(j.length===1){let G=`${t.replace(/\/+$/,"")}/${j[0]}`;return L.push({level:"error",message:`Site '${m}' has no index.html at the top of ${t}, but ${G} has one. The generator wrote one level down, so this release would serve 404s at the domain root. Set root: '${G}'.`}),L}if(j.length>1)return L.push({level:"warning",message:`Site '${m}' has no index.html at the top of ${t}. These subdirectories have one: ${j.join(", ")}. The domain root will 404.`}),L;return L.push({level:"warning",message:`Site '${m}' has no index.html anywhere under ${t}. The domain root will 404.`}),L}function $m(N){if(!N)return[];if(Array.isArray(N))return N.map(String);let t=N,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 L=A.Item;if(!L)return[];return Array.isArray(L)?L.map(String):[String(L)]}if("CNAME"in A){let L=A.CNAME;if(!L)return[];return Array.isArray(L)?L.map(String):[String(L)]}return Object.values(A).flatMap((L)=>{if(Array.isArray(L))return L.map(String);return typeof L==="string"?[L]:[]})}return[String(m)]}async function qs(N){if(N.length===0)return;let t=new w,m=await t.listDistributions(),A=[...new Set(N.map((L)=>L.toLowerCase()))];for(let L of A){let j=m.find((Z)=>$m(Z.Aliases).some((ne)=>ne.toLowerCase()===L));if(!j?.Id){a(`No CloudFront distribution found for ${L} — skipping dynamic HTTP method sync`);continue}if(await t.ensureDynamicHttpMethods(j.Id))n(`CloudFront ${j.Id} (${L}): enabled POST/PUT/PATCH/DELETE on dynamic paths`);else e(`CloudFront ${j.Id} (${L}): already allows dynamic HTTP methods`)}}import{execSync as Cm}from"node:child_process";async function Am(N,t,m,A={},L=process.cwd()){if(!N)return;if(A.step?.(`Running ${m} hook`),typeof N==="string"){Cm(N,{stdio:"inherit",cwd:L});return}await N(t)}async function ps(N,t,m={},A){try{return await Am(N.hooks?.[t],N,t,m,A),!0}catch(L){return m.error?.(`${t} hook failed: ${L?.message||L}`),!1}}var Ei=(N)=>N===void 0?void 0:N?"on":"off";function Rc(N){let t={ssl:N.ssl,always_use_https:Ei(N.alwaysUseHttps),automatic_https_rewrites:Ei(N.automaticHttpsRewrites),min_tls_version:N.minTlsVersion,tls_1_3:Ei(N.tls13),brotli:Ei(N.brotli),http3:Ei(N.http3),"0rtt":Ei(N.zeroRtt),early_hints:Ei(N.earlyHints),browser_cache_ttl:N.browserCacheTtl,always_online:Ei(N.alwaysOnline),websockets:Ei(N.websockets),email_obfuscation:Ei(N.emailObfuscation)};if(N.hsts)t.security_header={strict_transport_security:{enabled:N.hsts.enabled,max_age:N.hsts.maxAge??31536000,include_subdomains:N.hsts.includeSubdomains??!1,preload:N.hsts.preload??!1,nosniff:N.hsts.noSniff??!0}};for(let m of Object.keys(t))if(t[m]===void 0)delete t[m];return{...t,...N.raw??{}}}var ms={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 km}from"node:tls";async function Ic(N){let{address:t,serverName:m,port:A=443,timeoutMs:L=5000}=N;return new Promise((j)=>{let G=!1,Z=(ne)=>{if(G)return;G=!0;try{ee.destroy()}catch{}j(ne)},ee=km({host:t,port:A,servername:m,rejectUnauthorized:!1,timeout:L});ee.once("secureConnect",()=>{if(ee.authorized){Z({reachable:!0,trusted:!0});return}Z({reachable:!0,trusted:!1,reason:ee.authorizationError?String(ee.authorizationError):"origin certificate is not trusted for this hostname"})}),ee.once("timeout",()=>{Z({reachable:!1,trusted:!1,reason:`no TLS handshake within ${L}ms`})}),ee.once("error",(ne)=>{Z({reachable:!1,trusted:!1,reason:ne.message})})})}function Rm(N,t){let m=t.replace(/\.$/,"").toLowerCase();return N.some((A)=>{let L=A.replace(/\.$/,"").toLowerCase();if(L===m)return!0;if(!L.startsWith("*."))return!1;let j=L.slice(2);if(!m.endsWith(`.${j}`))return!1;return!m.slice(0,m.length-j.length-1).includes(".")})}async function Pc(N){let{provider:t,zone:m,hosts:A,ipv4:L,ipv6:j,proxied:G=!0,settings:Z=ms,cache:ee,originGuard:ne,purge:oe=!0,ttl:be=300,skipOriginProbe:we=!1,skipEdgeCertificateCheck:Ce=!1}=N,De={records:[],settingsChanged:[],cacheRules:0,originGuard:!1,purged:!1,deferredProxy:[],warnings:[]};if(A.length===0)return De;let Se=[],xe=G&&!Ce?await t.listEdgeCertificateHosts(m):null;for(let Ae of A){let Ee=G;if(Ee&&xe&&!Rm(xe,Ae)){Ee=!1;let Qe=xe.length===0?"the zone has no active edge certificate yet":`no active edge certificate covers it (${xe.join(", ")})`;De.deferredProxy.push({host:Ae,reason:Qe}),De.warnings.push(`${Ae}: publishing DNS-only — ${Qe}. 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(Ee&&!we){let Qe=await Ic({address:L,serverName:Ae});if(!Qe.trusted){Ee=!1;let Ke=Qe.reason||"origin is not serving a trusted certificate";De.deferredProxy.push({host:Ae,reason:Ke}),De.warnings.push(`${Ae}: publishing DNS-only for now — the origin does not yet serve a certificate this proxy can verify (${Ke}). 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(Ee)Se.push(Ae);let Te=[{type:"A",content:L}];if(j&&Pi(Ae))Te.push({type:"AAAA",content:j});for(let{type:Qe,content:Ke}of Te){let Ye=await t.upsertRecord(m,{name:Ae,type:Qe,content:Ke,ttl:be,proxied:Ee});if(Ye.success)De.records.push({host:Ae,type:Qe,content:Ke,proxied:Ee});else De.warnings.push(`${Ae} ${Qe} → ${Ke} failed: ${Ye.message||"unknown provider error"}`)}}if(Se.length===0){if(G)De.warnings.push("No host is proxied yet, so zone settings, cache rules and purge were skipped — they only take effect through the proxy.");return De}try{let Ae=Rc(Z),Ee=await t.applyZoneSettings(m,Ae);De.settingsChanged=Ee.changed;for(let Te of Ee.failed)De.warnings.push(`zone setting '${Te.id}' could not be applied: ${Te.error}`)}catch(Ae){De.warnings.push(`zone settings could not be read: ${Ae instanceof Error?Ae.message:String(Ae)}`)}let Me=fs(Se,ee);if(Me.length>0){let Ae=await t.putManagedPhaseRules(m,"http_request_cache_settings",Me);if(Ae.success)De.cacheRules=Me.length;else De.warnings.push(`cache rules could not be written: ${Ae.message}`)}if(ne){let Ae=hs(Se,ne.header,ne.value);if(Ae.length>0){let Ee=await t.putManagedPhaseRules(m,"http_request_late_transform",Ae);if(Ee.success)De.originGuard=!0;else De.warnings.push(`origin guard header could not be written: ${Ee.message}. `+"The gateway will reject requests that lack it — disable the guard or grant the token Transform Rules: Edit.")}}if(oe){if(De.purged=await t.purgeCache(m,{hosts:Se}),!De.purged)De.warnings.push("edge cache purge failed — new content may take until TTL expiry to appear.")}return De}var Im=(N)=>process.env[N];function gd(N,t){let m=N.replace(/\.$/,"").toLowerCase();if(t){let A=t.replace(/\.$/,"").toLowerCase();if(m===A||m.endsWith(`.${A}`))return A}return m.split(".").slice(-2).join(".")}function xc(N,t={}){let m=t.env??Im,A=N.infrastructure?.compute?.proxy?.cdn,L=[];if(!A||A.provider!=="cloudflare")return{plan:null,errors:L};if(A.originDomain)L.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 j=m("CLOUDFLARE_API_TOKEN");if(!j)return L.push("CLOUDFLARE_API_TOKEN is not set — the Cloudflare CDN cannot be reconciled."),{plan:null,errors:L};let G=N.sites??{},Z=A.frontedHosts?.length?[...new Set(A.frontedHosts.map((we)=>we.toLowerCase()))]:ki(G,{autoWww:N.infrastructure?.compute?.proxy?.autoWww!==!1});if(Z.length===0)return L.push("No hostnames to front — set compute.proxy.cdn.frontedHosts, or give a site a domain."),{plan:null,errors:L};let ee=N.infrastructure?.dns?.domain,ne=new Set(Z.map((we)=>gd(we,ee)));if(ne.size>1)return L.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:L};let oe=A.cloudflare??{},be=A.secret?{header:A.secretHeader??"X-Origin-Verify",value:A.secret}:void 0;return{plan:{zone:[...ne][0],hosts:Z,apiToken:j,zoneId:oe.zoneId??m("CLOUDFLARE_ZONE_ID"),accountId:oe.accountId??m("CLOUDFLARE_ACCOUNT_ID"),proxied:oe.proxied!==!1,settings:oe.settings??ms,cache:oe.cache,originGuard:be,purge:oe.purgeOnDeploy!==!1,skipOriginProbe:oe.skipOriginProbe===!0},errors:L}}async function Tm(N){let t=Us();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 st(N).getCallerIdentity();if(A.Arn)e(`AWS identity: ${A.Arn}`)}catch(m){a(`Could not verify AWS identity: ${m.message}`)}}function Nm(){try{return Nc("git rev-parse --short HEAD",{stdio:["ignore","pipe","ignore"]}).toString().trim()}catch{return Date.now().toString(36)}}async function Dc(N,t,m,A){let{ensureManagementDashboard:L}=await import("./chunk-52fhq35h.js");L(N,{logger:{info:e,warn:a}});let j=N.sites||{},G=Object.entries(j);if(G.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&&!j[A])return r(`Site '${A}' was not found. Configured sites: ${G.map(([Me])=>Me).join(", ")}`),!1;let Z=G.filter(([Me,Ae])=>{if(!Ae)return!1;if(A&&Me!==A)return!1;if(P(Ae)==="bucket")return a(`Site '${Me}' targets a bucket — skipping compute deploy (handled by the static-site path).`),!1;return Ds(Ae)});if(A)e(`Deploying only '${A}'. Other sites keep their current release; proxy routes are still regenerated in full.`);let ee=N.project.slug,oe=(N.infrastructure?.compute||{}).runtime||"bun",be=_({config:N}),we=Nm(),Ce=new Map,De=new Map,Se={step:(Me)=>p(Me),error:(Me)=>r(Me)};if(!await ps(N,"beforeDeploy",Se))return!1;if(!await ps(N,"beforeBuild",Se))return!1;for(let[Me,Ae]of Z){let Ee=P(Ae);if(i(`Preparing site: ${Me}`),e(`Domain: ${Ae.domain||"(none)"}`),e(`Source: ${Ae.root}`),e(`Kind: ${Ee==="server-static"?"static (shipped to /var/www)":"app (systemd service)"}`),Ee==="server-app")e(`Start: ${Ae.start}`),e(`Port: ${Ae.port??"(unset)"}`);if(Ae.build){p(`Running build: ${Ae.build}`);try{Nc(Ae.build,{stdio:"inherit",cwd:process.cwd()})}catch(et){return r(`Build failed for site '${Me}': ${et.message}`),!1}}let Te=fd({root:Ae.root,siteName:Me,expectIndex:Ee==="server-static"});for(let et of Te)if(et.level==="error")r(et.message);else a(et.message);if(Te.some((et)=>et.level==="error"))return!1;let Qe=Ae.build?void 0:JSON.stringify([Em(Ae.root),[...Ae.exclude||[]].sort()]),Ke=Qe?De.get(Qe):void 0;if(Ke){p(`Reusing package for ${Ae.root} → ${Ke}`),Ce.set(Me,Ke);continue}let Ye=vd(yd(),`${ee}-${Me}-${we}.tar.gz`);p(`Packaging ${Ae.root} → ${Ye}`);try{let et=(Ae.exclude||[]).map((Pt)=>`--exclude=${Pt}`);Pm("tar",["czf",Ye,"-C",Ae.root,...et,"."],{stdio:"inherit",env:{...process.env,COPYFILE_DISABLE:"1"}})}catch(et){return r(`Failed to package '${Me}': ${et.message}`),!1}if(Ce.set(Me,Ye),Qe)De.set(Qe,Ye)}if(!await ps(N,"afterBuild",Se))return!1;let xe=await Mi({config:A?{...N,sites:Object.fromEntries(Z)}:N,rpxConfig:A?N:void 0,environment:t,driver:be,sha:we,runtime:oe,tarballForSite:(Me)=>{let Ae=Ce.get(Me);if(!Ae)throw Error(`Missing tarball for site '${Me}'`);return Ae},logger:{info:(Me)=>e(Me),warn:(Me)=>a(Me),error:(Me)=>r(Me),step:(Me)=>p(Me),success:(Me)=>n(Me)}});if(xe&&!await ps(N,"afterDeploy",Se))return!1;return xe}async function Hs(N){let t=new xr;if(p("Running pre-deployment security scan..."),N.exclude?.length)e(`Excluding from the scan: ${N.exclude.join(", ")}`);let m=await t.scan({directory:N.sourceDir,failOnSeverity:N.failOnSeverity||"critical",skipPatterns:N.skipPatterns,exclude:N.exclude});return{passed:m.passed,result:m}}function Om(){let N=vd(yd(),`ts-cloud-empty-scan-${process.pid}`);return hd(N,{recursive:!0}),N}function Js(N){let{summary:t,findings:m,scannedFiles:A,duration:L}=N;if(e(`
376
+ Scanned ${A} files in ${L}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
+ Findings:`);let j=m.filter((oe)=>oe.pattern.severity==="critical"),G=m.filter((oe)=>oe.pattern.severity==="high"),Z=m.filter((oe)=>oe.pattern.severity==="medium"),ee=m.filter((oe)=>oe.pattern.severity==="low"),ne=(oe,be,we)=>{if(oe.length>0){console.log(`
378
+ ${Lt(`[${be}]`,we)}`);for(let Ce of oe.slice(0,10))e(` ${Ce.pattern.name}`),e(` File: ${Ce.file}:${Ce.line}`),e(` Match: ${Ce.match}`);if(oe.length>10)e(` ... and ${oe.length-10} more ${be.toLowerCase()} findings`)}};ne(j,"CRITICAL","red"),ne(G,"HIGH","yellow"),ne(Z,"MEDIUM","blue"),ne(ee,"LOW","gray")}}function bd(N,t){let m=Object.keys(N.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 Ec(N,t,m){let A=bd(N,t),L=y(process.cwd(),N);try{let j=new Rt(L.store);On(L);let G=ae(L,A);if(m)Ta(j,G,m);else Ea(j,G);let Z=er(j,{scope:G,desiredConfigHash:L.project.desiredConfigHash});return e(`Security policy: ${Z.decision.policyId} v${Z.decision.policyVersion} — ${Z.decision.outcome.toUpperCase()}`),e(`Security decision: ${Z.decision.explanation}`),{allowed:Z.decision.outcome!=="block",explanation:Z.decision.explanation}}finally{L.store.close()}}async function Lm(N,t){let m=bd(N),A=y(process.cwd(),N);try{let L=new Rt(A.store);On(A);let j=ae(A,m),G=t.imageSha256?`${t.imageRef}@sha256:${t.imageSha256.replace(/^sha256:/,"")}`:t.imageRef;p("Scanning the built image and producing release security artifacts...");let Z=await wa(L,{scope:j,releaseId:G,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: ${Z.scan.run.status} (${Z.scan.findings.length} findings)`),e(`Release SBOM: ${Z.sbomSource==="image"?"full image inventory":Z.sbomSource==="manifest"?"package-manifest fallback":"unavailable"}`);let ee=er(L,{scope:j,operationId:t.invocationId,desiredConfigHash:A.project.desiredConfigHash});return e(`Release policy: ${ee.decision.policyId} v${ee.decision.policyVersion} — ${ee.decision.outcome.toUpperCase()}`),e(`Release decision: ${ee.decision.explanation}`),{allowed:ee.decision.outcome!=="block",explanation:ee.decision.explanation}}finally{A.store.close()}}function jm(N){try{let t=y(process.cwd(),N),m=new qt(t.store).current();if(!m.attackMode&&!m.mitigationPause&&m.ipRules.allow.length===0&&m.ipRules.block.length===0)return N;let A=Uo(N,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 N}}function Oc(N){N.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",L=t?.skipPatterns?.split(",").map((Z)=>Z.trim())||[];if(!yr(m)){r(`Source directory not found: ${m}`);return}e(`Source: ${m}`),e(`Fail on: ${A} or higher severity`);let{passed:j,result:G}=await Hs({sourceDir:m,failOnSeverity:A,skipPatterns:L});if(Js(G),j)n(`
379
379
  ✓ Security scan passed - no blocking issues found`);else r(`
380
380
  ✗ Security scan failed - blocking issues detected`),e(`
381
- Recommendations:`),e(" 1. Remove any hardcoded credentials from your code"),e(" 2. Use environment variables or AWS Secrets Manager"),e(" 3. Add sensitive files to .gitignore"),e(" 4. Use --skip-patterns to ignore false positives"),process.exit(1)}catch(p){r(`Security scan failed: ${p.message}`),process.exit(1)}}),N.command("deploy","Deploy infrastructure").option("--stack <name>","Stack name").option("--env <environment>","Environment to deploy to").option("--site <name>","Deploy specific site only (routes are still regenerated in full)").option("--skip-security-scan","Skip pre-deployment security scan").option("--skip-dns-verification","Skip DNS provider verification and record creation (use when DNS is already configured)").option("--security-fail-on <severity>","Security scan fail threshold (critical, high, medium, low)",{default:"critical"}).option("--dry-run","Show the deployment plan without making changes").option("--yes","Skip confirmation prompts (non-interactive / CI)").action(async(t)=>{i("Deploying Infrastructure");let p=t?.yes===!0||process.env.CI==="true",A=t?.env||"staging",L=null;L=await Rp(A);try{let j=await Me(),G=kp(j),{errors:Z,warnings:ee}=hs(G);for(let vt of ee)a(vt);if(Z.length>0){r(`
382
- ✗ Deployment configuration is invalid:`);for(let vt of Z)r(` • ${vt}`);process.exitCode=1;return}if(t?.dryRun){if(t.site&&!G.sites?.[t.site]){r(`Site '${t.site}' was not found. Configured sites: ${Object.keys(G.sites||{}).join(", ")||"none"}`),process.exitCode=1;return}let vt=V(G),Lt=t.stack||E(G,A),nr=U(G),Qn=G.project.region||"us-east-1",di=t.site?[t.site]:Object.keys(G.sites||{});i("Deployment Plan"),e(`Cloud provider: ${nr}`),e(`Mode: ${vt}`),e(`Stack: ${Lt}`),e(`Region: ${Qn}`),e(`Environment: ${A}`),e(`Sites: ${di.length>0?di.join(", "):"none"}`),n("Dry run complete. No infrastructure, DNS, certificates, or application files were changed.");return}if(U(G)==="aws"){let vt=G.project?.region||"us-east-1";await Sp(vt)}let re;if(!t?.skipSecurityScan){let vt=za(G,t?.site);if(!vt)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:Lt}=await Ms({sourceDir:vt?process.cwd():Cp(),failOnSeverity:t?.securityFailOn||"critical",exclude:G.infrastructure?.security?.scan?.exclude});qs(Lt),re=Lt}else a(`Security scan skipped (--skip-security-scan)
383
- `);let oe=Sc(G,A,re);if(!oe.allowed){r(`
381
+ Recommendations:`),e(" 1. Remove any hardcoded credentials from your code"),e(" 2. Use environment variables or AWS Secrets Manager"),e(" 3. Add sensitive files to .gitignore"),e(" 4. Use --skip-patterns to ignore false positives"),process.exit(1)}catch(m){r(`Security scan failed: ${m.message}`),process.exit(1)}}),N.command("deploy","Deploy infrastructure").option("--stack <name>","Stack name").option("--env <environment>","Environment to deploy to").option("--site <name>","Deploy specific site only (routes are still regenerated in full)").option("--skip-security-scan","Skip pre-deployment security scan").option("--skip-dns-verification","Skip DNS provider verification and record creation (use when DNS is already configured)").option("--security-fail-on <severity>","Security scan fail threshold (critical, high, medium, low)",{default:"critical"}).option("--dry-run","Show the deployment plan without making changes").option("--yes","Skip confirmation prompts (non-interactive / CI)").action(async(t)=>{i("Deploying Infrastructure");let m=t?.yes===!0||process.env.CI==="true",A=t?.env||"staging",L=null;L=await Fm(A);try{let j=await Ne(),G=jm(j),{errors:Z,warnings:ee}=Ns(G);for(let wt of ee)a(wt);if(Z.length>0){r(`
382
+ ✗ Deployment configuration is invalid:`);for(let wt of Z)r(` • ${wt}`);process.exitCode=1;return}if(t?.dryRun){if(t.site&&!G.sites?.[t.site]){r(`Site '${t.site}' was not found. Configured sites: ${Object.keys(G.sites||{}).join(", ")||"none"}`),process.exitCode=1;return}let wt=V(G),Mt=t.stack||E(G,A),Yn=U(G),Zn=G.project.region||"us-east-1",Qr=t.site?[t.site]:Object.keys(G.sites||{});i("Deployment Plan"),e(`Cloud provider: ${Yn}`),e(`Mode: ${wt}`),e(`Stack: ${Mt}`),e(`Region: ${Zn}`),e(`Environment: ${A}`),e(`Sites: ${Qr.length>0?Qr.join(", "):"none"}`),n("Dry run complete. No infrastructure, DNS, certificates, or application files were changed.");return}if(U(G)==="aws"){let wt=G.project?.region||"us-east-1";await Tm(wt)}let ne;if(!t?.skipSecurityScan){let wt=nc(G,t?.site);if(!wt)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:Mt}=await Hs({sourceDir:wt?process.cwd():Om(),failOnSeverity:t?.securityFailOn||"critical",exclude:G.infrastructure?.security?.scan?.exclude});Js(Mt),ne=Mt}else a(`Security scan skipped (--skip-security-scan)
383
+ `);let oe=Ec(G,A,ne);if(!oe.allowed){r(`
384
384
  ✗ Deployment blocked by security policy: ${oe.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
- `),V(G)==="serverless"){m("Serverless project detected: deploying the Lambda application");let{deployServerlessApp:vt}=await import("./chunk-cpcae7e2.js");await vt(G,A,{}),n("Serverless deployment complete");return}let be=t?.stack||E(G,A),we=G.project.region||"us-east-1",Ce=G.infrastructure?.deployStack!==!1,Ee=G.sites&&Object.keys(G.sites).length>0,Se=G.infrastructure?.dns?.provider;if(Ee&&Se){if(await Dp(G,t?.site,Se,we,t?.skipDnsVerification,A,p),!Ce){if(G.infrastructure?.compute){let vt=Object.values(G.sites).map((Lt)=>Lt.domain).filter((Lt)=>!!Lt);if(vt.length>0)m("Syncing CloudFront dynamic HTTP methods for app domains..."),await Bs(vt)}return}}if(!Ce){e("Infrastructure stack deployment disabled (infrastructure.deployStack: false)");return}let De=U(G);if(e(`Cloud provider: ${De}`),e(`Stack: ${be}`),e(`Region: ${we}`),e(`Environment: ${A}`),(De==="hetzner"||G.infrastructure?.compute?.mode==="server")&&io(G)){m(`Provisioning ${De} compute infrastructure...`);let vt=x({config:G,provider:De});if(!vt.provisionComputeInfrastructure){r(`${De} driver does not support compute provisioning`),process.exitCode=1;return}let Lt=await vt.provisionComputeInfrastructure({config:G,environment:A});if(n(`${De} compute infrastructure ready`),Lt.appPublicIp)e(`App server: ${Lt.appPublicIp}`);if(Lt.appInstanceId)e(`Server ID: ${Lt.appInstanceId}`);if(!await wc(G,A,we,t?.site))r(`App deploy to ${De} compute reported a failure`),process.exitCode=1;else{if(n(`App deployed to ${De} compute`),t?.skipDnsVerification)e("DNS verification and record reconciliation skipped (--skip-dns-verification)");else await Ip(G,Lt.appPublicIp,Se,Lt.appPublicIpv6),await Pp(G,Se);let Qn=await Oi({config:G,environment:A,driver:vt,logger:{info:e,warn:a,error:r,step:m,success:n}});if(!Qn)r("App deployed, but rpx TLS certificate reconciliation failed");if(!Qn)process.exitCode=1;if(!t?.skipDnsVerification)await xp(G,Lt.appPublicIp,Lt.appPublicIpv6)}return}m("Generating CloudFormation template...");let Ae=new Ao({config:G,environment:A});Ae.generate();let Te=Ae.toJSON(),Ne=JSON.parse(Te);if(Object.keys(Ne.Resources||{}).length===0){e(`No infrastructure resources to deploy for environment '${A}' — skipping stack ${be}.`);return}m("Validating template...");let Ge=Yo(Ne),Ye=Zo(Te),et=Qo(Ne),Pt=[...Ge.errors,...Ye.errors,...et.errors];if(Pt.length>0){r("Template validation failed:");for(let vt of Pt)r(` - ${vt.path}: ${vt.message}`);return}let wt=[...Ge.warnings,...Ye.warnings,...et.warnings];if(wt.length>0)for(let vt of wt)a(` - ${vt.path}: ${vt.message}`);n("Template validated successfully");let Ot=Object.keys(Ne.Resources).length;e(`
386
- Resources to deploy: ${Ot}`);let Ut={};for(let vt of Object.values(Ne.Resources)){let Lt=vt.Type;Ut[Lt]=(Ut[Lt]||0)+1}for(let[vt,Lt]of Object.entries(Ut).sort((nr,Qn)=>Qn[1]-nr[1]).slice(0,5))e(` - ${vt}: ${Lt}`);if(!(p||await d(`
387
- Deploy now?`,!0))){e("Deployment cancelled");return}let jt=new v(we);m("Checking stack status...");let ft=!1;try{let vt=await jt.describeStacks({stackName:be});ft=vt.Stacks&&vt.Stacks.length>0}catch(vt){ft=!1}if(ft){e("Stack exists, updating...");let vt=new o("Updating CloudFormation stack...");vt.start();try{await jt.updateStack({stackName:be,templateBody:Te,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:G.project.name},{Key:"Environment",Value:A},{Key:"ManagedBy",Value:"ts-cloud"}]}),vt.succeed("Update initiated"),m("Waiting for stack update to complete..."),await jt.waitForStack(be,"stack-update-complete"),n("Stack updated successfully!")}catch(Lt){if(Lt.message.includes("No updates are to be performed"))vt.succeed("No changes detected"),e("Stack is already up to date — continuing to app deploy");else throw Lt}}else{e("Creating new stack...");let vt=new o("Creating CloudFormation stack...");vt.start(),await jt.createStack({stackName:be,templateBody:Te,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:G.project.name},{Key:"Environment",Value:A},{Key:"ManagedBy",Value:"ts-cloud"}]}),vt.succeed("Stack creation initiated"),m("Waiting for stack creation to complete..."),await jt.waitForStack(be,"stack-create-complete"),n("Stack created successfully!")}let Vt=await jt.getStackOutputs(be);if(he(`Deployment Complete!
385
+ `),V(G)==="serverless"){p("Serverless project detected: deploying the Lambda application");let{deployServerlessApp:wt}=await import("./chunk-dk9f6h0m.js");await wt(G,A,{}),n("Serverless deployment complete");return}let be=t?.stack||E(G,A),we=G.project.region||"us-east-1",Ce=G.infrastructure?.deployStack!==!1,De=G.sites&&Object.keys(G.sites).length>0,Se=G.infrastructure?.dns?.provider;if(De&&Se){if(await zm(G,t?.site,Se,we,t?.skipDnsVerification,A,m),!Ce){if(G.infrastructure?.compute){let wt=Object.values(G.sites).map((Mt)=>Mt.domain).filter((Mt)=>!!Mt);if(wt.length>0)p("Syncing CloudFront dynamic HTTP methods for app domains..."),await qs(wt)}return}}if(!Ce){e("Infrastructure stack deployment disabled (infrastructure.deployStack: false)");return}let xe=U(G);if(e(`Cloud provider: ${xe}`),e(`Stack: ${be}`),e(`Region: ${we}`),e(`Environment: ${A}`),(xe==="hetzner"||G.infrastructure?.compute?.mode==="server")&&uo(G)){p(`Provisioning ${xe} compute infrastructure...`);let wt=_({config:G,provider:xe});if(!wt.provisionComputeInfrastructure){r(`${xe} driver does not support compute provisioning`),process.exitCode=1;return}let Mt=await wt.provisionComputeInfrastructure({config:G,environment:A});if(n(`${xe} compute infrastructure ready`),Mt.appPublicIp)e(`App server: ${Mt.appPublicIp}`);if(Mt.appInstanceId)e(`Server ID: ${Mt.appInstanceId}`);if(!await Dc(G,A,we,t?.site))r(`App deploy to ${xe} compute reported a failure`),process.exitCode=1;else{if(n(`App deployed to ${xe} compute`),t?.skipDnsVerification)e("DNS verification and record reconciliation skipped (--skip-dns-verification)");else await Bm(G,Mt.appPublicIp,Se,Mt.appPublicIpv6),await Mm(G,Se);let Zn=await Ui({config:G,environment:A,driver:wt,logger:{info:e,warn:a,error:r,step:p,success:n}});if(!Zn)r("App deployed, but rpx TLS certificate reconciliation failed");if(!Zn)process.exitCode=1;if(!t?.skipDnsVerification)await _m(G,Mt.appPublicIp,Mt.appPublicIpv6)}return}p("Generating CloudFormation template...");let Ae=new Io({config:G,environment:A});Ae.generate();let Ee=Ae.toJSON(),Te=JSON.parse(Ee);if(Object.keys(Te.Resources||{}).length===0){e(`No infrastructure resources to deploy for environment '${A}' — skipping stack ${be}.`);return}p("Validating template...");let Ke=es(Te),Ye=ts(Ee),et=ns(Te),Pt=[...Ke.errors,...Ye.errors,...et.errors];if(Pt.length>0){r("Template validation failed:");for(let wt of Pt)r(` - ${wt.path}: ${wt.message}`);return}let Bt=[...Ke.warnings,...Ye.warnings,...et.warnings];if(Bt.length>0)for(let wt of Bt)a(` - ${wt.path}: ${wt.message}`);n("Template validated successfully");let vt=Object.keys(Te.Resources).length;e(`
386
+ Resources to deploy: ${vt}`);let Ot={};for(let wt of Object.values(Te.Resources)){let Mt=wt.Type;Ot[Mt]=(Ot[Mt]||0)+1}for(let[wt,Mt]of Object.entries(Ot).sort((Yn,Zn)=>Zn[1]-Yn[1]).slice(0,5))e(` - ${wt}: ${Mt}`);if(!(m||await d(`
387
+ Deploy now?`,!0))){e("Deployment cancelled");return}let zt=new v(we),ht=na(Te);if(ht.length>0){let{ensureFailoverReplicaBuckets:wt}=await import("./chunk-padk76ac.js");p("Ensuring failover replica buckets..."),await wt(ht,(Mt)=>e(` ${Mt}`))}p("Checking stack status...");let Vt=!1;try{let wt=await zt.describeStacks({stackName:be});Vt=wt.Stacks&&wt.Stacks.length>0}catch(wt){Vt=!1}if(Vt){e("Stack exists, updating...");let wt=new o("Updating CloudFormation stack...");wt.start();try{await zt.updateStack({stackName:be,templateBody:Ee,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:G.project.name},{Key:"Environment",Value:A},{Key:"ManagedBy",Value:"ts-cloud"}]}),wt.succeed("Update initiated"),p("Waiting for stack update to complete..."),await zt.waitForStack(be,"stack-update-complete"),n("Stack updated successfully!")}catch(Mt){if(Mt.message.includes("No updates are to be performed"))wt.succeed("No changes detected"),e("Stack is already up to date — continuing to app deploy");else throw Mt}}else{e("Creating new stack...");let wt=new o("Creating CloudFormation stack...");wt.start(),await zt.createStack({stackName:be,templateBody:Ee,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:G.project.name},{Key:"Environment",Value:A},{Key:"ManagedBy",Value:"ts-cloud"}]}),wt.succeed("Stack creation initiated"),p("Waiting for stack creation to complete..."),await zt.waitForStack(be,"stack-create-complete"),n("Stack created successfully!")}let nn=await zt.getStackOutputs(be);if(he(`Deployment Complete!
388
388
 
389
389
  Stack: ${be}
390
390
  Region: ${we}
391
391
  Environment: ${A}
392
- Resources: ${Ot}
392
+ Resources: ${vt}
393
393
 
394
394
  View in console:
395
- https://console.aws.amazon.com/cloudformation/home?region=${we}#/stacks/stackinfo?stackId=${encodeURIComponent(be)}`,"green"),Object.keys(Vt).length>0){e(`
396
- Stack Outputs:`);for(let[vt,Lt]of Object.entries(Vt))e(` - ${vt}: ${Lt}`)}if(G.infrastructure?.storage)for(let[vt,Lt]of Object.entries(G.infrastructure.storage)){if(!Lt.website||!Lt.root)continue;let nr=Vt[`${vt}BucketName`]||Vt.FrontendBucketName,Qn=Vt[`${vt}CloudFrontDistributionId`];if(!nr){a(`Could not find bucket name for storage '${vt}' — skipping file upload`);continue}if(!fr(Lt.root)){a(`Source directory not found: ${Lt.root} — skipping file upload for '${vt}'`);continue}m(`Uploading files from ${Lt.root} to s3://${nr}...`);let{uploadStaticFiles:di,invalidateCache:fo}=await import("./chunk-bbt0vyzj.js"),gr=await di({sourceDir:Lt.root,bucket:nr,region:we,onProgress:(tn,rn,Jr)=>{if(tn%10===0||tn===rn)e(` ${tn}/${rn}: ${Jr}`)}});if(gr.errors.length>0)a(`Upload completed with errors: ${gr.errors.join(", ")}`);else{let tn=gr.skipped>0?`Uploaded ${gr.uploaded} files (${gr.skipped} unchanged)`:`Uploaded ${gr.uploaded} files`;n(tn)}if(Qn&&gr.uploaded>0){m("Invalidating CloudFront cache...");let{invalidationId:tn}=await fo(Qn);n(`Cache invalidation created: ${tn}`)}}if(G.infrastructure?.compute)await wc(G,A,we,t?.site);if(G.infrastructure?.compute&&G.sites){let vt=Object.values(G.sites).map((Lt)=>Lt.domain).filter((Lt)=>!!Lt);if(vt.length>0)m("Syncing CloudFront dynamic HTTP methods for app domains..."),await Bs(vt)}}catch(j){if(r(`Deployment failed: ${j.message}`),process.exitCode=1,j.stack)e(`
397
- Stack trace:`),console.error(j.stack)}finally{if(L)await L()}}),N.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 p=await Me(),A=t?.env||"production",L=p.project.region||"us-east-1",j=wr(p);if(j){r(j),process.exitCode=1;return}if(!io(p)){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: ${L}`),e(`Environment: ${A}`),!await wc(p,A,L,t?.site)){process.exitCode=1;return}if(p.sites){let Z=Object.values(p.sites).map((ee)=>ee.domain).filter((ee)=>!!ee);if(Z.length>0)m("Syncing CloudFront dynamic HTTP methods for app domains..."),await Bs(Z)}n(`
398
- Server deployment complete!`)}catch(p){r(`Deployment failed: ${p.message}`),process.exitCode=1}}),N.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 p=await Me(),A=t?.env||"production",L=wr(p);if(L){r(L),process.exitCode=1;return}let{deployServerlessApp:j,redeployServerlessApp:G}=await import("./chunk-cpcae7e2.js");if(t?.redeploy){await G(p,A);return}await j(p,A,{skipBuild:t?.skipBuild,skipDeployHooks:t?.skipHooks})}catch(p){r(`Deployment failed: ${p.message}`),process.exitCode=1}}),N.command("serverless:rollback","Roll back a serverless app to the previous build").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{try{let p=await Me(),A=t?.env||"production",{rollbackServerlessApp:L}=await import("./chunk-cpcae7e2.js");await L(p,A)}catch(p){r(`Rollback failed: ${p.message}`),process.exitCode=1}}),N.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)").action(async(t,p)=>{i("Rolling Back Release");try{let A=await Me(),L=p?.env||"production",j=t||Object.keys(A.sites||{})[0];if(!j){r("No site configured to roll back."),process.exitCode=1;return}let{rollbackComputeSite:G}=await import("./chunk-9bxyy4th.js"),Z=await G({driver:x({config:A}),slug:A.project.slug,environment:L,logger:g},{siteName:j,to:p?.to});if(!Z.success){r(`Rollback failed: ${Z.error||"unknown error"}`),process.exitCode=1;return}for(let ee of Z.perInstance||[])e(` ${ee.instanceId}: ${ee.output?.trim()||ee.status}`)}catch(A){r(`Rollback failed: ${A.message}`),process.exitCode=1}}),N.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,p)=>{i("Deployment History");try{let A=await Me(),L=p?.env||"production",j=t||Object.keys(A.sites||{})[0];if(!j){r("No site configured."),process.exitCode=1;return}let{getComputeDeployHistory:G}=await import("./chunk-9bxyy4th.js"),Z=await G({driver:x({config:A}),slug:A.project.slug,environment:L,logger:g},{siteName:j,limit:Number.parseInt(p?.limit||"20",10)||20});if(!Z.success){r(`Could not read history: ${Z.error||"unknown error"}`),process.exitCode=1;return}for(let ee of Z.perInstance||[])e(`
399
- ${ee.instanceId}:`),e(ee.output?.trimEnd()||"(no output)")}catch(A){r(`History lookup failed: ${A.message}`),process.exitCode=1}}),N.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,p,A)=>{i(`Running Recipe: ${t}`);try{if(!fr(p)){r(`Recipe script not found: ${p}`),process.exitCode=1;return}let L=await Me(),j=A?.env||"production",G=$c(p,"utf8").split(`
400
- `),{runComputeRecipe:Z}=await import("./chunk-9bxyy4th.js"),ee=await Z({driver:x({config:L}),slug:L.project.slug,environment:j,logger:g},{name:t,script:G,user:A?.user});for(let re of ee.perInstance||[])e(`
401
- ${re.instanceId}:`),e(re.output?.trimEnd()||"(no output)");if(!ee.success)process.exitCode=1}catch(L){r(`Recipe failed: ${L.message}`),process.exitCode=1}}),N.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,p)=>{i("Database Restore");try{let A=await Me(),L=p?.env||"production",{restoreDatabaseBackup:j}=await import("./chunk-9bxyy4th.js"),G=await j({driver:x({config:A}),slug:A.project.slug,environment:L,logger:g},{database:C(A),from:t});for(let Z of G.perInstance||[])e(` ${Z.instanceId}: ${Z.output?.trim()||Z.status}`);if(!G.success)r(`Restore failed: ${G.error||"unknown error"}`),process.exitCode=1}catch(A){r(`Restore failed: ${A.message}`),process.exitCode=1}}),N.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 p=await Me(),{buildQuickDeployCi:A,inferQuickDeployProvider:L}=await import("./chunk-1baj8p0r.js"),j=t?.provider?.toLowerCase();if(j&&!["github","gitlab","bitbucket"].includes(j))throw Error(`Unsupported CI provider '${t?.provider}'. Use github, gitlab, or bitbucket.`);let G=j;if(!G)try{let ee=Cc("git remote get-url origin",{stdio:["ignore","pipe","ignore"]}).toString().trim();G=L(ee)}catch{}let Z=A(p,t?.env||"production",{provider:G,site:t?.site,skipDnsVerification:t?.skipDnsVerification,setup:fr("pantry.lock")?"pantry":"bun",sshPrivateKeySecret:t?.sshKeySecret});if(!Z){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(fr(Z.path)&&!t?.force){a(`${Z.path} already exists — re-run with --force to overwrite.`);return}sd(bp(Z.path),{recursive:!0}),vp(Z.path,Z.content),n(`Wrote ${Z.path} (${Z.provider}, deploys on push to '${Z.branch}').`),e("Next: commit it and add your provider credentials as CI secrets/variables.")}catch(p){r(`Quick deploy setup failed: ${p.message}`),process.exitCode=1}}),N.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 p=await Me(),A=t?.env||"production",{setMaintenance:L}=await import("./chunk-cpcae7e2.js");await L(p,A,!0,t?.secret)}catch(p){r(`Failed to enable maintenance mode: ${p.message}`),process.exitCode=1}}),N.command("up","Bring the serverless app out of maintenance mode").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{try{let p=await Me(),A=t?.env||"production",{setMaintenance:L}=await import("./chunk-cpcae7e2.js");await L(p,A,!1)}catch(p){r(`Failed to disable maintenance mode: ${p.message}`),process.exitCode=1}}),N.command("command <cmd>","Run a command on the serverless CLI function (e.g. migrations)").option("--env <environment>","Environment (production, staging, development)").action(async(t,p)=>{try{let A=await Me(),L=p?.env||"production",{runAndRecordCommand:j}=await import("./chunk-cpcae7e2.js"),{id:G,output:Z}=await j(A,L,t);if(e(Z),G>0)e(`
402
- (recorded as #${G} — re-run with \`cloud command:again ${G}\`)`)}catch(A){r(`Command failed: ${A.message}`),process.exitCode=1}}),N.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 p=await Me(),A=t?.env||"production",{listCommandHistory:L}=await import("./chunk-cpcae7e2.js"),j=await L(p,A);if(!j.length){e("No command history yet.");return}let G=Number(t?.limit??20);i(`Command history — ${p.project.slug} (${A})`),c(["#","When","Status","Command"],j.slice(-G).reverse().map((Z)=>[String(Z.id),Z.timestamp,Z.status,Z.command]))}catch(p){r(`Failed to read history: ${p.message}`),process.exitCode=1}}),N.command("command:again [id]","Re-run a recorded command (defaults to the most recent)").option("--env <environment>","Environment (production, staging, development)").action(async(t,p)=>{try{let A=await Me(),L=p?.env||"production",{getCommandRecord:j,runAndRecordCommand:G}=await import("./chunk-cpcae7e2.js"),Z=await j(A,L,t?Number(t):void 0);if(!Z){r(t?`No command #${t} in history.`:"No command history to re-run."),process.exitCode=1;return}e(`Re-running #${Z.id}: ${Z.command}`);let ee=await G(A,L,Z.command);if(e(ee.output),ee.id>0)e(`
403
- (recorded as #${ee.id})`)}catch(A){r(`Command failed: ${A.message}`),process.exitCode=1}}),N.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,p)=>{try{let A=await Me(),L=p?.env||"production",{runDbQuery:j}=await import("./chunk-cpcae7e2.js"),G=await j(A,L,t);e(G)}catch(A){r(`Query failed: ${A.message}`),process.exitCode=1}}),N.command("serverless:db-scale <min> <max>","Rescale the serverless Aurora cluster (min/max ACUs)").option("--env <environment>","Environment (production, staging, development)").action(async(t,p,A)=>{i("Scaling serverless database");try{let L=await Me(),j=A?.env||"production",G=Number(t),Z=Number(p);if(!(G>0)||!(Z>=G)){r("Provide valid capacities: min > 0 and max >= min (e.g. `serverless:db-scale 0.5 8`)."),process.exitCode=1;return}let{scaleServerlessDatabase:ee}=await import("./chunk-cpcae7e2.js");await ee(L,j,G,Z)}catch(L){r(`Scale failed: ${L.message}`),process.exitCode=1}}),N.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 p=await Me(),A=t?.env||"production",L;if(t?.to){if(L=new Date(t.to),Number.isNaN(L.getTime())){r(`Invalid --to timestamp: ${t.to} (use ISO-8601, e.g. 2026-06-18T10:00:00Z).`),process.exitCode=1;return}}let{restoreServerlessDatabase:j}=await import("./chunk-cpcae7e2.js");await j(p,A,{toTime:L,latest:t?.latest,target:t?.target})}catch(p){r(`Restore failed: ${p.message}`),process.exitCode=1}}),N.command("serverless:info","Show an operational summary of a deployed serverless app").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{try{let p=await Me(),A=t?.env||"production",{serverlessInfo:L}=await import("./chunk-cpcae7e2.js"),j=await L(p,A);if(i(`${j.slug} (${j.environment})`),e(`Stack: ${j.stackStatus}`),e(`Region: ${j.region}`),j.endpoint)e(`URL: ${j.endpoint}`);if(j.assetUrl)e(`Assets: ${j.assetUrl}`);if(e(`Scheduler:${j.scheduler==="off"?" off":` ${j.scheduler}`}`),e(`Queues: ${j.queues.length?j.queues.join(", "):"none"}`),e(`Warming: ${j.provisionedConcurrency>0?`provisioned concurrency ×${j.provisionedConcurrency}`:"none / ping-warm"}`),j.lastRelease)e(`Release: ${j.lastRelease.sha.slice(0,12)} @ ${j.lastRelease.timestamp}`);c(["Function","Version","Provisioned"],j.functions.map((G)=>[G.name,G.version,G.provisioned?`${G.provisioned.status} ${G.provisioned.allocated}/${G.provisioned.requested}`:"—"]))}catch(p){r(`Failed to read info: ${p.message}`),process.exitCode=1}}),N.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:p}=await import("node:fs"),A=t?.ui||"ui";if(!p(`${A}/package.json`)){r(`No UI project at ${A}/ (expected ${A}/package.json). Pass --ui <dir>.`),process.exitCode=1;return}let L=await Me(),j=t?.env||"production";m("Gathering live data from AWS");let G=null;if(L.environments?.[j]?.app){let{resolveDashboardData:oe}=await import("./chunk-pjgbnk3q.js"),be=await oe(L,j).catch((we)=>(a(`Serverless data unavailable (${we.message}) — that view uses sample data.`),null));if(be)G={...G??{},...be}}if(L.infrastructure?.compute){let{resolveServerDashboardData:oe}=await import("./chunk-kjbkwzxd.js"),be=await oe(L,j).catch(()=>null);if(be){if(be._serverReachable===!1)a("Server box not reachable over SSM — server view uses config + sample data.");G={...G??{},...be}}}m("Building dashboard");let{encodeDashboardPayload:Z}=await import("./chunk-64cdtvar.js"),{execSync:ee}=await import("node:child_process"),{rmSync:re}=await import("node:fs");for(let oe of[".stx/ssg-cache",".stx/cache"])re(`${A}/${oe}`,{recursive:!0,force:!0});ee("bun run build",{cwd:A,stdio:"inherit",env:{...process.env,...G?{TSCLOUD_DASHBOARD_DATA:Z(G)}:{}}}),n(`Dashboard built → ${A}/dist${G?" (live data)":" (sample data)"}`)}catch(p){r(`Dashboard build failed: ${p.message}`),process.exitCode=1}}),N.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 p=await Me().catch(()=>null),A=t?.region||p?.project.region||"us-east-1",L=t?.arch||"x86_64",j=t?.php||"8.3",G=`${t?.name||"tscloud-php"}-${j.replace(".","")}-${L}`,Z=t?.bucket||`${p?.project.slug||"tscloud"}-layers`,{buildPhpRuntimeLayerZip:ee}=await import("./chunk-v93xyx1p.js"),{S3Client:re}=await import("./chunk-4v6f496n.js"),{LambdaClient:oe}=await import("./chunk-f2wn46nw.js"),be=ee({architecture:L,phpVersion:j,onStep:(De)=>m(De)});e(`Layer: ${be.fileCount} files, ${(be.zip.length/1024/1024).toFixed(1)} MB`);let we=new re(A);if(!await we.bucketExists(Z))m(`Creating layer bucket ${Z}`),await we.createBucket(Z);let Ce=`layers/${G}.zip`;m("Uploading layer zip"),await we.putObject({bucket:Z,key:Ce,body:be.zip,contentType:"application/zip"}),m("Publishing layer version");let Se=await new oe(A).publishLayerVersion({LayerName:G,Description:`ts-cloud PHP ${j} runtime (${L})`,Content:{S3Bucket:Z,S3Key:Ce},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[L]});he(["PHP runtime layer published","",`ARN: ${Se.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'php', layers: ['${Se.LayerVersionArn}'] }`,"or set TSCLOUD_PHP_LAYER_ARN before deploying."].join(`
404
- `),"green")}catch(p){r(`Layer build failed: ${p.message}`),process.exitCode=1}}),N.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 p=await Me().catch(()=>null),A=t?.region||p?.project.region||"us-east-1",L=t?.arch||"x86_64",j=t?.node||"24",{buildNodeRuntimeLayerZip:G}=await import("./chunk-v93xyx1p.js"),{S3Client:Z}=await import("./chunk-4v6f496n.js"),{LambdaClient:ee}=await import("./chunk-f2wn46nw.js"),re=G({architecture:L,version:j,onStep:(De)=>m(De)}),oe=`${t?.name||"tscloud-node"}-${re.version.split(".")[0]}-${L}`,be=t?.bucket||`${p?.project.slug||"tscloud"}-layers`;e(`Layer: Node ${re.version}, ${(re.zip.length/1024/1024).toFixed(1)} MB`);let we=new Z(A);if(!await we.bucketExists(be))m(`Creating layer bucket ${be}`),await we.createBucket(be);let Ce=`layers/${oe}.zip`;m("Uploading layer zip"),await we.putObject({bucket:be,key:Ce,body:re.zip,contentType:"application/zip"}),m("Publishing layer version");let Se=await new ee(A).publishLayerVersion({LayerName:oe,Description:`ts-cloud Node ${re.version} runtime (${L})`,Content:{S3Bucket:be,S3Key:Ce},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[L]});he(["Node runtime layer published","",`ARN: ${Se.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'node', runtimeVersion: '${re.version.split(".")[0]}', layers: ['${Se.LayerVersionArn}'] }`,"or set TSCLOUD_NODE_LAYER_ARN before deploying."].join(`
405
- `),"green")}catch(p){r(`Layer build failed: ${p.message}`),process.exitCode=1}}),N.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 p=await Me().catch(()=>null),A=t?.region||p?.project.region||"us-east-1",L=t?.arch||"x86_64",j=t?.bun||"latest",{buildBunRuntimeLayerZip:G}=await import("./chunk-v93xyx1p.js"),{S3Client:Z}=await import("./chunk-4v6f496n.js"),{LambdaClient:ee}=await import("./chunk-f2wn46nw.js"),re=G({architecture:L,version:j,onStep:(De)=>m(De)}),oe=`${t?.name||"tscloud-bun"}-${re.version.replace(/\./g,"")}-${L}`,be=t?.bucket||`${p?.project.slug||"tscloud"}-layers`;e(`Layer: Bun ${re.version}, ${(re.zip.length/1024/1024).toFixed(1)} MB`);let we=new Z(A);if(!await we.bucketExists(be))m(`Creating layer bucket ${be}`),await we.createBucket(be);let Ce=`layers/${oe}.zip`;m("Uploading layer zip"),await we.putObject({bucket:be,key:Ce,body:re.zip,contentType:"application/zip"}),m("Publishing layer version");let Se=await new ee(A).publishLayerVersion({LayerName:oe,Description:`ts-cloud Bun ${re.version} runtime (${L})`,Content:{S3Bucket:be,S3Key:Ce},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[L]});he(["Bun runtime layer published","",`ARN: ${Se.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'bun', layers: ['${Se.LayerVersionArn}'] }`,"or set TSCLOUD_BUN_LAYER_ARN before deploying."].join(`
406
- `),"green")}catch(p){r(`Layer build failed: ${p.message}`),process.exitCode=1}}),N.command("deploy:status","Check deployment status").option("--stack <name>","Stack name").option("--env <environment>","Environment").action(async(t)=>{i("Deployment Status");try{let p=await Me(),A=t?.env||"production",L=t?.stack||E(p,A),j=p.project.region||"us-east-1";e(`Stack: ${L}`),e(`Region: ${j}`);let G=new o("Checking deployment status...");G.start();let ee=await new v(j).describeStacks({stackName:L});if(ee.Stacks.length===0){G.fail("Stack not found"),hr("No deployment found for this environment");return}let re=ee.Stacks[0];if(G.succeed("Status retrieved"),e(`
407
- Status: ${re.StackStatus}`),e(`Created: ${re.CreationTime}`),re.LastUpdatedTime)e(`Last Updated: ${re.LastUpdatedTime}`);if(re.Outputs&&re.Outputs.length>0){e(`
408
- Outputs:`);for(let oe of re.Outputs)e(` ${oe.OutputKey}: ${oe.OutputValue}`)}}catch(p){r(`Failed to get status: ${p.message}`)}}),N.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 p=await Me(),A=t?.env||"production",L=t?.stack||E(p,A),j=p.project.region||"us-east-1";if(e(`Stack: ${L}`),e(`Region: ${j}`),!await d(`
409
- Are you sure you want to rollback?`,!1)){e("Rollback cancelled");return}let Z=new o("Rolling back stack...");Z.start();let ee=new v(j);await ee.deleteStack(L),Z.succeed("Stack deletion initiated"),m("Waiting for stack deletion..."),await ee.waitForStack(L,"stack-delete-complete"),n("Stack rolled back successfully!")}catch(p){r(`Rollback failed: ${p.message}`)}}),N.command("deploy:static","Deploy static site (S3 + CloudFront invalidation)").option("--source <path>","Source directory",{default:"dist"}).option("--bucket <name>","S3 bucket name").option("--distribution <id>","CloudFront distribution ID").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"}).option("--no-invalidate","Skip CloudFront invalidation").option("--wait","Wait for invalidation to complete").option("--skip-security-scan","Skip pre-deployment security scan").option("--security-fail-on <severity>","Security scan fail threshold (critical, high, medium, low)",{default:"critical"}).action(async(t)=>{i("Deploying Static Site");try{let p=await Me(),A=p.project.region||"us-east-1",L=t?.source||"dist",j;if(!t?.skipSecurityScan){m("Scanning source directory for leaked secrets...");let{result:Te}=await Ms({sourceDir:L,failOnSeverity:t?.securityFailOn||"critical"});qs(Te),j=Te}else a(`Security scan skipped (--skip-security-scan)
410
- `);let G=Sc(p,void 0,j);if(!G.allowed){r(`
395
+ https://console.aws.amazon.com/cloudformation/home?region=${we}#/stacks/stackinfo?stackId=${encodeURIComponent(be)}`,"green"),Object.keys(nn).length>0){e(`
396
+ Stack Outputs:`);for(let[wt,Mt]of Object.entries(nn))e(` - ${wt}: ${Mt}`)}if(ht.length>0){let{grantFailoverReplicaAccess:wt}=await import("./chunk-padk76ac.js");p("Granting the distribution read access to its failover replicas..."),await wt(ht,nn,(Mt)=>e(` ${Mt}`))}if(G.infrastructure?.storage)for(let[wt,Mt]of Object.entries(G.infrastructure.storage)){if(!Mt.website||!Mt.root)continue;let Yn=nn[`${wt}BucketName`]||nn.FrontendBucketName,Zn=nn[`${wt}CloudFrontDistributionId`];if(!Yn){a(`Could not find bucket name for storage '${wt}' — skipping file upload`);continue}if(!yr(Mt.root)){a(`Source directory not found: ${Mt.root} — skipping file upload for '${wt}'`);continue}p(`Uploading files from ${Mt.root} to s3://${Yn}...`);let{uploadStaticFiles:Qr,invalidateCache:Ki}=await import("./chunk-d9yzg97w.js"),ur=await Qr({sourceDir:Mt.root,bucket:Yn,region:we,onProgress:(rn,Ar,Ir)=>{if(rn%10===0||rn===Ar)e(` ${rn}/${Ar}: ${Ir}`)}});if(ur.errors.length>0)a(`Upload completed with errors: ${ur.errors.join(", ")}`);else{let rn=ur.skipped>0?`Uploaded ${ur.uploaded} files (${ur.skipped} unchanged)`:`Uploaded ${ur.uploaded} files`;n(rn)}if(Zn&&ur.uploaded>0){p("Invalidating CloudFront cache...");let{invalidationId:rn}=await Ki(Zn);n(`Cache invalidation created: ${rn}`)}}if(ht.length>0){let{seedFailoverReplicas:wt}=await import("./chunk-padk76ac.js");p("Syncing failover replicas..."),await wt(ht,(Mt)=>e(` ${Mt}`))}if(G.infrastructure?.compute)await Dc(G,A,we,t?.site);if(G.infrastructure?.compute&&G.sites){let wt=Object.values(G.sites).map((Mt)=>Mt.domain).filter((Mt)=>!!Mt);if(wt.length>0)p("Syncing CloudFront dynamic HTTP methods for app domains..."),await qs(wt)}}catch(j){if(r(`Deployment failed: ${j.message}`),process.exitCode=1,j.stack)e(`
397
+ Stack trace:`),console.error(j.stack)}finally{if(L)await L()}}),N.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 Ne(),A=t?.env||"production",L=m.project.region||"us-east-1",j=Cr(m);if(j){r(j),process.exitCode=1;return}if(!uo(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: ${L}`),e(`Environment: ${A}`),!await Dc(m,A,L,t?.site)){process.exitCode=1;return}if(m.sites){let Z=Object.values(m.sites).map((ee)=>ee.domain).filter((ee)=>!!ee);if(Z.length>0)p("Syncing CloudFront dynamic HTTP methods for app domains..."),await qs(Z)}n(`
398
+ Server deployment complete!`)}catch(m){r(`Deployment failed: ${m.message}`),process.exitCode=1}}),N.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 Ne(),A=t?.env||"production",L=Cr(m);if(L){r(L),process.exitCode=1;return}let{deployServerlessApp:j,redeployServerlessApp:G}=await import("./chunk-dk9f6h0m.js");if(t?.redeploy){await G(m,A);return}await j(m,A,{skipBuild:t?.skipBuild,skipDeployHooks:t?.skipHooks})}catch(m){r(`Deployment failed: ${m.message}`),process.exitCode=1}}),N.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 Ne(),A=t?.env||"production",{rollbackServerlessApp:L}=await import("./chunk-dk9f6h0m.js");await L(m,A)}catch(m){r(`Rollback failed: ${m.message}`),process.exitCode=1}}),N.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)").action(async(t,m)=>{i("Rolling Back Release");try{let A=await Ne(),L=m?.env||"production",j=t||Object.keys(A.sites||{})[0];if(!j){r("No site configured to roll back."),process.exitCode=1;return}let{rollbackComputeSite:G}=await import("./chunk-hagakkgm.js"),Z=await G({driver:_({config:A}),slug:A.project.slug,environment:L,logger:f},{siteName:j,to:m?.to});if(!Z.success){r(`Rollback failed: ${Z.error||"unknown error"}`),process.exitCode=1;return}for(let ee of Z.perInstance||[])e(` ${ee.instanceId}: ${ee.output?.trim()||ee.status}`)}catch(A){r(`Rollback failed: ${A.message}`),process.exitCode=1}}),N.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 Ne(),L=m?.env||"production",j=t||Object.keys(A.sites||{})[0];if(!j){r("No site configured."),process.exitCode=1;return}let{getComputeDeployHistory:G}=await import("./chunk-hagakkgm.js"),Z=await G({driver:_({config:A}),slug:A.project.slug,environment:L,logger:f},{siteName:j,limit:Number.parseInt(m?.limit||"20",10)||20});if(!Z.success){r(`Could not read history: ${Z.error||"unknown error"}`),process.exitCode=1;return}for(let ee of Z.perInstance||[])e(`
399
+ ${ee.instanceId}:`),e(ee.output?.trimEnd()||"(no output)")}catch(A){r(`History lookup failed: ${A.message}`),process.exitCode=1}}),N.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(!yr(m)){r(`Recipe script not found: ${m}`),process.exitCode=1;return}let L=await Ne(),j=A?.env||"production",G=Tc(m,"utf8").split(`
400
+ `),{runComputeRecipe:Z}=await import("./chunk-hagakkgm.js"),ee=await Z({driver:_({config:L}),slug:L.project.slug,environment:j,logger:f},{name:t,script:G,user:A?.user});for(let ne of ee.perInstance||[])e(`
401
+ ${ne.instanceId}:`),e(ne.output?.trimEnd()||"(no output)");if(!ee.success)process.exitCode=1}catch(L){r(`Recipe failed: ${L.message}`),process.exitCode=1}}),N.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 Ne(),L=m?.env||"production",{restoreDatabaseBackup:j}=await import("./chunk-hagakkgm.js"),G=await j({driver:_({config:A}),slug:A.project.slug,environment:L,logger:f},{database:C(A),from:t});for(let Z of G.perInstance||[])e(` ${Z.instanceId}: ${Z.output?.trim()||Z.status}`);if(!G.success)r(`Restore failed: ${G.error||"unknown error"}`),process.exitCode=1}catch(A){r(`Restore failed: ${A.message}`),process.exitCode=1}}),N.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 Ne(),{buildQuickDeployCi:A,inferQuickDeployProvider:L}=await import("./chunk-dmdg23a5.js"),j=t?.provider?.toLowerCase();if(j&&!["github","gitlab","bitbucket"].includes(j))throw Error(`Unsupported CI provider '${t?.provider}'. Use github, gitlab, or bitbucket.`);let G=j;if(!G)try{let ee=Nc("git remote get-url origin",{stdio:["ignore","pipe","ignore"]}).toString().trim();G=L(ee)}catch{}let Z=A(m,t?.env||"production",{provider:G,site:t?.site,skipDnsVerification:t?.skipDnsVerification,setup:yr("pantry.lock")?"pantry":"bun",sshPrivateKeySecret:t?.sshKeySecret});if(!Z){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(yr(Z.path)&&!t?.force){a(`${Z.path} already exists — re-run with --force to overwrite.`);return}hd(Dm(Z.path),{recursive:!0}),xm(Z.path,Z.content),n(`Wrote ${Z.path} (${Z.provider}, deploys on push to '${Z.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}}),N.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 Ne(),A=t?.env||"production",{setMaintenance:L}=await import("./chunk-dk9f6h0m.js");await L(m,A,!0,t?.secret)}catch(m){r(`Failed to enable maintenance mode: ${m.message}`),process.exitCode=1}}),N.command("up","Bring the serverless app out of maintenance mode").option("--env <environment>","Environment (production, staging, development)").action(async(t)=>{try{let m=await Ne(),A=t?.env||"production",{setMaintenance:L}=await import("./chunk-dk9f6h0m.js");await L(m,A,!1)}catch(m){r(`Failed to disable maintenance mode: ${m.message}`),process.exitCode=1}}),N.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 Ne(),L=m?.env||"production",{runAndRecordCommand:j}=await import("./chunk-dk9f6h0m.js"),{id:G,output:Z}=await j(A,L,t);if(e(Z),G>0)e(`
402
+ (recorded as #${G} — re-run with \`cloud command:again ${G}\`)`)}catch(A){r(`Command failed: ${A.message}`),process.exitCode=1}}),N.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 Ne(),A=t?.env||"production",{listCommandHistory:L}=await import("./chunk-dk9f6h0m.js"),j=await L(m,A);if(!j.length){e("No command history yet.");return}let G=Number(t?.limit??20);i(`Command history — ${m.project.slug} (${A})`),c(["#","When","Status","Command"],j.slice(-G).reverse().map((Z)=>[String(Z.id),Z.timestamp,Z.status,Z.command]))}catch(m){r(`Failed to read history: ${m.message}`),process.exitCode=1}}),N.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 Ne(),L=m?.env||"production",{getCommandRecord:j,runAndRecordCommand:G}=await import("./chunk-dk9f6h0m.js"),Z=await j(A,L,t?Number(t):void 0);if(!Z){r(t?`No command #${t} in history.`:"No command history to re-run."),process.exitCode=1;return}e(`Re-running #${Z.id}: ${Z.command}`);let ee=await G(A,L,Z.command);if(e(ee.output),ee.id>0)e(`
403
+ (recorded as #${ee.id})`)}catch(A){r(`Command failed: ${A.message}`),process.exitCode=1}}),N.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 Ne(),L=m?.env||"production",{runDbQuery:j}=await import("./chunk-dk9f6h0m.js"),G=await j(A,L,t);e(G)}catch(A){r(`Query failed: ${A.message}`),process.exitCode=1}}),N.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 L=await Ne(),j=A?.env||"production",G=Number(t),Z=Number(m);if(!(G>0)||!(Z>=G)){r("Provide valid capacities: min > 0 and max >= min (e.g. `serverless:db-scale 0.5 8`)."),process.exitCode=1;return}let{scaleServerlessDatabase:ee}=await import("./chunk-dk9f6h0m.js");await ee(L,j,G,Z)}catch(L){r(`Scale failed: ${L.message}`),process.exitCode=1}}),N.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 Ne(),A=t?.env||"production",L;if(t?.to){if(L=new Date(t.to),Number.isNaN(L.getTime())){r(`Invalid --to timestamp: ${t.to} (use ISO-8601, e.g. 2026-06-18T10:00:00Z).`),process.exitCode=1;return}}let{restoreServerlessDatabase:j}=await import("./chunk-dk9f6h0m.js");await j(m,A,{toTime:L,latest:t?.latest,target:t?.target})}catch(m){r(`Restore failed: ${m.message}`),process.exitCode=1}}),N.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 Ne(),A=t?.env||"production",{serverlessInfo:L}=await import("./chunk-dk9f6h0m.js"),j=await L(m,A);if(i(`${j.slug} (${j.environment})`),e(`Stack: ${j.stackStatus}`),e(`Region: ${j.region}`),j.endpoint)e(`URL: ${j.endpoint}`);if(j.assetUrl)e(`Assets: ${j.assetUrl}`);if(e(`Scheduler:${j.scheduler==="off"?" off":` ${j.scheduler}`}`),e(`Queues: ${j.queues.length?j.queues.join(", "):"none"}`),e(`Warming: ${j.provisionedConcurrency>0?`provisioned concurrency ×${j.provisionedConcurrency}`:"none / ping-warm"}`),j.lastRelease)e(`Release: ${j.lastRelease.sha.slice(0,12)} @ ${j.lastRelease.timestamp}`);c(["Function","Version","Provisioned"],j.functions.map((G)=>[G.name,G.version,G.provisioned?`${G.provisioned.status} ${G.provisioned.allocated}/${G.provisioned.requested}`:"—"]))}catch(m){r(`Failed to read info: ${m.message}`),process.exitCode=1}}),N.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 L=await Ne(),j=t?.env||"production";p("Gathering live data from AWS");let G=null;if(L.environments?.[j]?.app){let{resolveDashboardData:oe}=await import("./chunk-ckk70zc9.js"),be=await oe(L,j).catch((we)=>(a(`Serverless data unavailable (${we.message}) — that view uses sample data.`),null));if(be)G={...G??{},...be}}if(L.infrastructure?.compute){let{resolveServerDashboardData:oe}=await import("./chunk-nagvc7e4.js"),be=await oe(L,j).catch(()=>null);if(be){if(be._serverReachable===!1)a("Server box not reachable over SSM — server view uses config + sample data.");G={...G??{},...be}}}p("Building dashboard");let{encodeDashboardPayload:Z}=await import("./chunk-swfmx4f8.js"),{execSync:ee}=await import("node:child_process"),{rmSync:ne}=await import("node:fs");for(let oe of[".stx/ssg-cache",".stx/cache"])ne(`${A}/${oe}`,{recursive:!0,force:!0});ee("bun run build",{cwd:A,stdio:"inherit",env:{...process.env,...G?{TSCLOUD_DASHBOARD_DATA:Z(G)}:{}}}),n(`Dashboard built → ${A}/dist${G?" (live data)":" (sample data)"}`)}catch(m){r(`Dashboard build failed: ${m.message}`),process.exitCode=1}}),N.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 Ne().catch(()=>null),A=t?.region||m?.project.region||"us-east-1",L=t?.arch||"x86_64",j=t?.php||"8.3",G=`${t?.name||"tscloud-php"}-${j.replace(".","")}-${L}`,Z=t?.bucket||`${m?.project.slug||"tscloud"}-layers`,{buildPhpRuntimeLayerZip:ee}=await import("./chunk-6se7ygxa.js"),{S3Client:ne}=await import("./chunk-a7kmjq4y.js"),{LambdaClient:oe}=await import("./chunk-wagzxm7z.js"),be=ee({architecture:L,phpVersion:j,onStep:(xe)=>p(xe)});e(`Layer: ${be.fileCount} files, ${(be.zip.length/1024/1024).toFixed(1)} MB`);let we=new ne(A);if(!await we.bucketExists(Z))p(`Creating layer bucket ${Z}`),await we.createBucket(Z);let Ce=`layers/${G}.zip`;p("Uploading layer zip"),await we.putObject({bucket:Z,key:Ce,body:be.zip,contentType:"application/zip"}),p("Publishing layer version");let Se=await new oe(A).publishLayerVersion({LayerName:G,Description:`ts-cloud PHP ${j} runtime (${L})`,Content:{S3Bucket:Z,S3Key:Ce},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[L]});he(["PHP runtime layer published","",`ARN: ${Se.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'php', layers: ['${Se.LayerVersionArn}'] }`,"or set TSCLOUD_PHP_LAYER_ARN before deploying."].join(`
404
+ `),"green")}catch(m){r(`Layer build failed: ${m.message}`),process.exitCode=1}}),N.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 Ne().catch(()=>null),A=t?.region||m?.project.region||"us-east-1",L=t?.arch||"x86_64",j=t?.node||"24",{buildNodeRuntimeLayerZip:G}=await import("./chunk-6se7ygxa.js"),{S3Client:Z}=await import("./chunk-a7kmjq4y.js"),{LambdaClient:ee}=await import("./chunk-wagzxm7z.js"),ne=G({architecture:L,version:j,onStep:(xe)=>p(xe)}),oe=`${t?.name||"tscloud-node"}-${ne.version.split(".")[0]}-${L}`,be=t?.bucket||`${m?.project.slug||"tscloud"}-layers`;e(`Layer: Node ${ne.version}, ${(ne.zip.length/1024/1024).toFixed(1)} MB`);let we=new Z(A);if(!await we.bucketExists(be))p(`Creating layer bucket ${be}`),await we.createBucket(be);let Ce=`layers/${oe}.zip`;p("Uploading layer zip"),await we.putObject({bucket:be,key:Ce,body:ne.zip,contentType:"application/zip"}),p("Publishing layer version");let Se=await new ee(A).publishLayerVersion({LayerName:oe,Description:`ts-cloud Node ${ne.version} runtime (${L})`,Content:{S3Bucket:be,S3Key:Ce},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[L]});he(["Node runtime layer published","",`ARN: ${Se.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'node', runtimeVersion: '${ne.version.split(".")[0]}', layers: ['${Se.LayerVersionArn}'] }`,"or set TSCLOUD_NODE_LAYER_ARN before deploying."].join(`
405
+ `),"green")}catch(m){r(`Layer build failed: ${m.message}`),process.exitCode=1}}),N.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 Ne().catch(()=>null),A=t?.region||m?.project.region||"us-east-1",L=t?.arch||"x86_64",j=t?.bun||"latest",{buildBunRuntimeLayerZip:G}=await import("./chunk-6se7ygxa.js"),{S3Client:Z}=await import("./chunk-a7kmjq4y.js"),{LambdaClient:ee}=await import("./chunk-wagzxm7z.js"),ne=G({architecture:L,version:j,onStep:(xe)=>p(xe)}),oe=`${t?.name||"tscloud-bun"}-${ne.version.replace(/\./g,"")}-${L}`,be=t?.bucket||`${m?.project.slug||"tscloud"}-layers`;e(`Layer: Bun ${ne.version}, ${(ne.zip.length/1024/1024).toFixed(1)} MB`);let we=new Z(A);if(!await we.bucketExists(be))p(`Creating layer bucket ${be}`),await we.createBucket(be);let Ce=`layers/${oe}.zip`;p("Uploading layer zip"),await we.putObject({bucket:be,key:Ce,body:ne.zip,contentType:"application/zip"}),p("Publishing layer version");let Se=await new ee(A).publishLayerVersion({LayerName:oe,Description:`ts-cloud Bun ${ne.version} runtime (${L})`,Content:{S3Bucket:be,S3Key:Ce},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[L]});he(["Bun runtime layer published","",`ARN: ${Se.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'bun', layers: ['${Se.LayerVersionArn}'] }`,"or set TSCLOUD_BUN_LAYER_ARN before deploying."].join(`
406
+ `),"green")}catch(m){r(`Layer build failed: ${m.message}`),process.exitCode=1}}),N.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 Ne(),A=t?.env||"production",L=t?.stack||E(m,A),j=m.project.region||"us-east-1";e(`Stack: ${L}`),e(`Region: ${j}`);let G=new o("Checking deployment status...");G.start();let ee=await new v(j).describeStacks({stackName:L});if(ee.Stacks.length===0){G.fail("Stack not found"),fr("No deployment found for this environment");return}let ne=ee.Stacks[0];if(G.succeed("Status retrieved"),e(`
407
+ Status: ${ne.StackStatus}`),e(`Created: ${ne.CreationTime}`),ne.LastUpdatedTime)e(`Last Updated: ${ne.LastUpdatedTime}`);if(ne.Outputs&&ne.Outputs.length>0){e(`
408
+ Outputs:`);for(let oe of ne.Outputs)e(` ${oe.OutputKey}: ${oe.OutputValue}`)}}catch(m){r(`Failed to get status: ${m.message}`)}}),N.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 Ne(),A=t?.env||"production",L=t?.stack||E(m,A),j=m.project.region||"us-east-1";if(e(`Stack: ${L}`),e(`Region: ${j}`),!await d(`
409
+ Are you sure you want to rollback?`,!1)){e("Rollback cancelled");return}let Z=new o("Rolling back stack...");Z.start();let ee=new v(j);await ee.deleteStack(L),Z.succeed("Stack deletion initiated"),p("Waiting for stack deletion..."),await ee.waitForStack(L,"stack-delete-complete"),n("Stack rolled back successfully!")}catch(m){r(`Rollback failed: ${m.message}`)}}),N.command("deploy:static","Deploy static site (S3 + CloudFront invalidation)").option("--source <path>","Source directory",{default:"dist"}).option("--bucket <name>","S3 bucket name").option("--distribution <id>","CloudFront distribution ID").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"}).option("--no-invalidate","Skip CloudFront invalidation").option("--wait","Wait for invalidation to complete").option("--skip-security-scan","Skip pre-deployment security scan").option("--security-fail-on <severity>","Security scan fail threshold (critical, high, medium, low)",{default:"critical"}).action(async(t)=>{i("Deploying Static Site");try{let m=await Ne(),A=m.project.region||"us-east-1",L=t?.source||"dist",j;if(!t?.skipSecurityScan){p("Scanning source directory for leaked secrets...");let{result:Ee}=await Hs({sourceDir:L,failOnSeverity:t?.securityFailOn||"critical"});Js(Ee),j=Ee}else a(`Security scan skipped (--skip-security-scan)
410
+ `);let G=Ec(m,void 0,j);if(!G.allowed){r(`
411
411
  ✗ Static deployment blocked by security policy: ${G.explanation}`),e("Remediate the findings or create an authorized, time-limited waiver in the Security posture center."),process.exitCode=1;return}n(`✓ Security policy allows this deployment
412
- `);let Z=t?.bucket,ee=t?.distribution,re=t?.prefix,oe=t?.delete||!1,be=t?.cacheControl||"public, max-age=31536000",we=t?.invalidate!==!1,Ce=t?.wait||!1;if(!Z){r("--bucket is required");return}if(!fr(L)){r(`Source directory not found: ${L}`);return}if(e(`Source: ${L}`),e(`Bucket: s3://${Z}${re?`/${re}`:""}`),e(`Cache-Control: ${be}`),ee)e(`CloudFront Distribution: ${ee}`);if(oe)a("Delete mode enabled - files not in source will be removed");if(!await d(`
413
- Deploy static site now?`,!0)){e("Deployment cancelled");return}let Se=new f(A),De=new o("Uploading files to S3...");De.start(),await Se.sync({source:L,bucket:Z,prefix:re,delete:oe,cacheControl:be,acl:"public-read"}),De.succeed("Files uploaded successfully!");let Ae=(await Se.getBucketSize(Z,re)/1024/1024).toFixed(2);if(e(`Total size: ${Ae} MB`),we&&ee){let Te=new w,Ne=new o("Invalidating CloudFront cache...");Ne.start();let Xe=await Te.invalidateAll(ee);if(Ne.succeed("Invalidation created"),e(`Invalidation ID: ${Xe.Id}`),Ce){let Ge=new o("Waiting for invalidation to complete...");Ge.start(),await Te.waitForInvalidation(ee,Xe.Id),Ge.succeed("Invalidation completed!")}}he(`Static Site Deployed!
412
+ `);let Z=t?.bucket,ee=t?.distribution,ne=t?.prefix,oe=t?.delete||!1,be=t?.cacheControl||"public, max-age=31536000",we=t?.invalidate!==!1,Ce=t?.wait||!1;if(!Z){r("--bucket is required");return}if(!yr(L)){r(`Source directory not found: ${L}`);return}if(e(`Source: ${L}`),e(`Bucket: s3://${Z}${ne?`/${ne}`:""}`),e(`Cache-Control: ${be}`),ee)e(`CloudFront Distribution: ${ee}`);if(oe)a("Delete mode enabled - files not in source will be removed");if(!await d(`
413
+ Deploy static site now?`,!0)){e("Deployment cancelled");return}let Se=new g(A),xe=new o("Uploading files to S3...");xe.start(),await Se.sync({source:L,bucket:Z,prefix:ne,delete:oe,cacheControl:be,acl:"public-read"}),xe.succeed("Files uploaded successfully!");let Ae=(await Se.getBucketSize(Z,ne)/1024/1024).toFixed(2);if(e(`Total size: ${Ae} MB`),we&&ee){let Ee=new w,Te=new o("Invalidating CloudFront cache...");Te.start();let Qe=await Ee.invalidateAll(ee);if(Te.succeed("Invalidation created"),e(`Invalidation ID: ${Qe.Id}`),Ce){let Ke=new o("Waiting for invalidation to complete...");Ke.start(),await Ee.waitForInvalidation(ee,Qe.Id),Ke.succeed("Invalidation completed!")}}he(`Static Site Deployed!
414
414
 
415
415
  Source: ${L}
416
- Bucket: s3://${Z}${re?`/${re}`:""}
416
+ Bucket: s3://${Z}${ne?`/${ne}`:""}
417
417
  Size: ${Ae} MB
418
418
  ${ee?`Distribution: ${ee}`:""}
419
419
 
420
420
  View your site:
421
- https://${Z}.s3.${A}.amazonaws.com${re?`/${re}`:""}/index.html`,"green")}catch(p){r(`Deployment failed: ${p.message}`),process.exitCode=1}}),N.command("deploy:container","Deploy container (ECR push + ECS service update)").option("--cluster <name>","ECS cluster name").option("--service <name>","ECS service name").option("--repository <name>","ECR repository name").option("--image <tag>","Docker image tag",{default:"latest"}).option("--dockerfile <path>","Dockerfile path",{default:"Dockerfile"}).option("--context <path>","Docker build context",{default:"."}).option("--task-definition <name>","Task definition family name").option("--force","Force new deployment even if no changes").option("--wait","Wait for deployment to stabilize").option("--skip-security-scan","Skip pre-deployment security scan").option("--security-fail-on <severity>","Security scan fail threshold (critical, high, medium, low)",{default:"critical"}).action(async(t)=>{i("Deploying Container");try{let p=await Me(),A=p.project.region||"us-east-1",L=t?.cluster,j=t?.service,G=t?.repository,Z=t?.image||"latest",ee=t?.dockerfile||"Dockerfile",re=t?.context||".",oe=t?.force||!1,be=t?.wait||!1,we=new Date().toISOString(),Ce=crypto.randomUUID();if(!L||!j){r("--cluster and --service are required");return}if(!G){r("--repository is required");return}if(!fr(ee)){r(`Dockerfile not found: ${ee}`);return}let Ee;if(!t?.skipSecurityScan){m("Scanning build context for leaked secrets...");let{result:rn}=await Ms({sourceDir:re,failOnSeverity:t?.securityFailOn||"critical"});qs(rn),Ee=rn}else a(`Security scan skipped (--skip-security-scan)
422
- `);let Se=Sc(p,void 0,Ee);if(!Se.allowed){r(`
421
+ https://${Z}.s3.${A}.amazonaws.com${ne?`/${ne}`:""}/index.html`,"green")}catch(m){r(`Deployment failed: ${m.message}`),process.exitCode=1}}),N.command("deploy:container","Deploy container (ECR push + ECS service update)").option("--cluster <name>","ECS cluster name").option("--service <name>","ECS service name").option("--repository <name>","ECR repository name").option("--image <tag>","Docker image tag",{default:"latest"}).option("--dockerfile <path>","Dockerfile path",{default:"Dockerfile"}).option("--context <path>","Docker build context",{default:"."}).option("--task-definition <name>","Task definition family name").option("--force","Force new deployment even if no changes").option("--wait","Wait for deployment to stabilize").option("--skip-security-scan","Skip pre-deployment security scan").option("--security-fail-on <severity>","Security scan fail threshold (critical, high, medium, low)",{default:"critical"}).action(async(t)=>{i("Deploying Container");try{let m=await Ne(),A=m.project.region||"us-east-1",L=t?.cluster,j=t?.service,G=t?.repository,Z=t?.image||"latest",ee=t?.dockerfile||"Dockerfile",ne=t?.context||".",oe=t?.force||!1,be=t?.wait||!1,we=new Date().toISOString(),Ce=crypto.randomUUID();if(!L||!j){r("--cluster and --service are required");return}if(!G){r("--repository is required");return}if(!yr(ee)){r(`Dockerfile not found: ${ee}`);return}let De;if(!t?.skipSecurityScan){p("Scanning build context for leaked secrets...");let{result:rn}=await Hs({sourceDir:ne,failOnSeverity:t?.securityFailOn||"critical"});Js(rn),De=rn}else a(`Security scan skipped (--skip-security-scan)
422
+ `);let Se=Ec(m,void 0,De);if(!Se.allowed){r(`
423
423
  ✗ Container deployment blocked by security policy: ${Se.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
424
424
  `),e(`Cluster: ${L}`),e(`Service: ${j}`),e(`Repository: ${G}`),e(`Image Tag: ${Z}`),e(`Dockerfile: ${ee}`),!await d(`
425
- Deploy container now?`,!0)){e("Deployment cancelled");return}let _e=new gt(A),Ae=new It(A),Te=new o("Getting ECR credentials...");Te.start();let Ne=await _e.getAuthorizationToken();if(!Ne.authorizationData?.[0]){Te.fail("Failed to get ECR credentials");return}let Xe=Ne.authorizationData[0],Ye=(Xe.proxyEndpoint||"").replace("https://","");Te.succeed("ECR credentials obtained");let et=new o("Logging into ECR...");et.start();let Pt=Xe.authorizationToken||"",Ot=Buffer.from(Pt,"base64").toString("utf8").split(":")[1],{spawn:Ut}=await import("child_process"),sn=Ut("docker",["login","--username","AWS","--password-stdin",Ye],{stdio:["pipe","pipe","pipe"]});sn.stdin.write(Ot),sn.stdin.end(),await new Promise((rn,Jr)=>{sn.on("close",(ui)=>{if(ui===0)rn();else Jr(Error(`Docker login failed with code ${ui}`))})}),et.succeed("Logged into ECR");let jt=new o("Building Docker image...");jt.start();let ft=`${Ye}/${G}:${Z}`,Vt=Ut("docker",["build","-t",ft,"-f",ee,re],{stdio:["pipe","pipe","pipe"]});await new Promise((rn,Jr)=>{let ui="";Vt.stderr.on("data",(ro)=>{ui+=ro.toString()}),Vt.on("close",(ro)=>{if(ro===0)rn();else Jr(Error(`Docker build failed: ${ui}`))})}),jt.succeed("Docker image built");let vt=new o("Pushing image to ECR...");vt.start();let Lt=Ut("docker",["push",ft],{stdio:["pipe","pipe","pipe"]});await new Promise((rn,Jr)=>{let ui="";Lt.stderr.on("data",(ro)=>{ui+=ro.toString()}),Lt.on("close",(ro)=>{if(ro===0)rn();else Jr(Error(`Docker push failed: ${ui}`))})}),vt.succeed("Image pushed to ECR");let nr,Qn=Ut("docker",["image","inspect","--format={{.Id}}",ft],{stdio:["ignore","pipe","pipe"]}),[di,fo]=await Promise.all([new Promise((rn)=>Qn.on("close",rn)),new Response(Qn.stdout).text()]);if(di===0){let rn=fo.trim();if(/^sha256:[a-f0-9]{64}$/i.test(rn))nr=rn}let gr=await Ap(p,{imageRef:ft,imageSha256:nr,artifactRoot:re,invocationId:Ce,startedAt:we});if(!gr.allowed){r(`
426
- ✗ ECS service update blocked by release security policy: ${gr.explanation}`),e("The image remains in ECR for investigation. Remediate and rebuild it, or create an authorized, time-limited waiver."),process.exitCode=1;return}n(`✓ Release security policy allows the ECS service update
427
- `);let tn=new o("Updating ECS service...");if(tn.start(),await Ae.updateService({cluster:L,service:j,forceNewDeployment:oe}),tn.succeed("ECS service updated"),be){let rn=new o("Waiting for deployment to stabilize...");rn.start(),await Ae.waitForServiceStable(L,j),rn.succeed("Deployment stabilized")}n(`
425
+ Deploy container now?`,!0)){e("Deployment cancelled");return}let Me=new gt(A),Ae=new It(A),Ee=new o("Getting ECR credentials...");Ee.start();let Te=await Me.getAuthorizationToken();if(!Te.authorizationData?.[0]){Ee.fail("Failed to get ECR credentials");return}let Qe=Te.authorizationData[0],Ye=(Qe.proxyEndpoint||"").replace("https://","");Ee.succeed("ECR credentials obtained");let et=new o("Logging into ECR...");et.start();let Pt=Qe.authorizationToken||"",vt=Buffer.from(Pt,"base64").toString("utf8").split(":")[1],{spawn:Ot}=await import("child_process"),an=Ot("docker",["login","--username","AWS","--password-stdin",Ye],{stdio:["pipe","pipe","pipe"]});an.stdin.write(vt),an.stdin.end(),await new Promise((rn,Ar)=>{an.on("close",(Ir)=>{if(Ir===0)rn();else Ar(Error(`Docker login failed with code ${Ir}`))})}),et.succeed("Logged into ECR");let zt=new o("Building Docker image...");zt.start();let ht=`${Ye}/${G}:${Z}`,Vt=Ot("docker",["build","-t",ht,"-f",ee,ne],{stdio:["pipe","pipe","pipe"]});await new Promise((rn,Ar)=>{let Ir="";Vt.stderr.on("data",(on)=>{Ir+=on.toString()}),Vt.on("close",(on)=>{if(on===0)rn();else Ar(Error(`Docker build failed: ${Ir}`))})}),zt.succeed("Docker image built");let nn=new o("Pushing image to ECR...");nn.start();let wt=Ot("docker",["push",ht],{stdio:["pipe","pipe","pipe"]});await new Promise((rn,Ar)=>{let Ir="";wt.stderr.on("data",(on)=>{Ir+=on.toString()}),wt.on("close",(on)=>{if(on===0)rn();else Ar(Error(`Docker push failed: ${Ir}`))})}),nn.succeed("Image pushed to ECR");let Mt,Yn=Ot("docker",["image","inspect","--format={{.Id}}",ht],{stdio:["ignore","pipe","pipe"]}),[Zn,Qr]=await Promise.all([new Promise((rn)=>Yn.on("close",rn)),new Response(Yn.stdout).text()]);if(Zn===0){let rn=Qr.trim();if(/^sha256:[a-f0-9]{64}$/i.test(rn))Mt=rn}let Ki=await Lm(m,{imageRef:ht,imageSha256:Mt,artifactRoot:ne,invocationId:Ce,startedAt:we});if(!Ki.allowed){r(`
426
+ ✗ ECS service update blocked by release security policy: ${Ki.explanation}`),e("The image remains in ECR for investigation. Remediate and rebuild it, or create an authorized, time-limited waiver."),process.exitCode=1;return}n(`✓ Release security policy allows the ECS service update
427
+ `);let ur=new o("Updating ECS service...");if(ur.start(),await Ae.updateService({cluster:L,service:j,forceNewDeployment:oe}),ur.succeed("ECS service updated"),be){let rn=new o("Waiting for deployment to stabilize...");rn.start(),await Ae.waitForServiceStable(L,j),rn.succeed("Deployment stabilized")}n(`
428
428
  Container deployment complete!`),e(`
429
- Image: ${ft}`),e(`Cluster: ${L}`),e(`Service: ${j}`)}catch(p){r(`Deployment failed: ${p.message}`),process.exitCode=1}})}async function Rp(N){let t=process.cwd(),p=`${t}/.env`,A=`${t}/.env.local`,G=({stage:["stage","staging"],staging:["staging","stage"]}[N]||[N]).map((be)=>`${t}/.env.${be}`).find((be)=>fr(be));if(!G){if(fr(p))a(`No .env.${N} found, falling back to .env`);else e(`No .env.${N} or .env file found; using the existing process environment`);return null}let Z=G.split("/").pop();m(`Loading environment file: ${Z}`);let ee=null,re=null;if(fr(A)){ee=`${A}.bak`,ms(A,ee);let be=$c(A,"utf-8");for(let Ce of be.split(`
430
- `)){let Ee=Ce.trim();if(!Ee||Ee.startsWith("#"))continue;let Se=Ee.indexOf("=");if(Se===-1)continue;let De=Ee.slice(0,Se).trim();delete process.env[De]}let{unlinkSync:we}=await import("node:fs");we(A),e("Temporarily moved .env.local out of the way and purged its values from process.env")}if(fr(p))re=`${p}.bak`,ms(p,re);ms(G,p);let oe=$c(G,"utf-8");for(let be of oe.split(`
431
- `)){let we=be.trim();if(!we||we.startsWith("#"))continue;let Ce=we.indexOf("=");if(Ce===-1)continue;let Ee=we.slice(0,Ce).trim(),Se=we.slice(Ce+1).trim();process.env[Ee]=Se}return n(`Loaded ${Z}`),async()=>{let{unlinkSync:be}=await import("node:fs");if(re)ms(re,`${t}/.env`),be(re),e("Restored .env");if(ee)ms(ee,`${t}/.env.local`),be(ee),e("Restored .env.local")}}async function Ip(N,t,p,A){if(!p)return;let L=Lo(N.sites||{});if(L.size===0)return;if(!t){a("DNS: no app server IP resolved — skipping A-record reconciliation.");return}let j;try{j=Xn(p)}catch(oe){a(`DNS: ${oe instanceof Error?oe.message:String(oe)}`),e(`Point each site domain at ${t} manually.`);return}if(!j){a(`DNS provider '${p}' is not configured — skipping A-record reconciliation.`),e(`Point each site domain at ${t} manually.`);return}if(j.provider==="route53"){let oe=N.infrastructure?.dns?.hostedZoneId;if(oe&&!j.hostedZoneId)j.hostedZoneId=oe}let G=O(j),Z=N.infrastructure?.dns?.domain,ee=le(A),re=ee?"A/AAAA":"A";m(`Reconciling DNS: ${L.size} ${re} record(s) → ${t} via ${p}...`);for(let oe of L){let be=Z&&oe.endsWith(Z)?Z:oe.split(".").slice(-2).join(".");try{let we=await jn({provider:G,zone:be,fqdn:oe,ipv4:t,ipv6:ee});for(let Ce of we.published)n(` ✓ ${Ce.fqdn} ${Ce.type} → ${Ce.content}`);for(let Ce of we.warnings)a(` ⚠ ${oe}: ${Ce}`)}catch(we){a(` ⚠ ${oe}: ${we instanceof Error?we.message:String(we)} — set the address record manually.`)}}}async function Pp(N,t){let p=N.infrastructure?.dns?.records;if(!t||!p?.length)return;let A=N.infrastructure?.dns?.domain;if(!A){a("DNS records: infrastructure.dns.domain is not set — skipping declared records.");return}let L;try{L=Xn(t)}catch(Z){a(`DNS records: ${Z instanceof Error?Z.message:String(Z)}`);return}if(!L){a(`DNS records: provider '${t}' is not configured — skipping.`);return}let j=O(L),G=Ki(p,A);m(`Reconciling ${G.length} declared DNS record(s) on ${A}...`);try{let Z=await Ji({provider:j,zone:A,records:G});for(let ee of Z.outcomes){let{record:re,action:oe}=ee,be=`${re.type} ${re.name} -> ${re.content.slice(0,60)}`;if(oe==="failed")a(` ⚠ ${be}: ${ee.message||"failed"}`);else if(oe==="unchanged")e(` = ${be}`);else n(` ✓ ${be} (${oe})`)}for(let ee of Z.warnings)a(` ⚠ ${ee}`)}catch(Z){a(`DNS records: ${Z instanceof Error?Z.message:String(Z)}`)}}async function xp(N,t,p){let{plan:A,errors:L}=bc(N);for(let G of L)a(`Cloudflare CDN: ${G}`);if(!A)return;if(!t){a("Cloudflare CDN: no app server IP resolved — skipping.");return}m(`Cloudflare CDN: reconciling ${A.hosts.length} host(s) on ${A.zone}...`);let j=new Je(A.apiToken,{zoneId:A.zoneId,accountId:A.accountId});try{let G=await vc({provider:j,zone:A.zone,hosts:A.hosts,ipv4:t,ipv6:le(p),proxied:A.proxied,settings:A.settings,cache:A.cache,originGuard:A.originGuard,purge:A.purge,skipOriginProbe:A.skipOriginProbe});for(let Z of G.records)n(` ✓ ${Z.host} ${Z.type} → ${Z.content}${Z.proxied?" (proxied)":" (DNS-only)"}`);for(let Z of G.settingsChanged)e(` ✓ ${Z.id}: ${JSON.stringify(Z.from)} → ${JSON.stringify(Z.to)}`);if(G.cacheRules>0)n(` ✓ ${G.cacheRules} cache rule(s) applied`);if(G.originGuard)n(" ✓ origin guard header applied");if(G.purged)n(" ✓ edge cache purged");for(let Z of G.warnings)a(` ⚠ ${Z}`)}catch(G){a(`Cloudflare CDN: ${G instanceof Error?G.message:String(G)}`)}}async function Dp(N,t,p,A,L,j,G=!1){let Z=N.sites||{},ee=t?[t]:Object.keys(Z);if(ee.length===0){a("No sites configured in cloud.config.ts");return}let re=null,oe=!1,be=()=>{if(re||oe)return re;let we=null;try{we=Xn(p)}catch(Ce){r(Ce.message)}if(!we)return r(`DNS provider '${p}' 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"),oe=!0,null;if(we.provider==="route53"){let Ce=N.infrastructure?.dns?.hostedZoneId;if(Ce&&!we.hostedZoneId)we.hostedZoneId=Ce}return re={config:we,provider:O(we)},re};for(let we of ee){let Ce=Z[we];if(!Ce){r(`Site '${we}' not found in configuration`);continue}if(!Va(Ce)){let Ye=R(Ce);e(`Site '${we}' is ${Ye} — handled outside the static bucket pipeline.`);continue}let Ee=be();if(!Ee)return;let Se=Ce.domain;if(!Se){r(`Site '${we}' has no domain configured`);continue}if(i(`Deploying Site: ${we}`),e(`Domain: ${Se}`),e(`Source: ${Ce.root}`),e(`DNS Provider: ${p}`),Ce.build){m(`Running build command: ${Ce.build}`);try{let{execSync:Ye}=await import("node:child_process");Ye(Ce.build,{stdio:"inherit",cwd:process.cwd()}),n("Build completed successfully")}catch(Ye){r(`Build failed: ${Ye.message}`);continue}}if(!fr(Ce.root)){if(r(`Source directory not found: ${Ce.root}`),!Ce.build)e('Run your build command first (e.g., bun run generate) or add a "build" option to your site config');continue}m("Checking existing DNS records...");let De=await Ee.provider.listRecords(Se);if(De.success&&De.records.length>0){let Ye=Se.split("."),et=Ye.length>2?Ye[0]:"@",Pt=Ye.slice(-2).join("."),wt=De.records.find((Ot)=>Ot.type==="CNAME"&&(Ot.name===Se||Ot.name===et||Ot.name===`${et}.${Pt}`));if(wt){let Ot=wt.content.includes("netlify"),Ut=wt.content.includes("vercel");if(wt.content.includes("cloudfront.net"))e(`Domain already points to CloudFront: ${wt.content}`),e("Proceeding with file upload...");else{let jt=Ot?"Netlify":Ut?"Vercel":"another provider";if(a(`
433
- Existing CNAME record detected:`),e(` ${wt.name} -> ${wt.content}`),Ot||Ut)e(`
434
- This domain is currently pointing to ${jt}.`),e("Deploying will update this record to point to AWS CloudFront.");if(!(G||await d(`
435
- Update DNS record to point to AWS CloudFront?`,!0))){e("Deployment cancelled");continue}m(`Removing old ${jt} CNAME record...`);let Vt=await Ee.provider.deleteRecord(Se,{name:wt.name,type:"CNAME",content:wt.content});if(Vt.success)n(`Removed CNAME: ${wt.name} -> ${wt.content}`);else a(`Could not remove old record: ${Vt.message}`),e("The deployment will attempt to update it instead.")}}}let _e=Ce.root,Ae,Te=!!Ce.installScript;if(Te){let{mkdtempSync:Ye,copyFileSync:et,readdirSync:Pt,statSync:wt}=await import("node:fs"),{tmpdir:Ot}=await import("node:os"),{join:Ut,resolve:sn}=await import("node:path");Ae=Ye(Ut(Ot(),"cloud-install-"));let jt=sn(Ce.installScript);et(jt,Ut(Ae,"index.html"));let ft=jt.split("/").pop();if(ft!=="index.html")et(jt,Ut(Ae,ft));if(fr(Ce.root)){let Vt=Pt(Ce.root);for(let vt of Vt){let Lt=Ut(Ce.root,vt);if(wt(Lt).isFile()&&vt!=="index.html")et(Lt,Ut(Ae,vt))}}_e=Ae,e(`Install script: ${Ce.installScript}`),e(`Serving at: curl -fsSL https://${Se} | bash`)}m("Deploying to AWS (S3 + CloudFront)...");let Ne=js(N,we,Ce,j),Xe=Vs(N,we);e(`Stack: ${Ne}`);let Ge=await Ti({siteName:Xe,stackName:Ne,domain:Se,region:A,bucket:zs(N.project.slug,j,we,Ce.bucket),sourceDir:_e,certificateArn:Ce.certificateArn,dnsProvider:Ee.config,skipDnsVerification:L,passthroughUrls:Te,dynamicApp:!!N.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginDomain:N.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginPort:N.infrastructure?.compute?.cloudFrontOriginPort??N.infrastructure?.api?.port??3008,computeOriginId:N.infrastructure?.compute?.cloudFrontOriginId??`${N.project.slug}-site-ec2`,onProgress:(Ye,et)=>{if(Ye==="infrastructure")m(et||"Setting up infrastructure...");else if(Ye==="upload"){if(et?.includes("/")&&!et.includes("1/")){let Pt=et.match(/(\d+)\/(\d+)/);if(Pt){let[,wt,Ot]=Pt;if(Number(wt)%10===0||wt===Ot)e(` Uploaded ${wt}/${Ot} files`)}}}else if(Ye==="invalidate")m("Invalidating CDN cache...")}});if(Ae){let{rmSync:Ye}=await import("node:fs");Ye(Ae,{recursive:!0,force:!0})}if(Ge.success){n(`
436
- Deployment successful!`);let Ye=Ge.filesSkipped>0?`${Ge.filesUploaded} uploaded, ${Ge.filesSkipped} unchanged`:`${Ge.filesUploaded}`,et=Te?`
437
- Install: curl -fsSL https://${Ge.domain} | bash`:"";he(`Site Deployed!
438
-
439
- Domain: https://${Ge.domain}
440
- CloudFront: ${Ge.distributionDomain}
441
- Bucket: ${Ge.bucket}
429
+ Image: ${ht}`),e(`Cluster: ${L}`),e(`Service: ${j}`)}catch(m){r(`Deployment failed: ${m.message}`),process.exitCode=1}})}async function Fm(N){let t=process.cwd(),m=`${t}/.env`,A=`${t}/.env.local`,G=({stage:["stage","staging"],staging:["staging","stage"]}[N]||[N]).map((be)=>`${t}/.env.${be}`).find((be)=>yr(be));if(!G){if(yr(m))a(`No .env.${N} found, falling back to .env`);else e(`No .env.${N} or .env file found; using the existing process environment`);return null}let Z=G.split("/").pop();p(`Loading environment file: ${Z}`);let ee=null,ne=null;if(yr(A)){ee=`${A}.bak`,gs(A,ee);let be=Tc(A,"utf-8");for(let Ce of be.split(`
430
+ `)){let De=Ce.trim();if(!De||De.startsWith("#"))continue;let Se=De.indexOf("=");if(Se===-1)continue;let xe=De.slice(0,Se).trim();delete process.env[xe]}let{unlinkSync:we}=await import("node:fs");we(A),e("Temporarily moved .env.local out of the way and purged its values from process.env")}if(yr(m))ne=`${m}.bak`,gs(m,ne);gs(G,m);let oe=Tc(G,"utf-8");for(let be of oe.split(`
431
+ `)){let we=be.trim();if(!we||we.startsWith("#"))continue;let Ce=we.indexOf("=");if(Ce===-1)continue;let De=we.slice(0,Ce).trim(),Se=we.slice(Ce+1).trim();process.env[De]=Se}return n(`Loaded ${Z}`),async()=>{let{unlinkSync:be}=await import("node:fs");if(ne)gs(ne,`${t}/.env`),be(ne),e("Restored .env");if(ee)gs(ee,`${t}/.env.local`),be(ee),e("Restored .env.local")}}async function Bm(N,t,m,A){if(!m)return;let L=jo(N.sites||{});if(L.size===0)return;if(!t){a("DNS: no app server IP resolved — skipping A-record reconciliation.");return}let j;try{j=ir(m)}catch(oe){a(`DNS: ${oe instanceof Error?oe.message:String(oe)}`),e(`Point each site domain at ${t} manually.`);return}if(!j){a(`DNS provider '${m}' is not configured — skipping A-record reconciliation.`),e(`Point each site domain at ${t} manually.`);return}if(j.provider==="route53"){let oe=N.infrastructure?.dns?.hostedZoneId;if(oe&&!j.hostedZoneId)j.hostedZoneId=oe}let G=O(j),Z=N.infrastructure?.dns?.domain,ee=le(A),ne=ee?"A/AAAA":"A";p(`Reconciling DNS: ${L.size} ${ne} record(s) → ${t} via ${m}...`);for(let oe of L){let be=Z&&oe.endsWith(Z)?Z:oe.split(".").slice(-2).join(".");try{let we=await zn({provider:G,zone:be,fqdn:oe,ipv4:t,ipv6:ee});for(let Ce of we.published)n(` ✓ ${Ce.fqdn} ${Ce.type} → ${Ce.content}`);for(let Ce of we.warnings)a(` ⚠ ${oe}: ${Ce}`)}catch(we){a(` ⚠ ${oe}: ${we instanceof Error?we.message:String(we)} — set the address record manually.`)}}}async function Mm(N,t){let m=N.infrastructure?.dns?.records;if(!t||!m?.length)return;let A=N.infrastructure?.dns?.domain;if(!A){a("DNS records: infrastructure.dns.domain is not set — skipping declared records.");return}let L;try{L=ir(t)}catch(Z){a(`DNS records: ${Z instanceof Error?Z.message:String(Z)}`);return}if(!L){a(`DNS records: provider '${t}' is not configured — skipping.`);return}let j=O(L),G=Qi(m,A);p(`Reconciling ${G.length} declared DNS record(s) on ${A}...`);try{let Z=await Yi({provider:j,zone:A,records:G});for(let ee of Z.outcomes){let{record:ne,action:oe}=ee,be=`${ne.type} ${ne.name} -> ${ne.content.slice(0,60)}`;if(oe==="failed")a(` ⚠ ${be}: ${ee.message||"failed"}`);else if(oe==="unchanged")e(` = ${be}`);else n(` ✓ ${be} (${oe})`)}for(let ee of Z.warnings)a(` ⚠ ${ee}`)}catch(Z){a(`DNS records: ${Z instanceof Error?Z.message:String(Z)}`)}}async function _m(N,t,m){let{plan:A,errors:L}=xc(N);for(let G of L)a(`Cloudflare CDN: ${G}`);if(!A)return;if(!t){a("Cloudflare CDN: no app server IP resolved — skipping.");return}p(`Cloudflare CDN: reconciling ${A.hosts.length} host(s) on ${A.zone}...`);let j=new Xe(A.apiToken,{zoneId:A.zoneId,accountId:A.accountId});try{let G=await Pc({provider:j,zone:A.zone,hosts:A.hosts,ipv4:t,ipv6:le(m),proxied:A.proxied,settings:A.settings,cache:A.cache,originGuard:A.originGuard,purge:A.purge,skipOriginProbe:A.skipOriginProbe});for(let Z of G.records)n(` ✓ ${Z.host} ${Z.type} → ${Z.content}${Z.proxied?" (proxied)":" (DNS-only)"}`);for(let Z of G.settingsChanged)e(` ✓ ${Z.id}: ${JSON.stringify(Z.from)} → ${JSON.stringify(Z.to)}`);if(G.cacheRules>0)n(` ✓ ${G.cacheRules} cache rule(s) applied`);if(G.originGuard)n(" ✓ origin guard header applied");if(G.purged)n(" ✓ edge cache purged");for(let Z of G.warnings)a(` ⚠ ${Z}`)}catch(G){a(`Cloudflare CDN: ${G instanceof Error?G.message:String(G)}`)}}async function zm(N,t,m,A,L,j,G=!1){let Z=N.sites||{},ee=t?[t]:Object.keys(Z);if(ee.length===0){a("No sites configured in cloud.config.ts");return}let ne=null,oe=!1,be=()=>{if(ne||oe)return ne;let we=null;try{we=ir(m)}catch(Ce){r(Ce.message)}if(!we)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"),oe=!0,null;if(we.provider==="route53"){let Ce=N.infrastructure?.dns?.hostedZoneId;if(Ce&&!we.hostedZoneId)we.hostedZoneId=Ce}return ne={config:we,provider:O(we)},ne};for(let we of ee){let Ce=Z[we];if(!Ce){r(`Site '${we}' not found in configuration`);continue}if(!rc(Ce)){let Ye=P(Ce);e(`Site '${we}' is ${Ye} — handled outside the static bucket pipeline.`);continue}let De=be();if(!De)return;let Se=Ce.domain;if(!Se){r(`Site '${we}' has no domain configured`);continue}if(i(`Deploying Site: ${we}`),e(`Domain: ${Se}`),e(`Source: ${Ce.root}`),e(`DNS Provider: ${m}`),Ce.build){p(`Running build command: ${Ce.build}`);try{let{execSync:Ye}=await import("node:child_process");Ye(Ce.build,{stdio:"inherit",cwd:process.cwd()}),n("Build completed successfully")}catch(Ye){r(`Build failed: ${Ye.message}`);continue}}if(!yr(Ce.root)){if(r(`Source directory not found: ${Ce.root}`),!Ce.build)e('Run your build command first (e.g., bun run generate) or add a "build" option to your site config');continue}p("Checking existing DNS records...");let xe=await De.provider.listRecords(Se);if(xe.success&&xe.records.length>0){let Ye=Se.split("."),et=Ye.length>2?Ye[0]:"@",Pt=Ye.slice(-2).join("."),Bt=xe.records.find((vt)=>vt.type==="CNAME"&&(vt.name===Se||vt.name===et||vt.name===`${et}.${Pt}`));if(Bt){let vt=Bt.content.includes("netlify"),Ot=Bt.content.includes("vercel");if(Bt.content.includes("cloudfront.net"))e(`Domain already points to CloudFront: ${Bt.content}`),e("Proceeding with file upload...");else{let zt=vt?"Netlify":Ot?"Vercel":"another provider";if(a(`
433
+ Existing CNAME record detected:`),e(` ${Bt.name} -> ${Bt.content}`),vt||Ot)e(`
434
+ This domain is currently pointing to ${zt}.`),e("Deploying will update this record to point to AWS CloudFront.");if(!(G||await d(`
435
+ Update DNS record to point to AWS CloudFront?`,!0))){e("Deployment cancelled");continue}p(`Removing old ${zt} CNAME record...`);let Vt=await De.provider.deleteRecord(Se,{name:Bt.name,type:"CNAME",content:Bt.content});if(Vt.success)n(`Removed CNAME: ${Bt.name} -> ${Bt.content}`);else a(`Could not remove old record: ${Vt.message}`),e("The deployment will attempt to update it instead.")}}}let Me=Ce.root,Ae,Ee=!!Ce.installScript;if(Ee){let{mkdtempSync:Ye,copyFileSync:et,readdirSync:Pt,statSync:Bt}=await import("node:fs"),{tmpdir:vt}=await import("node:os"),{join:Ot,resolve:an}=await import("node:path");Ae=Ye(Ot(vt(),"cloud-install-"));let zt=an(Ce.installScript);et(zt,Ot(Ae,"index.html"));let ht=zt.split("/").pop();if(ht!=="index.html")et(zt,Ot(Ae,ht));if(yr(Ce.root)){let Vt=Pt(Ce.root);for(let nn of Vt){let wt=Ot(Ce.root,nn);if(Bt(wt).isFile()&&nn!=="index.html")et(wt,Ot(Ae,nn))}}Me=Ae,e(`Install script: ${Ce.installScript}`),e(`Serving at: curl -fsSL https://${Se} | bash`)}p("Deploying to AWS (S3 + CloudFront)...");let Te=Ws(N,we,Ce,j),Qe=Gs(N,we);e(`Stack: ${Te}`);let Ke=await Ci({siteName:Qe,stackName:Te,domain:Se,region:A,bucket:Ks(N.project.slug,j,we,Ce.bucket),sourceDir:Me,certificateArn:Ce.certificateArn,dnsProvider:De.config,skipDnsVerification:L,passthroughUrls:Ee,dynamicApp:!!N.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginDomain:N.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginPort:N.infrastructure?.compute?.cloudFrontOriginPort??N.infrastructure?.api?.port??3008,computeOriginId:N.infrastructure?.compute?.cloudFrontOriginId??`${N.project.slug}-site-ec2`,onProgress:(Ye,et)=>{if(Ye==="infrastructure")p(et||"Setting up infrastructure...");else if(Ye==="upload"){if(et?.includes("/")&&!et.includes("1/")){let Pt=et.match(/(\d+)\/(\d+)/);if(Pt){let[,Bt,vt]=Pt;if(Number(Bt)%10===0||Bt===vt)e(` Uploaded ${Bt}/${vt} files`)}}}else if(Ye==="invalidate")p("Invalidating CDN cache...")}});if(Ae){let{rmSync:Ye}=await import("node:fs");Ye(Ae,{recursive:!0,force:!0})}if(Ke.success){n(`
436
+ Deployment successful!`);let Ye=Ke.filesSkipped>0?`${Ke.filesUploaded} uploaded, ${Ke.filesSkipped} unchanged`:`${Ke.filesUploaded}`,et=Ee?`
437
+ Install: curl -fsSL https://${Ke.domain} | bash`:"";he(`Site Deployed!
438
+
439
+ Domain: https://${Ke.domain}
440
+ CloudFront: ${Ke.distributionDomain}
441
+ Bucket: ${Ke.bucket}
442
442
  Files: ${Ye}${et}
443
443
 
444
- Your site is now live at https://${Ge.domain}`,"green")}else throw Error(Ge.message||`Site '${we}' deployment failed`)}}import{execFileSync as Ep}from"node:child_process";import{createHash as Tp}from"node:crypto";import{existsSync as dd,mkdirSync as Np,mkdtempSync as Op,readdirSync as Lp,readFileSync as jp,rmSync as Fp,statSync as ud,writeFileSync as Bp}from"node:fs";import{tmpdir as Mp}from"node:os";import{basename as _p,join as xo,relative as zp,resolve as kc}from"node:path";var Up=new Set([".git","node_modules","dist","coverage"]);function md(N,t=N){let p=[];for(let A of Lp(N).sort()){if(Up.has(A))continue;let L=xo(N,A);if(Us(L,t))continue;let j=ud(L);if(j.isDirectory())p.push(...md(L,t));else if(j.isFile())p.push(zp(t,L))}return p}function Vp(N){let t=kc(N);if(!dd(t)||!ud(t).isDirectory())throw Error(`Container context does not exist: ${t}`);let p=Tp("sha256");for(let A of md(t))p.update(A),p.update("\x00"),p.update(jp(xo(t,A))),p.update("\x00");return p.digest("hex")}function Wp(N){return{ecr:new gt(N.region||"us-east-1",N.profile),run:(t,p,A)=>{Ep(t,p,{cwd:A?.cwd,env:A?.env,stdio:A?.stdio||"inherit"})}}}async function pd(N,t){let p=kc(N.context),A=kc(N.dockerfile||xo(p,"Dockerfile"));if(!dd(A))throw Error(`Dockerfile does not exist: ${A}`);if(!N.repository.split("/").every((Ee)=>/^[a-z0-9][a-z0-9._-]*$/.test(Ee)&&!/[._-]$/.test(Ee)&&!/[._-]{2}/.test(Ee)))throw Error("ECR repository name is invalid");let G=t||Wp(N),Z=Vp(p),ee=`sha-${Z.slice(0,20)}`,re="";try{re=(await G.ecr.describeRepositories({repositoryNames:[N.repository]})).repositories?.[0]?.repositoryUri||""}catch{}if(!re)re=(await G.ecr.createRepository({repositoryName:N.repository,imageTagMutability:"IMMUTABLE",imageScanningConfiguration:{scanOnPush:!0},tags:[{Key:"ManagedBy",Value:"ts-cloud"}]})).repository?.repositoryUri||"";if(!re)throw Error(`Could not resolve ECR repository URI for ${N.repository}`);await G.ecr.putLifecyclePolicy({repositoryName:N.repository,lifecyclePolicyText:JSON.stringify({rules:[{rulePriority:1,description:"Retain the newest 25 release images",selection:{tagStatus:"any",countType:"imageCountMoreThan",countNumber:25},action:{type:"expire"}}]})});let oe=`${re}:${ee}`,be=Op(xo(Mp(),"ts-cloud-container-"));try{G.run("docker",["version"],{stdio:"ignore"}),G.run("docker",["build","--platform",N.platform||"linux/amd64","--provenance=false","--sbom=false","--file",A,"--tag",oe,p],{cwd:p});let Ee=(await G.ecr.getAuthorizationToken()).authorizationData?.[0];if(!Ee?.authorizationToken||!Ee.proxyEndpoint)throw Error("ECR did not return Docker authorization data");let Se=Ee.proxyEndpoint.replace(/^https?:\/\//,""),De=xo(be,"docker");Np(De,{recursive:!0}),Bp(xo(De,"config.json"),JSON.stringify({auths:{[Se]:{auth:Ee.authorizationToken}}}),{mode:384}),G.run("docker",["push",oe],{env:{...process.env,DOCKER_CONFIG:De}})}finally{Fp(be,{recursive:!0,force:!0})}let Ce=(await G.ecr.describeImages({repositoryName:N.repository,imageIds:[{imageTag:ee}]})).imageDetails?.[0]?.imageDigest;if(!Ce||!/^sha256:[a-f0-9]{64}$/i.test(Ce))throw Error(`ECR did not return an immutable digest for ${_p(oe)}`);return{repository:N.repository,tag:ee,imageUri:oe,digest:Ce,digestUri:`${re}@${Ce}`,contextSha256:Z}}import{createHash as qp}from"node:crypto";function Gp(N){let t=N;return String(t?.code||t?.name||t?.statusCode||"")}function Hp(N){return Gp(N)==="ValidationError"&&String(N.message).includes("does not exist")}function Kp(N){return Object.fromEntries((N||[]).map((t)=>[t.OutputKey,t.OutputValue]))}function Jp(N){let t=N?.Aliases?.Items??N?.Aliases;if(Array.isArray(t))return t.map(String);if(typeof t==="string")return[t];return[]}function fd(N){if(!/^[a-z][a-z0-9-]{1,62}$/.test(N.slug))throw Error("Slug must be a lowercase DNS-safe identifier");if(!/^[A-Z0-9]{8,32}$/.test(N.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(N.expectedAlias))throw Error("Expected distribution alias is invalid");let t=N.imageUri.match(/@((?:sha256:)[a-f0-9]{64})$/i)?.[1];if(!t)throw Error("Container image must use an immutable ECR digest URI ending in @sha256:<64 hex>");let p=N.stackName||`${N.slug}-backend`;if(!/^[A-Za-z][A-Za-z0-9-]{0,127}$/.test(p))throw Error("CloudFormation stack name is invalid");let A=N.pathPattern||"/api/*";if(!/^\/[A-Za-z0-9_.*?~!$&'()+,;=:@%/-]+\*$/.test(A)||A==="/*")throw Error("Backend path must be a non-default wildcard pattern");let L=N.originId||`${N.slug}-backend`;if(!/^[A-Za-z0-9_.-]{1,128}$/.test(L))throw Error("CloudFront origin ID is invalid");if(N.originVerifySecret&&N.originVerifySecret.length<24)throw Error("Origin verification secret must contain at least 24 characters");if(!!N.originDomain!==!!N.certificateArn)throw Error("A custom origin domain and matching ACM certificate ARN must be provided together");return{stackName:p,region:N.region||"us-east-1",pathPattern:A,originId:L,digest:t}}function Rc(N){let t=N.region||"us-east-1";return{cloudformation:new v(t,N.profile),cloudfront:new w(N.profile),sts:new st(t,N.profile),fetch,sleep:(p)=>new Promise((A)=>setTimeout(A,p))}}function Yp(N){let t=fd(N),p=Ks({name:N.name,slug:N.slug,domain:N.originDomain||N.expectedAlias,imageUri:N.imageUri,certificateArn:N.certificateArn,originVerifySecret:N.originVerifySecret,desiredCount:N.desiredCount,database:N.database,cache:N.cache,queue:N.queue}),A=Gs(p);return A.Description=`Managed backend for existing static frontend ${N.expectedAlias}`,A.Metadata={ManagedBy:"ts-cloud",ExistingDistributionId:N.distributionId,ExistingDistributionAlias:N.expectedAlias,BackendPathPattern:t.pathPattern,ContainerDigest:t.digest},A}async function Zp(N,t,p){let A;for(let L=0;L<30;L++){let j=Date.now();try{let G=await N.fetch(t,{redirect:"manual",headers:p?{"x-origin-verify":p}:void 0}),Z=Date.now()-j,ee=await G.text(),re=ee;try{re=JSON.parse(ee)}catch{}if(G.ok)return{url:t,status:G.status,latencyMs:Z,body:re};A=Error(`origin returned ${G.status}`)}catch(G){A=G}if(L<29)await N.sleep(1e4)}throw Error(`Backend origin did not become healthy: ${A instanceof Error?A.message:String(A)}`)}async function gd(N,t){let p=fd(N),A=t||Rc({region:p.region,profile:N.profile}),[L,j]=await Promise.all([A.sts.getCallerIdentity(),A.cloudfront.getDistribution(N.distributionId)]);if(!L.Account)throw Error("AWS identity did not return an account ID");if(!j.Enabled)throw Error(`CloudFront distribution ${N.distributionId} is disabled`);if(!Jp(j).includes(N.expectedAlias))throw Error(`Distribution ${N.distributionId} does not contain expected alias ${N.expectedAlias}`);let G;try{G=(await A.cloudformation.describeStacks({stackName:p.stackName})).Stacks[0]}catch(Ne){if(!Hp(Ne))throw Ne}let Z=Yp(N),ee=JSON.stringify(Z);if(Buffer.byteLength(ee)>51200)throw Error("Generated template exceeds the direct CloudFormation template body limit");let re=Object.values(Z.Resources||{}).reduce((Ne,Xe)=>(Ne[Xe.Type]=(Ne[Xe.Type]||0)+1,Ne),{}),oe=[`cloud cdn:origin:remove ${N.distributionId} ${N.originDomain||"ALB_HOST"} --id ${p.originId} --path '${p.pathPattern}' --profile ${N.profile||"default"} --apply --confirm 'remove:${N.distributionId}:${p.pathPattern}'`,`After CloudFront is deployed, restore external DNS if changed and delete stack ${p.stackName} only with an approved data-retention decision.`],be={mode:N.apply?"apply":"plan",accountId:L.Account,stack:{name:p.stackName,existed:!!G,status:G?.StackStatus,templateSha256:qp("sha256").update(ee).digest("hex"),resourceCount:Object.keys(Z.Resources||{}).length,resourceTypes:re},distribution:{id:j.Id,alias:N.expectedAlias,domainName:j.DomainName,status:j.Status,pathPattern:p.pathPattern,originId:p.originId},artifact:{imageUri:N.imageUri,digest:p.digest},services:{database:N.database!==!1,cache:N.cache!==!1,queue:N.queue!==!1,mail:"ses",desiredCount:N.desiredCount||1},cloudFrontApplied:!1,dnsApplied:!1,applied:!1,rollback:oe};if(!N.apply)return be;let we=`${N.distributionId}:${p.pathPattern}:${p.stackName}`;if(N.confirm!==we)throw Error(`Pass exact confirmation token ${we} to provision the backend and change live routing`);if(G)try{await A.cloudformation.updateStack({stackName:p.stackName,templateBody:ee,capabilities:["CAPABILITY_NAMED_IAM"],tags:[{Key:"ManagedBy",Value:"ts-cloud"},{Key:"Application",Value:N.slug}]}),await A.cloudformation.waitForStack(p.stackName,"stack-update-complete")}catch(Ne){if(!String(Ne.message).includes("No updates are to be performed"))throw Ne}else await A.cloudformation.createStack({stackName:p.stackName,templateBody:ee,capabilities:["CAPABILITY_NAMED_IAM"],onFailure:"ROLLBACK",tags:[{Key:"ManagedBy",Value:"ts-cloud"},{Key:"Application",Value:N.slug}]}),await A.cloudformation.waitForStack(p.stackName,"stack-create-complete");let Ce=(await A.cloudformation.describeStacks({stackName:p.stackName})).Stacks[0],Ee=Kp(Ce?.Outputs),Se=Ee.AppLoadBalancerDnsName;if(!Se)throw Error("Backend stack did not return AppLoadBalancerDnsName");let De=!1;if(N.originDomain&&N.originDomain!==Se){if(!A.dns)throw Error(`External DNS provider is required to map ${N.originDomain} to ${Se}`);let Ne=await A.dns.upsertRecord(N.originDomain,{name:N.originDomain,type:"CNAME",content:Se,ttl:300});if(!Ne.success)throw Error(Ne.message||"External DNS update failed");De=!0}let _e=N.originDomain||Se,Ae=N.certificateArn?"https-only":"http-only",Te=N.skipHealthCheck?void 0:await Zp(A,`${Ae==="https-only"?"https":"http"}://${_e}/api/health`,N.originVerifySecret);return await A.cloudfront.upsertExistingDistributionOrigin(N.distributionId,{id:p.originId,domainName:_e,pathPattern:p.pathPattern,protocolPolicy:Ae,customHeaders:N.originVerifySecret?{"X-Origin-Verify":N.originVerifySecret}:void 0}),{...be,outputs:Ee,health:Te,cloudFrontApplied:!0,dnsApplied:De,applied:!0}}function hd(N={}){let p={fargate:730*(N.desiredCount||1)*0.012342500000000001,applicationLoadBalancer:16.425,natGateway:32.85,postgres:730*(N.multiAzDatabase?0.032:0.016),redis:23.36};return{monthlyUsd:Number(Object.values(p).reduce((A,L)=>A+L,0).toFixed(2)),components:Object.fromEntries(Object.entries(p).map(([A,L])=>[A,Number(L.toFixed(2))])),assumptions:["us-east-1 public on-demand rate snapshot, July 2026","one 0.25 vCPU/0.5 GB Linux Fargate task unless desiredCount is changed","one ALB before LCU charges and one NAT gateway before data processing","single-AZ db.t4g.micro PostgreSQL by default and two cache.t4g.micro Redis nodes","excludes storage, backups, CloudFront, data transfer, public IPv4, logs, email, queue requests, taxes, and free-tier credits"]}}function Ic(N){N.command("container:artifact <repository>","Build, scan, push, and resolve an immutable ECR container artifact").option("--context <path>","Docker build context",{default:"."}).option("--dockerfile <path>","Dockerfile path").option("--platform <platform>","Target platform",{default:"linux/amd64"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--profile <name>","AWS credential profile").action(async(t,p)=>{try{let A=await pd({repository:t,context:p.context||".",dockerfile:p.dockerfile,platform:p.platform,region:p.region,profile:p.profile});n("Immutable container artifact published."),e(JSON.stringify(A,null,2))}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("deploy:fullstack <distributionId> <alias>","Plan or deploy an ECS/RDS/Redis/SQS/SES backend behind an existing static site").option("--name <name>","Application display name").option("--slug <slug>","Application slug").option("--image <digest-uri>","Immutable ECR image URI with @sha256 digest").option("--stack <name>","Backend CloudFormation stack name").option("--path <pattern>","CloudFront backend path",{default:"/api/*"}).option("--origin-id <id>","CloudFront origin ID").option("--origin-domain <domain>","DNS name for the ALB origin").option("--certificate-arn <arn>","ACM certificate matching the origin domain").option("--dns-provider <provider>","External DNS provider (porkbun, godaddy, cloudflare, route53)").option("--origin-secret-env <name>","Environment variable holding the CloudFront-to-ALB secret",{default:"TS_CLOUD_ORIGIN_SECRET"}).option("--desired-count <count>","Baseline Fargate task count",{default:"1"}).option("--no-database","Do not provision PostgreSQL").option("--no-cache","Do not provision Redis").option("--no-queue","Do not provision SQS").option("--region <region>","AWS region",{default:"us-east-1"}).option("--profile <name>","AWS credential profile").option("--skip-health-check","Skip the ALB health gate before CloudFront").option("--apply","Provision and change live routing").option("--confirm <text>","Exact distribution:path:stack confirmation").action(async(t,p,A)=>{try{if(!A.slug)throw Error("--slug is required");if(!A.image)throw Error("--image must be an immutable digest URI from container:artifact");let L=A.originSecretEnv||"TS_CLOUD_ORIGIN_SECRET",j=process.env[L];if(A.apply&&!j)throw Error(`${L} must contain a stable random CloudFront-to-ALB secret before apply`);let G=Rc({region:A.region,profile:A.profile}),Z=A.originDomain&&A.apply?{...G,dns:pi(A.dnsProvider)}:G,ee=await gd({name:A.name||A.slug,slug:A.slug,imageUri:A.image,distributionId:t,expectedAlias:p,stackName:A.stack,pathPattern:A.path,originId:A.originId,originDomain:A.originDomain,certificateArn:A.certificateArn,originVerifySecret:j,desiredCount:Number(A.desiredCount)||1,database:A.database!==!1,cache:A.cache!==!1,queue:A.queue!==!1,region:A.region,profile:A.profile,skipHealthCheck:!!A.skipHealthCheck,apply:!!A.apply,confirm:A.confirm},Z);if(e(JSON.stringify(ee,null,2)),ee.applied)n("Full-stack backend is healthy and the CloudFront path patch was submitted.");else e(`Plan only. Apply with --confirm '${t}:${ee.distribution.pathPattern}:${ee.stack.name}'.`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.command("deploy:fullstack:cost","Estimate the always-on full-stack backend baseline").option("--desired-count <count>","Fargate task count",{default:"1"}).option("--multi-az-database","Estimate a two-instance database baseline").action((t)=>e(JSON.stringify(hd({desiredCount:Number(t.desiredCount)||1,multiAzDatabase:!!t.multiAzDatabase}),null,2)))}function Qp(N){return N.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function yd(N,t="/var/www"){let p=`${t}/${N}-`;return["set -u",`for TS_CLOUD_DIR in ${p}*; do`,' [ -d "$TS_CLOUD_DIR" ] || continue',' [ -d "$TS_CLOUD_DIR/releases" ] || continue',` TS_CLOUD_SITE="\${TS_CLOUD_DIR#${p}}"`," TS_CLOUD_ACTIVE=no",` for TS_CLOUD_UNIT in $(ls /etc/systemd/system/ 2>/dev/null | grep -E "^${Qp(N)}-$TS_CLOUD_SITE[-@.]" || true); do`,' systemctl is-active "$TS_CLOUD_UNIT" >/dev/null 2>&1 && TS_CLOUD_ACTIVE=yes'," done",' TS_CLOUD_SIZE="$(du -sh "$TS_CLOUD_DIR" 2>/dev/null | cut -f1)"',' echo "site:$TS_CLOUD_SITE:$TS_CLOUD_ACTIVE:${TS_CLOUD_SIZE:-?}"',"done","exit 0"]}function vd(N){if(!N)return[];let t=[];for(let p of N.split(`
445
- `)){let A=p.trim();if(!A.startsWith("site:"))continue;let[,L,j,G]=A.split(":");if(!L||j!=="no")continue;t.push({name:L,size:G||"?"})}return t}function bd(N,t,p){let A=N.map((j)=>` ${t}-${j.name} (${j.size})`).join(`
444
+ Your site is now live at https://${Ke.domain}`,"green")}else throw Error(Ke.message||`Site '${we}' deployment failed`)}}import{execFileSync as Um}from"node:child_process";import{createHash as Vm}from"node:crypto";import{existsSync as wd,mkdirSync as Wm,mkdtempSync as qm,readdirSync as Gm,readFileSync as Hm,rmSync as Km,statSync as Sd,writeFileSync as Jm}from"node:fs";import{tmpdir as Ym}from"node:os";import{basename as Zm,join as Oo,relative as Qm,resolve as Lc}from"node:path";var Xm=new Set([".git","node_modules","dist","coverage"]);function $d(N,t=N){let m=[];for(let A of Gm(N).sort()){if(Xm.has(A))continue;let L=Oo(N,A);if(zs(L,t))continue;let j=Sd(L);if(j.isDirectory())m.push(...$d(L,t));else if(j.isFile())m.push(Qm(t,L))}return m}function ef(N){let t=Lc(N);if(!wd(t)||!Sd(t).isDirectory())throw Error(`Container context does not exist: ${t}`);let m=Vm("sha256");for(let A of $d(t))m.update(A),m.update("\x00"),m.update(Hm(Oo(t,A))),m.update("\x00");return m.digest("hex")}function tf(N){return{ecr:new gt(N.region||"us-east-1",N.profile),run:(t,m,A)=>{Um(t,m,{cwd:A?.cwd,env:A?.env,stdio:A?.stdio||"inherit"})}}}async function Cd(N,t){let m=Lc(N.context),A=Lc(N.dockerfile||Oo(m,"Dockerfile"));if(!wd(A))throw Error(`Dockerfile does not exist: ${A}`);if(!N.repository.split("/").every((De)=>/^[a-z0-9][a-z0-9._-]*$/.test(De)&&!/[._-]$/.test(De)&&!/[._-]{2}/.test(De)))throw Error("ECR repository name is invalid");let G=t||tf(N),Z=ef(m),ee=`sha-${Z.slice(0,20)}`,ne="";try{ne=(await G.ecr.describeRepositories({repositoryNames:[N.repository]})).repositories?.[0]?.repositoryUri||""}catch{}if(!ne)ne=(await G.ecr.createRepository({repositoryName:N.repository,imageTagMutability:"IMMUTABLE",imageScanningConfiguration:{scanOnPush:!0},tags:[{Key:"ManagedBy",Value:"ts-cloud"}]})).repository?.repositoryUri||"";if(!ne)throw Error(`Could not resolve ECR repository URI for ${N.repository}`);await G.ecr.putLifecyclePolicy({repositoryName:N.repository,lifecyclePolicyText:JSON.stringify({rules:[{rulePriority:1,description:"Retain the newest 25 release images",selection:{tagStatus:"any",countType:"imageCountMoreThan",countNumber:25},action:{type:"expire"}}]})});let oe=`${ne}:${ee}`,be=qm(Oo(Ym(),"ts-cloud-container-"));try{G.run("docker",["version"],{stdio:"ignore"}),G.run("docker",["build","--platform",N.platform||"linux/amd64","--provenance=false","--sbom=false","--file",A,"--tag",oe,m],{cwd:m});let De=(await G.ecr.getAuthorizationToken()).authorizationData?.[0];if(!De?.authorizationToken||!De.proxyEndpoint)throw Error("ECR did not return Docker authorization data");let Se=De.proxyEndpoint.replace(/^https?:\/\//,""),xe=Oo(be,"docker");Wm(xe,{recursive:!0}),Jm(Oo(xe,"config.json"),JSON.stringify({auths:{[Se]:{auth:De.authorizationToken}}}),{mode:384}),G.run("docker",["push",oe],{env:{...process.env,DOCKER_CONFIG:xe}})}finally{Km(be,{recursive:!0,force:!0})}let Ce=(await G.ecr.describeImages({repositoryName:N.repository,imageIds:[{imageTag:ee}]})).imageDetails?.[0]?.imageDigest;if(!Ce||!/^sha256:[a-f0-9]{64}$/i.test(Ce))throw Error(`ECR did not return an immutable digest for ${Zm(oe)}`);return{repository:N.repository,tag:ee,imageUri:oe,digest:Ce,digestUri:`${ne}@${Ce}`,contextSha256:Z}}import{createHash as nf}from"node:crypto";function rf(N){let t=N;return String(t?.code||t?.name||t?.statusCode||"")}function of(N){return rf(N)==="ValidationError"&&String(N.message).includes("does not exist")}function sf(N){return Object.fromEntries((N||[]).map((t)=>[t.OutputKey,t.OutputValue]))}function af(N){let t=N?.Aliases?.Items??N?.Aliases;if(Array.isArray(t))return t.map(String);if(typeof t==="string")return[t];return[]}function Ad(N){if(!/^[a-z][a-z0-9-]{1,62}$/.test(N.slug))throw Error("Slug must be a lowercase DNS-safe identifier");if(!/^[A-Z0-9]{8,32}$/.test(N.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(N.expectedAlias))throw Error("Expected distribution alias is invalid");let t=N.imageUri.match(/@((?:sha256:)[a-f0-9]{64})$/i)?.[1];if(!t)throw Error("Container image must use an immutable ECR digest URI ending in @sha256:<64 hex>");let m=N.stackName||`${N.slug}-backend`;if(!/^[A-Za-z][A-Za-z0-9-]{0,127}$/.test(m))throw Error("CloudFormation stack name is invalid");let A=N.pathPattern||"/api/*";if(!/^\/[A-Za-z0-9_.*?~!$&'()+,;=:@%/-]+\*$/.test(A)||A==="/*")throw Error("Backend path must be a non-default wildcard pattern");let L=N.originId||`${N.slug}-backend`;if(!/^[A-Za-z0-9_.-]{1,128}$/.test(L))throw Error("CloudFront origin ID is invalid");if(N.originVerifySecret&&N.originVerifySecret.length<24)throw Error("Origin verification secret must contain at least 24 characters");if(!!N.originDomain!==!!N.certificateArn)throw Error("A custom origin domain and matching ACM certificate ARN must be provided together");return{stackName:m,region:N.region||"us-east-1",pathPattern:A,originId:L,digest:t}}function jc(N){let t=N.region||"us-east-1";return{cloudformation:new v(t,N.profile),cloudfront:new w(N.profile),sts:new st(t,N.profile),fetch,sleep:(m)=>new Promise((A)=>setTimeout(A,m))}}function cf(N){let t=Ad(N),m=oa({name:N.name,slug:N.slug,domain:N.originDomain||N.expectedAlias,imageUri:N.imageUri,certificateArn:N.certificateArn,originVerifySecret:N.originVerifySecret,desiredCount:N.desiredCount,database:N.database,cache:N.cache,queue:N.queue}),A=ia(m);return A.Description=`Managed backend for existing static frontend ${N.expectedAlias}`,A.Metadata={ManagedBy:"ts-cloud",ExistingDistributionId:N.distributionId,ExistingDistributionAlias:N.expectedAlias,BackendPathPattern:t.pathPattern,ContainerDigest:t.digest},A}async function lf(N,t,m){let A;for(let L=0;L<30;L++){let j=Date.now();try{let G=await N.fetch(t,{redirect:"manual",headers:m?{"x-origin-verify":m}:void 0}),Z=Date.now()-j,ee=await G.text(),ne=ee;try{ne=JSON.parse(ee)}catch{}if(G.ok)return{url:t,status:G.status,latencyMs:Z,body:ne};A=Error(`origin returned ${G.status}`)}catch(G){A=G}if(L<29)await N.sleep(1e4)}throw Error(`Backend origin did not become healthy: ${A instanceof Error?A.message:String(A)}`)}async function kd(N,t){let m=Ad(N),A=t||jc({region:m.region,profile:N.profile}),[L,j]=await Promise.all([A.sts.getCallerIdentity(),A.cloudfront.getDistribution(N.distributionId)]);if(!L.Account)throw Error("AWS identity did not return an account ID");if(!j.Enabled)throw Error(`CloudFront distribution ${N.distributionId} is disabled`);if(!af(j).includes(N.expectedAlias))throw Error(`Distribution ${N.distributionId} does not contain expected alias ${N.expectedAlias}`);let G;try{G=(await A.cloudformation.describeStacks({stackName:m.stackName})).Stacks[0]}catch(Te){if(!of(Te))throw Te}let Z=cf(N),ee=JSON.stringify(Z);if(Buffer.byteLength(ee)>51200)throw Error("Generated template exceeds the direct CloudFormation template body limit");let ne=Object.values(Z.Resources||{}).reduce((Te,Qe)=>(Te[Qe.Type]=(Te[Qe.Type]||0)+1,Te),{}),oe=[`cloud cdn:origin:remove ${N.distributionId} ${N.originDomain||"ALB_HOST"} --id ${m.originId} --path '${m.pathPattern}' --profile ${N.profile||"default"} --apply --confirm 'remove:${N.distributionId}:${m.pathPattern}'`,`After CloudFront is deployed, restore external DNS if changed and delete stack ${m.stackName} only with an approved data-retention decision.`],be={mode:N.apply?"apply":"plan",accountId:L.Account,stack:{name:m.stackName,existed:!!G,status:G?.StackStatus,templateSha256:nf("sha256").update(ee).digest("hex"),resourceCount:Object.keys(Z.Resources||{}).length,resourceTypes:ne},distribution:{id:j.Id,alias:N.expectedAlias,domainName:j.DomainName,status:j.Status,pathPattern:m.pathPattern,originId:m.originId},artifact:{imageUri:N.imageUri,digest:m.digest},services:{database:N.database!==!1,cache:N.cache!==!1,queue:N.queue!==!1,mail:"ses",desiredCount:N.desiredCount||1},cloudFrontApplied:!1,dnsApplied:!1,applied:!1,rollback:oe};if(!N.apply)return be;let we=`${N.distributionId}:${m.pathPattern}:${m.stackName}`;if(N.confirm!==we)throw Error(`Pass exact confirmation token ${we} to provision the backend and change live routing`);if(G)try{await A.cloudformation.updateStack({stackName:m.stackName,templateBody:ee,capabilities:["CAPABILITY_NAMED_IAM"],tags:[{Key:"ManagedBy",Value:"ts-cloud"},{Key:"Application",Value:N.slug}]}),await A.cloudformation.waitForStack(m.stackName,"stack-update-complete")}catch(Te){if(!String(Te.message).includes("No updates are to be performed"))throw Te}else await A.cloudformation.createStack({stackName:m.stackName,templateBody:ee,capabilities:["CAPABILITY_NAMED_IAM"],onFailure:"ROLLBACK",tags:[{Key:"ManagedBy",Value:"ts-cloud"},{Key:"Application",Value:N.slug}]}),await A.cloudformation.waitForStack(m.stackName,"stack-create-complete");let Ce=(await A.cloudformation.describeStacks({stackName:m.stackName})).Stacks[0],De=sf(Ce?.Outputs),Se=De.AppLoadBalancerDnsName;if(!Se)throw Error("Backend stack did not return AppLoadBalancerDnsName");let xe=!1;if(N.originDomain&&N.originDomain!==Se){if(!A.dns)throw Error(`External DNS provider is required to map ${N.originDomain} to ${Se}`);let Te=await A.dns.upsertRecord(N.originDomain,{name:N.originDomain,type:"CNAME",content:Se,ttl:300});if(!Te.success)throw Error(Te.message||"External DNS update failed");xe=!0}let Me=N.originDomain||Se,Ae=N.certificateArn?"https-only":"http-only",Ee=N.skipHealthCheck?void 0:await lf(A,`${Ae==="https-only"?"https":"http"}://${Me}/api/health`,N.originVerifySecret);return await A.cloudfront.upsertExistingDistributionOrigin(N.distributionId,{id:m.originId,domainName:Me,pathPattern:m.pathPattern,protocolPolicy:Ae,customHeaders:N.originVerifySecret?{"X-Origin-Verify":N.originVerifySecret}:void 0}),{...be,outputs:De,health:Ee,cloudFrontApplied:!0,dnsApplied:xe,applied:!0}}function Rd(N={}){let m={fargate:730*(N.desiredCount||1)*0.012342500000000001,applicationLoadBalancer:16.425,natGateway:32.85,postgres:730*(N.multiAzDatabase?0.032:0.016),redis:23.36};return{monthlyUsd:Number(Object.values(m).reduce((A,L)=>A+L,0).toFixed(2)),components:Object.fromEntries(Object.entries(m).map(([A,L])=>[A,Number(L.toFixed(2))])),assumptions:["us-east-1 public on-demand rate snapshot, July 2026","one 0.25 vCPU/0.5 GB Linux Fargate task unless desiredCount is changed","one ALB before LCU charges and one NAT gateway before data processing","single-AZ db.t4g.micro PostgreSQL by default and two cache.t4g.micro Redis nodes","excludes storage, backups, CloudFront, data transfer, public IPv4, logs, email, queue requests, taxes, and free-tier credits"]}}function Fc(N){N.command("container:artifact <repository>","Build, scan, push, and resolve an immutable ECR container artifact").option("--context <path>","Docker build context",{default:"."}).option("--dockerfile <path>","Dockerfile path").option("--platform <platform>","Target platform",{default:"linux/amd64"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--profile <name>","AWS credential profile").action(async(t,m)=>{try{let A=await Cd({repository:t,context:m.context||".",dockerfile:m.dockerfile,platform:m.platform,region:m.region,profile:m.profile});n("Immutable container artifact published."),e(JSON.stringify(A,null,2))}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("deploy:fullstack <distributionId> <alias>","Plan or deploy an ECS/RDS/Redis/SQS/SES backend behind an existing static site").option("--name <name>","Application display name").option("--slug <slug>","Application slug").option("--image <digest-uri>","Immutable ECR image URI with @sha256 digest").option("--stack <name>","Backend CloudFormation stack name").option("--path <pattern>","CloudFront backend path",{default:"/api/*"}).option("--origin-id <id>","CloudFront origin ID").option("--origin-domain <domain>","DNS name for the ALB origin").option("--certificate-arn <arn>","ACM certificate matching the origin domain").option("--dns-provider <provider>","External DNS provider (porkbun, godaddy, cloudflare, route53)").option("--origin-secret-env <name>","Environment variable holding the CloudFront-to-ALB secret",{default:"TS_CLOUD_ORIGIN_SECRET"}).option("--desired-count <count>","Baseline Fargate task count",{default:"1"}).option("--no-database","Do not provision PostgreSQL").option("--no-cache","Do not provision Redis").option("--no-queue","Do not provision SQS").option("--region <region>","AWS region",{default:"us-east-1"}).option("--profile <name>","AWS credential profile").option("--skip-health-check","Skip the ALB health gate before CloudFront").option("--apply","Provision and change live routing").option("--confirm <text>","Exact distribution:path:stack confirmation").action(async(t,m,A)=>{try{if(!A.slug)throw Error("--slug is required");if(!A.image)throw Error("--image must be an immutable digest URI from container:artifact");let L=A.originSecretEnv||"TS_CLOUD_ORIGIN_SECRET",j=process.env[L];if(A.apply&&!j)throw Error(`${L} must contain a stable random CloudFront-to-ALB secret before apply`);let G=jc({region:A.region,profile:A.profile}),Z=A.originDomain&&A.apply?{...G,dns:vi(A.dnsProvider)}:G,ee=await kd({name:A.name||A.slug,slug:A.slug,imageUri:A.image,distributionId:t,expectedAlias:m,stackName:A.stack,pathPattern:A.path,originId:A.originId,originDomain:A.originDomain,certificateArn:A.certificateArn,originVerifySecret:j,desiredCount:Number(A.desiredCount)||1,database:A.database!==!1,cache:A.cache!==!1,queue:A.queue!==!1,region:A.region,profile:A.profile,skipHealthCheck:!!A.skipHealthCheck,apply:!!A.apply,confirm:A.confirm},Z);if(e(JSON.stringify(ee,null,2)),ee.applied)n("Full-stack backend is healthy and the CloudFront path patch was submitted.");else e(`Plan only. Apply with --confirm '${t}:${ee.distribution.pathPattern}:${ee.stack.name}'.`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.command("deploy:fullstack:cost","Estimate the always-on full-stack backend baseline").option("--desired-count <count>","Fargate task count",{default:"1"}).option("--multi-az-database","Estimate a two-instance database baseline").action((t)=>e(JSON.stringify(Rd({desiredCount:Number(t.desiredCount)||1,multiAzDatabase:!!t.multiAzDatabase}),null,2)))}function df(N){return N.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Id(N,t="/var/www"){let m=`${t}/${N}-`;return["set -u",`for TS_CLOUD_DIR in ${m}*; do`,' [ -d "$TS_CLOUD_DIR" ] || continue',' [ -d "$TS_CLOUD_DIR/releases" ] || continue',` TS_CLOUD_SITE="\${TS_CLOUD_DIR#${m}}"`," TS_CLOUD_ACTIVE=no",` for TS_CLOUD_UNIT in $(ls /etc/systemd/system/ 2>/dev/null | grep -E "^${df(N)}-$TS_CLOUD_SITE[-@.]" || true); do`,' systemctl is-active "$TS_CLOUD_UNIT" >/dev/null 2>&1 && TS_CLOUD_ACTIVE=yes'," done",' TS_CLOUD_SIZE="$(du -sh "$TS_CLOUD_DIR" 2>/dev/null | cut -f1)"',' echo "site:$TS_CLOUD_SITE:$TS_CLOUD_ACTIVE:${TS_CLOUD_SIZE:-?}"',"done","exit 0"]}function Pd(N){if(!N)return[];let t=[];for(let m of N.split(`
445
+ `)){let A=m.trim();if(!A.startsWith("site:"))continue;let[,L,j,G]=A.split(":");if(!L||j!=="no")continue;t.push({name:L,size:G||"?"})}return t}function xd(N,t,m){let A=N.map((j)=>` ${t}-${j.name} (${j.size})`).join(`
446
446
  `),L=N.length===1?"it":"them";return`This server still holds ${N.length} site tree${N.length===1?"":"s"} with nothing running for ${L}:
447
447
  ${A}
448
448
  `+"A site moved off this box keeps its files here on purpose — they are the rollback, and they are what makes "+`the move reversible until this server is destroyed. Destroying it now discards that.
449
- Verify the site is serving from its new home first, then re-run with ${p} to destroy it anyway.`}var Xp="--discard-drained-sites";async function ef(N,t,p,A){if(A)return!0;let L=t.project.slug,j;try{let Z=await N.findComputeTargets({slug:L,environment:p,role:"app",stackName:E(t,p)});if(Z.length===0)return!0;let ee=await N.runRemoteDeploy({targets:Z,commands:yd(L),comment:`ts-cloud scan drained sites ${L}`,tags:{Project:L,Environment:p,Role:"app"}});if(!ee.success)return a(`Could not check the server for moved-off site files: ${ee.error||"unknown error"}`),!0;j=ee.perInstance.map((re)=>re.output??"").join(`
450
- `)}catch(Z){return a(`Could not check the server for moved-off site files: ${Z instanceof Error?Z.message:String(Z)}`),!0}let G=vd(j);if(G.length===0)return!0;return r(bd(G,L,Xp)),!1}function Pc(N){N.command("destroy","Destroy the single-server compute (instance + firewall)").option("--env <env>","Environment",{default:"production"}).option("--force","Skip the confirmation prompt").option("--discard-drained-sites","Destroy even though a moved site left its rollback files here").action(async(t)=>{i("Destroy Compute");let p=await Me(),A=t?.env||"production",L=U(p),j=x({config:p,provider:L});if(!j.destroyCompute){r(`The ${L} driver does not support compute teardown`);return}if(a(`This terminates the ${L} server for ${p.project.slug}/${A} and deletes its firewall.`),!await ef(j,p,A,!!t?.discardDrainedSites)){process.exitCode=1;return}if(!t?.force){if(!await d("This is irreversible. Continue?",!1)){e("Cancelled");return}}let G=new o("Destroying compute...");G.start();try{let{destroyed:Z}=await j.destroyCompute({config:p,environment:A});if(G.succeed("Compute destroyed"),Z.length>0)Z.forEach((ee)=>e(` removed ${ee}`));else e("Nothing to destroy (no matching resources found)")}catch(Z){G.fail("Teardown failed"),r(Z instanceof Error?Z.message:String(Z))}})}import{statSync as tf,writeFileSync as nf}from"node:fs";function xc(N){N.command("stack:list","List all CloudFormation stacks").action(async()=>{i("CloudFormation Stacks");try{let p=(await Me()).project.region||"us-east-1",A=new v(p),L=new o("Loading stacks...");L.start();let j=await A.listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS"]);if(L.succeed(`Found ${j.StackSummaries.length} stacks`),j.StackSummaries.length===0){e("No stacks found");return}let G=["Stack Name","Status","Created","Updated"],Z=j.StackSummaries.map((ee)=>[ee.StackName,ee.StackStatus,new Date(ee.CreationTime).toLocaleString(),ee.LastUpdatedTime?new Date(ee.LastUpdatedTime).toLocaleString():"Never"]);c(G,Z)}catch(t){r(`Failed to list stacks: ${t.message}`)}}),N.command("stack:describe STACK_NAME","Describe a CloudFormation stack").action(async(t)=>{i(`Stack: ${t}`);try{let A=(await Me()).project.region||"us-east-1",L=new v(A),j=new o("Loading stack details...");j.start();let G=await L.describeStacks({stackName:t});if(!G.Stacks||G.Stacks.length===0){j.fail("Stack not found");return}let Z=G.Stacks[0];if(j.succeed("Stack details loaded"),e(`
449
+ Verify the site is serving from its new home first, then re-run with ${m} to destroy it anyway.`}var uf="--discard-drained-sites";async function pf(N,t,m,A){if(A)return!0;let L=t.project.slug,j;try{let Z=await N.findComputeTargets({slug:L,environment:m,role:"app",stackName:E(t,m)});if(Z.length===0)return!0;let ee=await N.runRemoteDeploy({targets:Z,commands:Id(L),comment:`ts-cloud scan drained sites ${L}`,tags:{Project:L,Environment:m,Role:"app"}});if(!ee.success)return a(`Could not check the server for moved-off site files: ${ee.error||"unknown error"}`),!0;j=ee.perInstance.map((ne)=>ne.output??"").join(`
450
+ `)}catch(Z){return a(`Could not check the server for moved-off site files: ${Z instanceof Error?Z.message:String(Z)}`),!0}let G=Pd(j);if(G.length===0)return!0;return r(xd(G,L,uf)),!1}function Bc(N){N.command("destroy","Destroy the single-server compute (instance + firewall)").option("--env <env>","Environment",{default:"production"}).option("--force","Skip the confirmation prompt").option("--discard-drained-sites","Destroy even though a moved site left its rollback files here").action(async(t)=>{i("Destroy Compute");let m=await Ne(),A=t?.env||"production",L=U(m),j=_({config:m,provider:L});if(!j.destroyCompute){r(`The ${L} driver does not support compute teardown`);return}if(a(`This terminates the ${L} server for ${m.project.slug}/${A} and deletes its firewall.`),!await pf(j,m,A,!!t?.discardDrainedSites)){process.exitCode=1;return}if(!t?.force){if(!await d("This is irreversible. Continue?",!1)){e("Cancelled");return}}let G=new o("Destroying compute...");G.start();try{let{destroyed:Z}=await j.destroyCompute({config:m,environment:A});if(G.succeed("Compute destroyed"),Z.length>0)Z.forEach((ee)=>e(` removed ${ee}`));else e("Nothing to destroy (no matching resources found)")}catch(Z){G.fail("Teardown failed"),r(Z instanceof Error?Z.message:String(Z))}})}import{statSync as mf,writeFileSync as ff}from"node:fs";function Mc(N){N.command("stack:list","List all CloudFormation stacks").action(async()=>{i("CloudFormation Stacks");try{let m=(await Ne()).project.region||"us-east-1",A=new v(m),L=new o("Loading stacks...");L.start();let j=await A.listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS"]);if(L.succeed(`Found ${j.StackSummaries.length} stacks`),j.StackSummaries.length===0){e("No stacks found");return}let G=["Stack Name","Status","Created","Updated"],Z=j.StackSummaries.map((ee)=>[ee.StackName,ee.StackStatus,new Date(ee.CreationTime).toLocaleString(),ee.LastUpdatedTime?new Date(ee.LastUpdatedTime).toLocaleString():"Never"]);c(G,Z)}catch(t){r(`Failed to list stacks: ${t.message}`)}}),N.command("stack:describe STACK_NAME","Describe a CloudFormation stack").action(async(t)=>{i(`Stack: ${t}`);try{let A=(await Ne()).project.region||"us-east-1",L=new v(A),j=new o("Loading stack details...");j.start();let G=await L.describeStacks({stackName:t});if(!G.Stacks||G.Stacks.length===0){j.fail("Stack not found");return}let Z=G.Stacks[0];if(j.succeed("Stack details loaded"),e(`
451
451
  Stack Information:`),e(` - Name: ${Z.StackName}`),e(` - Status: ${Z.StackStatus}`),e(` - Created: ${new Date(Z.CreationTime).toLocaleString()}`),Z.LastUpdatedTime)e(` - Updated: ${new Date(Z.LastUpdatedTime).toLocaleString()}`);if(Z.Parameters&&Z.Parameters.length>0){e(`
452
- Parameters:`);for(let re of Z.Parameters)e(` - ${re.ParameterKey}: ${re.ParameterValue}`)}if(Z.Outputs&&Z.Outputs.length>0){e(`
453
- Outputs:`);for(let re of Z.Outputs)if(e(` - ${re.OutputKey}: ${re.OutputValue}`),re.Description)e(` ${re.Description}`)}if(Z.Tags&&Z.Tags.length>0){e(`
454
- Tags:`);for(let re of Z.Tags)e(` - ${re.Key}: ${re.Value}`)}m(`
452
+ Parameters:`);for(let ne of Z.Parameters)e(` - ${ne.ParameterKey}: ${ne.ParameterValue}`)}if(Z.Outputs&&Z.Outputs.length>0){e(`
453
+ Outputs:`);for(let ne of Z.Outputs)if(e(` - ${ne.OutputKey}: ${ne.OutputValue}`),ne.Description)e(` ${ne.Description}`)}if(Z.Tags&&Z.Tags.length>0){e(`
454
+ Tags:`);for(let ne of Z.Tags)e(` - ${ne.Key}: ${ne.Value}`)}p(`
455
455
  Loading stack resources...`);let ee=await L.listStackResources(t);if(ee.StackResourceSummaries.length>0){e(`
456
- Resources (${ee.StackResourceSummaries.length}):`);let re=["Logical ID","Type","Status"],oe=ee.StackResourceSummaries.slice(0,10).map((be)=>[be.LogicalResourceId,be.ResourceType,be.ResourceStatus]);if(c(re,oe),ee.StackResourceSummaries.length>10)e(`
457
- ... and ${ee.StackResourceSummaries.length-10} more resources`)}}catch(p){r(`Failed to describe stack: ${p.message}`)}}),N.command("stack:delete STACK_NAME","Delete a CloudFormation stack").option("--yes","Skip the confirmation prompt").action(async(t,p)=>{i(`Delete Stack: ${t}`);try{let L=(await Me()).project.region||"us-east-1";if(a("This will permanently delete the stack and all its resources!"),!(p?.yes||await d(`
458
- Are you sure you want to delete this stack?`,!1))){e("Deletion cancelled");return}let G=new v(L),Z=new o("Deleting stack...");Z.start(),await G.deleteStack(t),Z.succeed("Stack deletion initiated"),m("Waiting for stack deletion..."),await G.waitForStack(t,"stack-delete-complete"),n("Stack deleted successfully!")}catch(A){r(`Failed to delete stack: ${A.message}`)}}),N.command("stack:events STACK_NAME","Show stack events").option("--limit <number>","Limit number of events",{default:"20"}).action(async(t,p)=>{i(`Stack Events: ${t}`);try{let L=(await Me()).project.region||"us-east-1",j=new v(L),G=new o("Loading events...");G.start();let Z=await j.describeStackEvents(t);G.succeed(`Found ${Z.StackEvents.length} events`);let ee=p?.limit?Number.parseInt(p.limit):20,re=Z.StackEvents.slice(0,ee);if(re.length===0){e("No events found");return}let oe=["Time","Resource","Status","Reason"],be=re.map((we)=>[new Date(we.Timestamp).toLocaleString(),we.LogicalResourceId,we.ResourceStatus,we.ResourceStatusReason||""]);c(oe,be)}catch(A){r(`Failed to load events: ${A.message}`)}}),N.command("stack:outputs STACK_NAME","Show stack outputs").action(async(t)=>{i(`Stack Outputs: ${t}`);try{let A=(await Me()).project.region||"us-east-1",L=new v(A),j=new o("Loading stack outputs...");j.start();let G=await L.describeStacks({stackName:t});if(!G.Stacks||G.Stacks.length===0){j.fail("Stack not found");return}let Z=G.Stacks[0];if(j.succeed("Stack outputs loaded"),!Z.Outputs||Z.Outputs.length===0){e("No outputs found for this stack");return}let ee=["Key","Value","Description","Export Name"],re=Z.Outputs.map((oe)=>[oe.OutputKey||"",oe.OutputValue||"",oe.Description||"",oe.ExportName||""]);c(ee,re),e(`
459
- Copy-friendly format:`);for(let oe of Z.Outputs)e(`${oe.OutputKey}=${oe.OutputValue}`)}catch(p){r(`Failed to load outputs: ${p.message}`)}}),N.command("stack:export STACK_NAME","Export stack template").option("--output <file>","Output file path").option("--format <format>","Output format (json or yaml)",{default:"json"}).action(async(t,p)=>{i(`Export Stack: ${t}`);try{let L=(await Me()).project.region||"us-east-1",j=new v(L),G=new o("Fetching stack template...");G.start();let Z=await j.getTemplate(t);if(!Z.TemplateBody){G.fail("Template not found");return}G.succeed("Template fetched");let ee=p?.format||"json",re=Z.TemplateBody;if(ee==="json"){let oe=JSON.parse(re);re=JSON.stringify(oe,null,2)}if(p?.output){let oe=p.output;nf(oe,re,"utf-8"),n(`Template exported to: ${oe}`);let we=(tf(oe).size/1024).toFixed(2);e(`File size: ${we} KB`)}else e(`
460
- Template:`),console.log(re)}catch(A){r(`Failed to export template: ${A.message}`)}})}import{existsSync as rf}from"node:fs";function Dc(N,t){N.command("upgrade","Upgrade CLI to latest version").action(async()=>{Ar("upgrade",{message:`Self-update is disabled for this ${t} build because no signed update-manifest source is configured.`,nextAction:"Install a verified release with your package manager or configure platform maintenance updates."})}),N.command("doctor","Check system requirements and AWS credentials").action(async()=>{if(i("System Diagnostics"),m("Checking Bun..."),n(`Bun ${process.versions.bun}`),m("Checking AWS CLI..."),await Ns())n("AWS CLI is installed");else r("AWS CLI is not installed"),e("Install: https://aws.amazon.com/cli/");if(m("Checking AWS credentials..."),await Ds()){n("AWS credentials are configured");let j=await ks();if(j)e(`Account ID: ${j}`)}else r("AWS credentials are not configured"),e("Run: aws configure");m("Checking CloudFront list access...");let L=!1;try{let{CloudFrontClient:j}=await import("./chunk-knthwdhz.js");await new j().listDistributions(),n("CloudFront list access is enabled"),L=!0}catch(j){a(`CloudFront list check failed: ${j.message}`)}if(L){m("Checking CloudFront create access...");try{let j=new v("us-east-1"),G=JSON.stringify({AWSTemplateFormatVersion:"2010-09-09",Description:"Test CloudFront access",Resources:{TestOAC:{Type:"AWS::CloudFront::OriginAccessControl",Properties:{OriginAccessControlConfig:{Name:"test-oac-validation",OriginAccessControlOriginType:"s3",SigningBehavior:"always",SigningProtocol:"sigv4"}}}}});await j.validateTemplate(G),n("CloudFront create access appears enabled")}catch(j){if(j.message?.includes("403")||j.message?.includes("AccessDenied")||j.message?.includes("must be verified"))r("CloudFront create access denied - account verification required"),e(""),e("Your AWS account needs to be verified for CloudFront."),e("This is required before you can create CloudFront distributions."),e(""),e("To verify your account:"),e(" 1. Go to: https://console.aws.amazon.com/support/home#/"),e(" 2. Create a support case"),e(" 3. Select: Service limit increase > CloudFront"),e(' 4. Request: "Please verify my account for CloudFront access"'),e(""),e("Verification usually takes 1-2 business days.");else n("CloudFront create access appears enabled (validation passed)")}}m("Checking S3 access...");try{let{S3Client:j}=await import("./chunk-4v6f496n.js");await new j("us-east-1").listBuckets(),n("S3 access is enabled")}catch(j){a(`S3 check failed: ${j.message}`)}m("Checking ACM (SSL certificates) access...");try{await new q("us-east-1").listCertificates(),n("ACM access is enabled")}catch(j){a(`ACM check failed: ${j.message}`)}if(m("Checking configuration..."),rf("cloud.config.ts"))n("cloud.config.ts found");else a("cloud.config.ts not found"),e("Run: cloud init")}),N.command("regions","List available AWS regions").action(async()=>{i("AWS Regions");let p=new o("Fetching regions...");p.start();let A=await xs();p.stop(),A.forEach((L)=>{console.log(` ${L}`)})}),N.command("version","Show the version of the CLI").alias("v").action(()=>{console.log(`ts-cloud v${t}`)})}function Ec(N){N.command("analytics:sites:list","List all analytics sites").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t)=>{i("Analytics Sites");try{let p=new z(t.region),A=new o("Fetching sites...");A.start();let L=await p.scan({TableName:t.table,FilterExpression:"begins_with(pk, :pk)",ExpressionAttributeValues:{":pk":{S:"SITE#"}}});if(A.succeed(`Found ${L.Items?.length||0} site(s)`),!L.Items||L.Items.length===0){e("No analytics sites found"),e("Use `cloud analytics:sites:create` to create a new site");return}let j=L.Items.map((G)=>z.unmarshal(G));c(["ID","Name","Domains","Active","Created"],j.map((G)=>[G.siteId||"N/A",G.name||"Unnamed",(G.domains||[]).join(", ")||"-",G.isActive?"Yes":"No",G.createdAt?new Date(G.createdAt).toLocaleDateString():"N/A"]))}catch(p){r(`Failed to list sites: ${p.message}`),process.exit(1)}}),N.command("analytics:sites:create","Create a new analytics site").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","Site name").option("--domain <domain>","Site domain(s) (can be specified multiple times)").action(async(t)=>{i("Create Analytics Site");try{let p=new z(t.region),A=t.name||await I("Site name","My Site"),L=[];if(t.domain)L=Array.isArray(t.domain)?t.domain:[t.domain];else L=(await I("Site domains (comma-separated)","example.com")).split(",").map((oe)=>oe.trim()).filter(Boolean);if(e(`
456
+ Resources (${ee.StackResourceSummaries.length}):`);let ne=["Logical ID","Type","Status"],oe=ee.StackResourceSummaries.slice(0,10).map((be)=>[be.LogicalResourceId,be.ResourceType,be.ResourceStatus]);if(c(ne,oe),ee.StackResourceSummaries.length>10)e(`
457
+ ... and ${ee.StackResourceSummaries.length-10} more resources`)}}catch(m){r(`Failed to describe stack: ${m.message}`)}}),N.command("stack:delete STACK_NAME","Delete a CloudFormation stack").option("--yes","Skip the confirmation prompt").action(async(t,m)=>{i(`Delete Stack: ${t}`);try{let L=(await Ne()).project.region||"us-east-1";if(a("This will permanently delete the stack and all its resources!"),!(m?.yes||await d(`
458
+ Are you sure you want to delete this stack?`,!1))){e("Deletion cancelled");return}let G=new v(L),Z=new o("Deleting stack...");Z.start(),await G.deleteStack(t),Z.succeed("Stack deletion initiated"),p("Waiting for stack deletion..."),await G.waitForStack(t,"stack-delete-complete"),n("Stack deleted successfully!")}catch(A){r(`Failed to delete stack: ${A.message}`)}}),N.command("stack:events STACK_NAME","Show stack events").option("--limit <number>","Limit number of events",{default:"20"}).action(async(t,m)=>{i(`Stack Events: ${t}`);try{let L=(await Ne()).project.region||"us-east-1",j=new v(L),G=new o("Loading events...");G.start();let Z=await j.describeStackEvents(t);G.succeed(`Found ${Z.StackEvents.length} events`);let ee=m?.limit?Number.parseInt(m.limit):20,ne=Z.StackEvents.slice(0,ee);if(ne.length===0){e("No events found");return}let oe=["Time","Resource","Status","Reason"],be=ne.map((we)=>[new Date(we.Timestamp).toLocaleString(),we.LogicalResourceId,we.ResourceStatus,we.ResourceStatusReason||""]);c(oe,be)}catch(A){r(`Failed to load events: ${A.message}`)}}),N.command("stack:outputs STACK_NAME","Show stack outputs").action(async(t)=>{i(`Stack Outputs: ${t}`);try{let A=(await Ne()).project.region||"us-east-1",L=new v(A),j=new o("Loading stack outputs...");j.start();let G=await L.describeStacks({stackName:t});if(!G.Stacks||G.Stacks.length===0){j.fail("Stack not found");return}let Z=G.Stacks[0];if(j.succeed("Stack outputs loaded"),!Z.Outputs||Z.Outputs.length===0){e("No outputs found for this stack");return}let ee=["Key","Value","Description","Export Name"],ne=Z.Outputs.map((oe)=>[oe.OutputKey||"",oe.OutputValue||"",oe.Description||"",oe.ExportName||""]);c(ee,ne),e(`
459
+ Copy-friendly format:`);for(let oe of Z.Outputs)e(`${oe.OutputKey}=${oe.OutputValue}`)}catch(m){r(`Failed to load outputs: ${m.message}`)}}),N.command("stack:export STACK_NAME","Export stack template").option("--output <file>","Output file path").option("--format <format>","Output format (json or yaml)",{default:"json"}).action(async(t,m)=>{i(`Export Stack: ${t}`);try{let L=(await Ne()).project.region||"us-east-1",j=new v(L),G=new o("Fetching stack template...");G.start();let Z=await j.getTemplate(t);if(!Z.TemplateBody){G.fail("Template not found");return}G.succeed("Template fetched");let ee=m?.format||"json",ne=Z.TemplateBody;if(ee==="json"){let oe=JSON.parse(ne);ne=JSON.stringify(oe,null,2)}if(m?.output){let oe=m.output;ff(oe,ne,"utf-8"),n(`Template exported to: ${oe}`);let we=(mf(oe).size/1024).toFixed(2);e(`File size: ${we} KB`)}else e(`
460
+ Template:`),console.log(ne)}catch(A){r(`Failed to export template: ${A.message}`)}})}import{existsSync as gf}from"node:fs";function _c(N,t){N.command("upgrade","Upgrade CLI to latest version").action(async()=>{Vr("upgrade",{message:`Self-update is disabled for this ${t} build because no signed update-manifest source is configured.`,nextAction:"Install a verified release with your package manager or configure platform maintenance updates."})}),N.command("doctor","Check system requirements and AWS credentials").action(async()=>{if(i("System Diagnostics"),p("Checking Bun..."),n(`Bun ${process.versions.bun}`),p("Checking AWS CLI..."),await Es())n("AWS CLI is installed");else r("AWS CLI is not installed"),e("Install: https://aws.amazon.com/cli/");if(p("Checking AWS credentials..."),await ws()){n("AWS credentials are configured");let j=await Cs();if(j)e(`Account ID: ${j}`)}else r("AWS credentials are not configured"),e("Run: aws configure");p("Checking CloudFront list access...");let L=!1;try{let{CloudFrontClient:j}=await import("./chunk-72g1hr4z.js");await new j().listDistributions(),n("CloudFront list access is enabled"),L=!0}catch(j){a(`CloudFront list check failed: ${j.message}`)}if(L){p("Checking CloudFront create access...");try{let j=new v("us-east-1"),G=JSON.stringify({AWSTemplateFormatVersion:"2010-09-09",Description:"Test CloudFront access",Resources:{TestOAC:{Type:"AWS::CloudFront::OriginAccessControl",Properties:{OriginAccessControlConfig:{Name:"test-oac-validation",OriginAccessControlOriginType:"s3",SigningBehavior:"always",SigningProtocol:"sigv4"}}}}});await j.validateTemplate(G),n("CloudFront create access appears enabled")}catch(j){if(j.message?.includes("403")||j.message?.includes("AccessDenied")||j.message?.includes("must be verified"))r("CloudFront create access denied - account verification required"),e(""),e("Your AWS account needs to be verified for CloudFront."),e("This is required before you can create CloudFront distributions."),e(""),e("To verify your account:"),e(" 1. Go to: https://console.aws.amazon.com/support/home#/"),e(" 2. Create a support case"),e(" 3. Select: Service limit increase > CloudFront"),e(' 4. Request: "Please verify my account for CloudFront access"'),e(""),e("Verification usually takes 1-2 business days.");else n("CloudFront create access appears enabled (validation passed)")}}p("Checking S3 access...");try{let{S3Client:j}=await import("./chunk-a7kmjq4y.js");await new j("us-east-1").listBuckets(),n("S3 access is enabled")}catch(j){a(`S3 check failed: ${j.message}`)}p("Checking ACM (SSL certificates) access...");try{await new H("us-east-1").listCertificates(),n("ACM access is enabled")}catch(j){a(`ACM check failed: ${j.message}`)}if(p("Checking configuration..."),gf("cloud.config.ts"))n("cloud.config.ts found");else a("cloud.config.ts not found"),e("Run: cloud init")}),N.command("regions","List available AWS regions").action(async()=>{i("AWS Regions");let m=new o("Fetching regions...");m.start();let A=await As();m.stop(),A.forEach((L)=>{console.log(` ${L}`)})}),N.command("version","Show the version of the CLI").alias("v").action(()=>{console.log(`ts-cloud v${t}`)})}function zc(N){N.command("analytics:sites:list","List all analytics sites").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t)=>{i("Analytics Sites");try{let m=new z(t.region),A=new o("Fetching sites...");A.start();let L=await m.scan({TableName:t.table,FilterExpression:"begins_with(pk, :pk)",ExpressionAttributeValues:{":pk":{S:"SITE#"}}});if(A.succeed(`Found ${L.Items?.length||0} site(s)`),!L.Items||L.Items.length===0){e("No analytics sites found"),e("Use `cloud analytics:sites:create` to create a new site");return}let j=L.Items.map((G)=>z.unmarshal(G));c(["ID","Name","Domains","Active","Created"],j.map((G)=>[G.siteId||"N/A",G.name||"Unnamed",(G.domains||[]).join(", ")||"-",G.isActive?"Yes":"No",G.createdAt?new Date(G.createdAt).toLocaleDateString():"N/A"]))}catch(m){r(`Failed to list sites: ${m.message}`),process.exit(1)}}),N.command("analytics:sites:create","Create a new analytics site").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","Site name").option("--domain <domain>","Site domain(s) (can be specified multiple times)").action(async(t)=>{i("Create Analytics Site");try{let m=new z(t.region),A=t.name||await I("Site name","My Site"),L=[];if(t.domain)L=Array.isArray(t.domain)?t.domain:[t.domain];else L=(await I("Site domains (comma-separated)","example.com")).split(",").map((oe)=>oe.trim()).filter(Boolean);if(e(`
461
461
  Creating site: ${A}`),e(`Domains: ${L.join(", ")}`),!await d(`
462
- Create this site?`,!0)){e("Operation cancelled");return}let G=new o("Creating site...");G.start();let Z=`site_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,ee=new Date().toISOString();await p.putItem({TableName:t.table,Item:{pk:{S:`SITE#${Z}`},sk:{S:`SITE#${Z}`},siteId:{S:Z},name:{S:A},domains:{L:L.map((re)=>({S:re}))},isActive:{BOOL:!0},createdAt:{S:ee},updatedAt:{S:ee}}}),G.succeed("Site created successfully"),n(`
462
+ Create this site?`,!0)){e("Operation cancelled");return}let G=new o("Creating site...");G.start();let Z=`site_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,ee=new Date().toISOString();await m.putItem({TableName:t.table,Item:{pk:{S:`SITE#${Z}`},sk:{S:`SITE#${Z}`},siteId:{S:Z},name:{S:A},domains:{L:L.map((ne)=>({S:ne}))},isActive:{BOOL:!0},createdAt:{S:ee},updatedAt:{S:ee}}}),G.succeed("Site created successfully"),n(`
463
463
  Site ID: ${Z}`),e(`
464
- Add the tracking script to your website:`),e(` <script src="https://analytics.stacksjs.com/track.js" data-site-id="${Z}"></script>`)}catch(p){r(`Failed to create site: ${p.message}`),process.exit(1)}}),N.command("analytics:sites:update <siteId>","Update an analytics site").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","New site name").option("--domain <domain>","Set domains (replaces existing)").option("--active <boolean>","Set site active/inactive status").action(async(t,p)=>{i("Update Analytics Site");try{let A=new z(p.region),L=await A.getItem({TableName:p.table,Key:{pk:{S:`SITE#${t}`},sk:{S:`SITE#${t}`}}});if(!L.Item)r(`Site not found: ${t}`),process.exit(1);let j=z.unmarshal(L.Item);e(`Updating site: ${j.name||"Unnamed"} (${t})`),e("");let G=[],Z={},ee={};if(p.name)G.push("#n = :name"),Z["#n"]="name",ee[":name"]={S:p.name},e(` Name: ${j.name} -> ${p.name}`);if(p.domain!==void 0){let re=Array.isArray(p.domain)?p.domain:[p.domain];G.push("domains = :domains"),ee[":domains"]={L:re.map((oe)=>({S:oe}))},e(` Domains: ${JSON.stringify(j.domains||[])} -> ${JSON.stringify(re)}`)}if(p.active!==void 0){let re=p.active==="true"||p.active==="1";G.push("isActive = :active"),ee[":active"]={BOOL:re},e(` Active: ${j.isActive} -> ${re}`)}if(G.length===0){a("No updates specified. Use --name, --domain, or --active options.");return}G.push("updatedAt = :updatedAt"),ee[":updatedAt"]={S:new Date().toISOString()},await A.updateItem({TableName:p.table,Key:{pk:{S:`SITE#${t}`},sk:{S:`SITE#${t}`}},UpdateExpression:`SET ${G.join(", ")}`,ExpressionAttributeNames:Object.keys(Z).length>0?Z:void 0,ExpressionAttributeValues:ee}),e(""),n("Site updated successfully")}catch(A){r(`Failed to update site: ${A.message}`),process.exit(1)}}),N.command("analytics:query <siteId>","Query analytics data for a site").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--limit <n>","Max items to return",{default:"10"}).option("--type <type>","Filter by type (pageview, event, etc.)").action(async(t,p)=>{i(`Analytics Data: ${t}`);try{let A=new z(p.region),L=new o("Querying data...");L.start();let j=await A.query({TableName:p.table,KeyConditionExpression:"pk = :pk",ExpressionAttributeValues:{":pk":{S:`SITE#${t}`}},ScanIndexForward:!1,Limit:parseInt(p.limit,10)});if(L.succeed(`Found ${j.Items?.length||0} items`),!j.Items||j.Items.length===0){e("No data found for this site");return}let G=j.Items.map((Z)=>z.unmarshal(Z));for(let Z of G){if(e(""),e(`SK: ${Z.sk}`),Z.path)e(` Path: ${Z.path}`);if(Z.timestamp)e(` Time: ${Z.timestamp}`);if(Z.visitorId)e(` Visitor: ${Z.visitorId}`);if(Z.browser)e(` Browser: ${Z.browser}`);if(Z.country)e(` Country: ${Z.country}`)}}catch(A){r(`Failed to query data: ${A.message}`),process.exit(1)}}),N.command("analytics:realtime <siteId>","Check realtime visitors for a site").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--minutes <n>","Minutes to look back",{default:"5"}).action(async(t,p)=>{i(`Realtime: ${t}`);try{let A=new z(p.region),L=new o("Checking realtime...");L.start();let j=parseInt(p.minutes,10),G=new Date(Date.now()-j*60*1000),Z=await A.query({TableName:p.table,KeyConditionExpression:"pk = :pk AND sk BETWEEN :start AND :end",ExpressionAttributeValues:{":pk":{S:`SITE#${t}`},":start":{S:`PAGEVIEW#${G.toISOString()}`},":end":{S:"PAGEVIEW#Z"}},ScanIndexForward:!1});if(L.succeed(`Found ${Z.Items?.length||0} pageviews in last ${j} minutes`),!Z.Items||Z.Items.length===0){a("No recent pageviews found"),e(""),e("Possible issues:"),e(" 1. Tracking script not installed correctly"),e(" 2. Site ID mismatch"),e(" 3. CORS or network issues");return}let ee=Z.Items.map((be)=>z.unmarshal(be)),re=new Set(ee.map((be)=>be.visitorId)).size;e(""),n(`${re} unique visitor(s) online`),e("");let oe={};for(let be of ee){let we=be.path||"/";oe[we]=(oe[we]||0)+1}e("Active pages:");for(let[be,we]of Object.entries(oe).sort((Ce,Ee)=>Ee[1]-Ce[1]).slice(0,5))e(` ${be}: ${we} view(s)`);e(""),e("Recent pageviews:");for(let be of ee.slice(0,5))e(` ${be.timestamp} - ${be.path} (${be.browser||"Unknown"})`)}catch(A){r(`Failed to check realtime: ${A.message}`),process.exit(1)}})}import{chmodSync as of,mkdirSync as sf,readFileSync as af,writeFileSync as cf}from"node:fs";import{dirname as lf,resolve as Zs}from"node:path";function nn(N){return new Er(N?{path:Zs(N)}:{})}function df(N){let t=N.health();i("Control-plane diagnostics"),e(`Database: ${t.path}`),e(`Schema: ${t.schemaVersion}/${t.supportedSchemaVersion}`),e(`Integrity: ${t.integrity}`),e(`Journal: ${t.journalMode}`),e(`Size: ${t.databaseBytes.toLocaleString()} bytes`),e(`Last backup: ${t.lastBackupAt??"never"}`),e(`Operations: ${Object.entries(t.operations).map(([p,A])=>`${p}=${A}`).join(", ")}`),e(`Pending/retryable: ${t.pendingRetryableOperations}`)}function Mi(N,t){let p=N.getOrganization(t)??N.getOrganizationBySlug(t);if(!p)throw Error(`Organization '${t}' was not found.`);return p}function Hs(N,t){let p=N??"organization";if(p==="organization")return{type:"organization"};if(!["project","environment","resource"].includes(p)||!t)throw Error("Scoped access requires --scope project|environment|resource and --scope-id <id>.");return{type:p,id:t}}function Js(N,t){let p=t.includes("@")?N.getIdentityByEmail(t):N.getIdentityByUsername(t);if(!p)throw Error(`Authentication identity '${t}' was not found.`);return p}function Ys(N,t){return new ke(N,{encryptionKey:k(Zs(t??process.cwd()))})}function wd(N,t="TS_CLOUD_API_TOKEN"){let p=process.env[t]?.trim();if(!p)throw Error(`Set ${t} to a one-time-issued API token.`);return new Ci({baseUrl:N??process.env.TS_CLOUD_API_URL??"http://127.0.0.1:7676",token:p})}function Tc(N){N.command("dashboard:serve","Run the local Forge-style cloud management UI").option("--host <host>","Host to bind",{default:"127.0.0.1"}).option("--port <port>","Port to bind",{default:"7676"}).option("--env <environment>","Environment to manage").option("--box","Box mode: run on the provisioned server (operate on localhost)").option("--open","Print the URL for opening in a browser").option("--verbose","Print server errors").action(async(t)=>{let p=await er({host:t?.host,port:Number(t?.port??7676),environment:t?.env,box:t?.box,verbose:t?.verbose});i("ts-cloud Local Dashboard"),n(`Serving ${p.url}`),e("Use Ctrl+C to stop."),await new Promise((A)=>{let L=()=>{p.server.stop(!0),A()};process.once("SIGINT",L),process.once("SIGTERM",L)})}),N.command("control-plane:status","Inspect local control-plane storage health").option("--path <path>","Use a non-default control-plane database").action((t)=>{let p=nn(t?.path);try{df(p)}finally{p.close()}}),N.command("control-plane:backup","Create a consistent local control-plane backup").option("--path <path>","Use a non-default control-plane database").action((t)=>{let p=nn(t?.path);try{let A=p.createBackup("cli");n(`Backup written to ${A}`)}finally{p.close()}}),N.command("control-plane:export <file>","Export portable control-plane metadata and history").option("--path <path>","Use a non-default control-plane database").action((t,p)=>{let A=Zs(t),L=nn(p?.path);try{sf(lf(A),{recursive:!0}),cf(A,`${JSON.stringify(L.exportSnapshot(),null,2)}
465
- `,{mode:384}),of(A,384),n(`Control-plane snapshot exported to ${A}`)}finally{L.close()}}),N.command("control-plane:import <file>","Import a portable control-plane snapshot").option("--path <path>","Use a non-default control-plane database").option("--replace","Replace existing control-plane records").action((t,p)=>{let A=Zs(t),L=JSON.parse(af(A,"utf8")),j=nn(p?.path);try{let G=p?.replace?j.createBackup("pre-import"):void 0;if(j.importSnapshot(L,{replace:p?.replace}),n(`Imported control-plane snapshot from ${A}`),G)e(`Previous state backed up to ${G}`)}finally{j.close()}}),N.command("control-plane:compact","Apply history retention and compact control-plane storage").option("--path <path>","Use a non-default control-plane database").option("--event-days <days>","Retain event history for this many days",{default:"90"}).option("--operation-days <days>","Retain terminal operations for this many days",{default:"365"}).option("--no-vacuum","Delete expired records without reclaiming file space").action((t)=>{let p=nn(t?.path);try{let A=p.compact({eventRetentionDays:Number(t?.eventDays??90),operationRetentionDays:Number(t?.operationDays??365),vacuum:t?.vacuum!==!1});if(n(`Removed ${A.deletedEvents} event(s) and ${A.deletedOperations} terminal operation(s).`),A.vacuumed)e("Database file compacted.")}finally{p.close()}}),N.command("organization:list","List organizations in the local control plane").option("--path <path>","Use a non-default control-plane database").action((t)=>{let p=nn(t?.path);try{i("Organizations");for(let A of p.listOrganizations())e(`${A.slug} ${A.name} ${A.id}`)}finally{p.close()}}),N.command("organization:members <organization>","List memberships and scoped grants").option("--path <path>","Use a non-default control-plane database").option("--all","Include revoked memberships").action((t,p)=>{let A=nn(p?.path);try{let L=Mi(A,t);i(`${L.name} members`);for(let j of A.listMemberships(L.id,{includeRevoked:p?.all})){let G=A.getActor(j.actorId);e(`${G?.displayName??j.actorId} ${j.roleTemplate} ${j.scope.type}:${j.scope.id??L.slug} ${j.status} ${j.id}`);for(let Z of A.listGrants(j.id))e(` ${Z.effect} ${Z.capability} @ ${Z.scope.type}:${Z.scope.id??L.slug}`)}}finally{A.close()}}),N.command("organization:invite <organization> <email>","Create a hashed, expiring organization invitation").option("--path <path>","Use a non-default control-plane database").option("--role <role>","owner|admin|deployer|operator|viewer|auditor",{default:"viewer"}).option("--scope <scope>","organization|project|environment|resource",{default:"organization"}).option("--scope-id <id>","Project, environment, or resource ID").option("--days <days>","Invitation lifetime in days",{default:"7"}).option("--base-url <url>","Dashboard URL used to print the acceptance link",{default:"http://127.0.0.1:7676"}).action((t,p,A)=>{let L=nn(A?.path);try{let j=Mi(L,t);if(!["owner","admin","deployer","operator","viewer","auditor"].includes(A?.role??"viewer"))throw Error("Unknown role template.");let G=L.createInvitation({organizationId:j.id,email:p,roleTemplate:A?.role??"viewer",scope:Hs(A?.scope,A?.scopeId),expiresInMs:Number(A?.days??7)*86400000});n(`Invitation created for ${G.invitation.email}; expires ${G.invitation.expiresAt}`),e(`${String(A?.baseUrl??"http://127.0.0.1:7676").replace(/\/$/,"")}/accept-invitation?token=${encodeURIComponent(G.token)}`),e("This token is shown once. Resending revokes it and creates a new token.")}finally{L.close()}}),N.command("organization:invitations <organization>","List invitation states without exposing tokens").option("--path <path>","Use a non-default control-plane database").action((t,p)=>{let A=nn(p?.path);try{let L=Mi(A,t);i(`${L.name} invitations`);for(let j of A.listInvitations(L.id))e(`${j.email} ${j.roleTemplate} ${j.scope.type}:${j.scope.id??L.slug} ${j.state} ${j.id}`)}finally{A.close()}}),N.command("organization:revoke-invitation <id>","Revoke a pending organization invitation").option("--path <path>","Use a non-default control-plane database").action((t,p)=>{let A=nn(p?.path);try{let L=A.revokeInvitation(t);n(`Invitation ${L.id} is ${L.state}.`)}finally{A.close()}}),N.command("organization:grant <organization> <membership> <capability>","Add an explicit scoped allow or deny").option("--path <path>","Use a non-default control-plane database").option("--effect <effect>","allow|deny",{default:"allow"}).option("--scope <scope>","organization|project|environment|resource",{default:"organization"}).option("--scope-id <id>","Project, environment, or resource ID").action((t,p,A,L)=>{let j=nn(L?.path);try{let G=Mi(j,t);if(!ie.includes(A))throw Error(`Unknown capability '${A}'.`);if(L?.effect!==void 0&&!["allow","deny"].includes(L.effect))throw Error("Effect must be allow or deny.");let Z=j.upsertGrant({organizationId:G.id,membershipId:p,effect:L?.effect??"allow",capability:A,scope:Hs(L?.scope,L?.scopeId)});n(`${Z.effect} ${Z.capability} added at ${Z.scope.type}:${Z.scope.id??G.slug}.`)}finally{j.close()}}),N.command("organization:revoke-member <membership>","Revoke a membership and invalidate its sessions").option("--path <path>","Use a non-default control-plane database").option("--confirm <id>","Type the membership ID to confirm").action((t,p)=>{if(p?.confirm!==t)throw Error(`Pass --confirm ${t} to revoke this membership.`);let A=nn(p?.path);try{let L=A.revokeMembership(t);n(`Membership ${L.id} revoked; session version is now ${L.sessionVersion}.`)}finally{A.close()}}),N.command("auth:identities","List local authentication identities without credential material").option("--path <path>","Use a non-default control-plane database").action((t)=>{let p=nn(t?.path);try{let A=new ke(p);i("Authentication identities");for(let L of A.listIdentities())e(`${L.username} ${L.email??"no-email"} ${L.disabledAt?"disabled":"active"} ${L.id}`)}finally{p.close()}}),N.command("auth:reset-link <identity>","Create a one-time password reset link for offline recovery").option("--path <path>","Use a non-default control-plane database").option("--base-url <url>","Dashboard URL used to print the reset link",{default:"http://127.0.0.1:7676"}).action((t,p)=>{let A=nn(p?.path);try{let L=new ke(A),j=Js(L,t);L.revokeActionTokens(j.id,"password_reset");let G=L.createActionToken(j.id,"password_reset");n(`Created a one-time reset link for ${j.username}; it expires ${G.actionToken.expiresAt}.`),e(`${String(p?.baseUrl??"http://127.0.0.1:7676").replace(/\/$/,"")}/reset-password?token=${encodeURIComponent(G.token)}`)}finally{A.close()}}),N.command("auth:sessions <identity>","List active and expired sessions for an identity").option("--path <path>","Use a non-default control-plane database").action((t,p)=>{let A=nn(p?.path);try{let L=new ke(A),j=Js(L,t);i(`${j.username} sessions`);for(let G of L.listSessions(j.id,{includeInactive:!0}))e(`${G.state} ${G.authMethod} ${G.userAgent??"unknown-device"} ${G.lastUsedAt} ${G.id}`)}finally{A.close()}}),N.command("auth:revoke-sessions <identity>","Revoke every dashboard session for an identity").option("--path <path>","Use a non-default control-plane database").option("--confirm <identity>","Type the username or email to confirm").action((t,p)=>{if(p?.confirm!==t)throw Error(`Pass --confirm ${t} to revoke these sessions.`);let A=nn(p?.path);try{let L=new ke(A),j=Js(L,t),G=L.listSessions(j.id);for(let Z of G)L.revokeSession(j.id,Z.id);n(`Revoked ${G.length} session(s) for ${j.username}.`)}finally{A.close()}}),N.command("auth:disable-mfa <identity>","Disable MFA through the offline administrative recovery path").option("--path <path>","Use a non-default control-plane database").option("--confirm <identity>","Type the username or email to confirm").action((t,p)=>{if(p?.confirm!==t)throw Error(`Pass --confirm ${t} to disable MFA.`);let A=nn(p?.path);try{let L=new ke(A),j=Js(L,t);L.disableMfa(j.id);for(let G of L.listSessions(j.id))L.revokeSession(j.id,G.id);n(`Disabled MFA and revoked active sessions for ${j.username}.`)}finally{A.close()}}),N.command("auth:oidc:list <organization>","List OIDC providers without credential material").option("--path <path>","Use a non-default control-plane database").action((t,p)=>{let A=nn(p?.path);try{let L=Mi(A,t),j=Ys(A);i(`${L.name} OIDC providers`);let G=j.listOidcProviders(L.id,{includeDisabled:!0});if(G.length===0)e("No providers configured.");for(let Z of G)e(`${Z.enabled?"enabled":"disabled"} ${Z.slug} ${Z.issuer} role=${Z.defaultRole} enforce=${Z.enforceSso}`)}finally{A.close()}}),N.command("auth:oidc:configure <organization> <slug>","Create or update an encrypted OIDC provider configuration").option("--path <path>","Use a non-default control-plane database").option("--auth-root <path>","Directory containing .ts-cloud/auth-encryption-key").option("--name <name>","Provider label shown on the sign-in page").option("--issuer <url>","Exact OIDC issuer URL").option("--client-id <id>","OIDC client ID").option("--secret-env <name>","Environment variable containing the client secret",{default:"TS_CLOUD_OIDC_CLIENT_SECRET"}).option("--domains <domains>","Comma-separated verified email domains").option("--scopes <scopes>","Comma-separated scopes (openid is always included)",{default:"email,profile"}).option("--default-role <role>","Role for provisioned users: admin, deployer, operator, viewer, or auditor",{default:"viewer"}).option("--enforce-sso","Require SSO for matching non-owner local accounts").option("--disabled","Save the provider disabled").action((t,p,A)=>{let L=nn(A?.path);try{let j=Mi(L,t),G=Ys(L,A?.authRoot),Z=G.getOidcProviderBySlug(p),ee=A?.secretEnv??"TS_CLOUD_OIDC_CLIENT_SECRET",re=process.env[ee]?.trim();if(!Z&&!re)throw Error(`Set ${ee} to the client secret before creating this provider.`);let oe=(A?.domains??Z?.allowedDomains.join(",")??"").split(",").map((Ce)=>Ce.trim()).filter(Boolean),be=A?.defaultRole??Z?.defaultRole??"viewer";if(!["admin","deployer","operator","viewer","auditor"].includes(be))throw Error("Default role must be admin, deployer, operator, viewer, or auditor.");let we=G.upsertOidcProvider({id:Z?.id,organizationId:j.id,slug:p,name:A?.name??Z?.name??p,issuer:A?.issuer??Z?.issuer??"",clientId:A?.clientId??Z?.clientId??"",clientSecret:re,allowedDomains:oe,scopes:(A?.scopes??Z?.scopes.join(",")??"email,profile").split(",").map((Ce)=>Ce.trim()).filter(Boolean),defaultRole:be,enforceSso:A?.enforceSso??Z?.enforceSso??!1,enabled:A?.disabled?!1:Z?.enabled??!0});if(n(`${we.name} saved as ${we.enabled?"enabled":"disabled"} with encrypted credentials.`),e(`Callback path: /auth/oidc/${we.slug}/callback`),we.enforceSso)e(`Offline recovery: cloud auth:oidc:disable ${we.slug} --confirm ${we.slug}`)}finally{L.close()}}),N.command("auth:oidc:test <slug>","Validate OIDC discovery for a configured provider").option("--path <path>","Use a non-default control-plane database").option("--auth-root <path>","Directory containing .ts-cloud/auth-encryption-key").action(async(t,p)=>{let A=nn(p?.path);try{let j=Ys(A,p?.authRoot).getOidcProviderBySlug(t);if(!j)throw Error(`OIDC provider '${t}' was not found.`);let G=await ln(j);n(`${j.name} discovery is valid and issuer-bound.`),e(`Authorization: ${G.authorization_endpoint}`),e(`Token: ${G.token_endpoint}`),e(`JWKS: ${G.jwks_uri}`)}finally{A.close()}}),N.command("auth:oidc:disable <slug>","Disable an OIDC provider through the offline recovery path").option("--path <path>","Use a non-default control-plane database").option("--auth-root <path>","Directory containing .ts-cloud/auth-encryption-key").option("--confirm <slug>","Type the provider slug to confirm").action((t,p)=>{if(p?.confirm!==t)throw Error(`Pass --confirm ${t} to disable this provider.`);let A=nn(p?.path);try{let L=Ys(A,p?.authRoot),j=L.getOidcProviderBySlug(t);if(!j)throw Error(`OIDC provider '${t}' was not found.`);L.setOidcProviderEnabled(j.id,!1),n(`Disabled ${j.name}, cancelled pending sign-ins, and removed SSO enforcement.`)}finally{A.close()}}),N.command("api:service-accounts <organization>","List service accounts and safe token metadata").option("--path <path>","Use a non-default control-plane database").action((t,p)=>{let A=nn(p?.path);try{let L=Mi(A,t),j=new Be(A);i(`${L.name} service accounts`);for(let G of j.listServiceAccounts(L.id,{includeDisabled:!0})){e(`${G.state} ${G.slug} ${G.name} ${G.id}`);for(let Z of j.listTokens(G.id,{includeInactive:!0}))e(` ${Z.state} ${Z.prefix} ${Z.name} expires=${Z.expiresAt} last-used=${Z.lastUsedAt??"never"}`)}}finally{A.close()}}),N.command("api:service-account:create <organization> <slug>","Create a scoped service account").option("--path <path>","Use a non-default control-plane database").option("--name <name>","Display name").option("--role <role>","admin, deployer, operator, viewer, or auditor",{default:"deployer"}).option("--scope <scope>","organization, project, environment, or resource",{default:"organization"}).option("--scope-id <id>","Required for non-organization scope").action((t,p,A)=>{let L=nn(A?.path);try{let j=Mi(L,t),G=A?.role??"deployer";if(!["admin","deployer","operator","viewer","auditor"].includes(G))throw Error("Service-account role cannot be owner and must be a supported role.");let ee=new Be(L).createServiceAccount({organizationId:j.id,slug:p,name:A?.name??p,roleTemplate:G,scope:Hs(A?.scope,A?.scopeId)});n(`Created ${ee.serviceAccount.name} (${ee.serviceAccount.id}) with ${ee.membership.roleTemplate} access.`)}finally{L.close()}}),N.command("api:token:create <organization> <account>","Create and reveal a scoped API token exactly once").option("--path <path>","Use a non-default control-plane database").option("--name <name>","Token label",{default:"CLI token"}).option("--capabilities <list>","Comma-separated capabilities").option("--scope <scope>","organization, project, environment, or resource",{default:"organization"}).option("--scope-id <id>","Required for non-organization scope").option("--expires-days <days>","Expiry in days, maximum 365",{default:"90"}).action((t,p,A)=>{let L=nn(A?.path);try{let j=Mi(L,t),G=new Be(L),Z=G.getServiceAccount(p)??G.getServiceAccountBySlug(j.id,p);if(!Z||Z.organizationId!==j.id)throw Error(`Service account '${p}' was not found.`);let ee=(A?.capabilities??"").split(",").map((be)=>be.trim()).filter((be)=>ie.includes(be));if(!ee.length)throw Error(`Pass --capabilities with one or more of: ${ie.join(", ")}`);let re=Math.min(365,Math.max(1,Number(A?.expiresDays??90))),oe=G.createToken({serviceAccountId:Z.id,name:A?.name??"CLI token",capabilities:ee,scope:Hs(A?.scope,A?.scopeId),expiresAt:new Date(Date.now()+re*24*60*60*1000).toISOString()});n(`Created ${oe.token.prefix}; copy this secret now. It will not be shown again.`),e(oe.secret)}finally{L.close()}}),N.command("api:token:rotate <token>","Create an overlapping replacement for an API token").option("--path <path>","Use a non-default control-plane database").action((t,p)=>{let A=nn(p?.path);try{let L=new Be(A).rotateToken(t);n(`Created overlapping replacement ${L.token.prefix}; revoke the old token after rollout.`),e(L.secret)}finally{A.close()}}),N.command("api:token:revoke <token>","Immediately revoke an API token").option("--path <path>","Use a non-default control-plane database").option("--confirm <token>","Type the token ID to confirm").action((t,p)=>{if(p?.confirm!==t)throw Error(`Pass --confirm ${t} to revoke this token.`);let A=nn(p?.path);try{let L=new Be(A).revokeToken(t);n(`Revoked ${L.prefix}; bearer requests stop immediately.`)}finally{A.close()}}),N.command("api:projects","List projects through the public v1 API").option("--base-url <url>","Dashboard base URL").option("--token-env <name>","Environment variable containing the API token",{default:"TS_CLOUD_API_TOKEN"}).action(async(t)=>{let p=await wd(t?.baseUrl,t?.tokenEnv).listProjects();for(let A of p.data)e(`${A.slug} ${A.name} ${A.id}`)}),N.command("api:deploy <project> <environment>","Queue a deployment through the public v1 API").option("--base-url <url>","Dashboard base URL").option("--token-env <name>","Environment variable containing the API token",{default:"TS_CLOUD_API_TOKEN"}).option("--service <id>","Optional service resource ID").option("--revision <revision>","Revision or release identifier").option("--idempotency-key <key>","Stable retry key").action(async(t,p,A)=>{let L=A?.idempotencyKey??`cli-${Date.now()}-${crypto.randomUUID().slice(0,8)}`,j=await wd(A?.baseUrl,A?.tokenEnv).createDeployment({projectId:t,environmentId:p,serviceId:A?.service,revision:A?.revision},L);n(`${j.idempotentReplay?"Replayed":"Queued"} operation ${j.operation.id} (${j.operation.state}).`)})}import{createHash as uf}from"node:crypto";var mf=`let coldStart = true
464
+ Add the tracking script to your website:`),e(` <script src="https://analytics.stacksjs.com/track.js" data-site-id="${Z}"></script>`)}catch(m){r(`Failed to create site: ${m.message}`),process.exit(1)}}),N.command("analytics:sites:update <siteId>","Update an analytics site").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","New site name").option("--domain <domain>","Set domains (replaces existing)").option("--active <boolean>","Set site active/inactive status").action(async(t,m)=>{i("Update Analytics Site");try{let A=new z(m.region),L=await A.getItem({TableName:m.table,Key:{pk:{S:`SITE#${t}`},sk:{S:`SITE#${t}`}}});if(!L.Item)r(`Site not found: ${t}`),process.exit(1);let j=z.unmarshal(L.Item);e(`Updating site: ${j.name||"Unnamed"} (${t})`),e("");let G=[],Z={},ee={};if(m.name)G.push("#n = :name"),Z["#n"]="name",ee[":name"]={S:m.name},e(` Name: ${j.name} -> ${m.name}`);if(m.domain!==void 0){let ne=Array.isArray(m.domain)?m.domain:[m.domain];G.push("domains = :domains"),ee[":domains"]={L:ne.map((oe)=>({S:oe}))},e(` Domains: ${JSON.stringify(j.domains||[])} -> ${JSON.stringify(ne)}`)}if(m.active!==void 0){let ne=m.active==="true"||m.active==="1";G.push("isActive = :active"),ee[":active"]={BOOL:ne},e(` Active: ${j.isActive} -> ${ne}`)}if(G.length===0){a("No updates specified. Use --name, --domain, or --active options.");return}G.push("updatedAt = :updatedAt"),ee[":updatedAt"]={S:new Date().toISOString()},await A.updateItem({TableName:m.table,Key:{pk:{S:`SITE#${t}`},sk:{S:`SITE#${t}`}},UpdateExpression:`SET ${G.join(", ")}`,ExpressionAttributeNames:Object.keys(Z).length>0?Z:void 0,ExpressionAttributeValues:ee}),e(""),n("Site updated successfully")}catch(A){r(`Failed to update site: ${A.message}`),process.exit(1)}}),N.command("analytics:query <siteId>","Query analytics data for a site").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--limit <n>","Max items to return",{default:"10"}).option("--type <type>","Filter by type (pageview, event, etc.)").action(async(t,m)=>{i(`Analytics Data: ${t}`);try{let A=new z(m.region),L=new o("Querying data...");L.start();let j=await A.query({TableName:m.table,KeyConditionExpression:"pk = :pk",ExpressionAttributeValues:{":pk":{S:`SITE#${t}`}},ScanIndexForward:!1,Limit:parseInt(m.limit,10)});if(L.succeed(`Found ${j.Items?.length||0} items`),!j.Items||j.Items.length===0){e("No data found for this site");return}let G=j.Items.map((Z)=>z.unmarshal(Z));for(let Z of G){if(e(""),e(`SK: ${Z.sk}`),Z.path)e(` Path: ${Z.path}`);if(Z.timestamp)e(` Time: ${Z.timestamp}`);if(Z.visitorId)e(` Visitor: ${Z.visitorId}`);if(Z.browser)e(` Browser: ${Z.browser}`);if(Z.country)e(` Country: ${Z.country}`)}}catch(A){r(`Failed to query data: ${A.message}`),process.exit(1)}}),N.command("analytics:realtime <siteId>","Check realtime visitors for a site").option("--table <name>","DynamoDB table name",{default:"ts-analytics"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--minutes <n>","Minutes to look back",{default:"5"}).action(async(t,m)=>{i(`Realtime: ${t}`);try{let A=new z(m.region),L=new o("Checking realtime...");L.start();let j=parseInt(m.minutes,10),G=new Date(Date.now()-j*60*1000),Z=await A.query({TableName:m.table,KeyConditionExpression:"pk = :pk AND sk BETWEEN :start AND :end",ExpressionAttributeValues:{":pk":{S:`SITE#${t}`},":start":{S:`PAGEVIEW#${G.toISOString()}`},":end":{S:"PAGEVIEW#Z"}},ScanIndexForward:!1});if(L.succeed(`Found ${Z.Items?.length||0} pageviews in last ${j} minutes`),!Z.Items||Z.Items.length===0){a("No recent pageviews found"),e(""),e("Possible issues:"),e(" 1. Tracking script not installed correctly"),e(" 2. Site ID mismatch"),e(" 3. CORS or network issues");return}let ee=Z.Items.map((be)=>z.unmarshal(be)),ne=new Set(ee.map((be)=>be.visitorId)).size;e(""),n(`${ne} unique visitor(s) online`),e("");let oe={};for(let be of ee){let we=be.path||"/";oe[we]=(oe[we]||0)+1}e("Active pages:");for(let[be,we]of Object.entries(oe).sort((Ce,De)=>De[1]-Ce[1]).slice(0,5))e(` ${be}: ${we} view(s)`);e(""),e("Recent pageviews:");for(let be of ee.slice(0,5))e(` ${be.timestamp} - ${be.path} (${be.browser||"Unknown"})`)}catch(A){r(`Failed to check realtime: ${A.message}`),process.exit(1)}})}import{chmodSync as hf,mkdirSync as yf,readFileSync as vf,writeFileSync as bf}from"node:fs";import{dirname as wf,resolve as sa}from"node:path";function sn(N){return new wr(N?{path:sa(N)}:{})}function Sf(N){let t=N.health();i("Control-plane diagnostics"),e(`Database: ${t.path}`),e(`Schema: ${t.schemaVersion}/${t.supportedSchemaVersion}`),e(`Integrity: ${t.integrity}`),e(`Journal: ${t.journalMode}`),e(`Size: ${t.databaseBytes.toLocaleString()} bytes`),e(`Last backup: ${t.lastBackupAt??"never"}`),e(`Operations: ${Object.entries(t.operations).map(([m,A])=>`${m}=${A}`).join(", ")}`),e(`Pending/retryable: ${t.pendingRetryableOperations}`)}function qi(N,t){let m=N.getOrganization(t)??N.getOrganizationBySlug(t);if(!m)throw Error(`Organization '${t}' was not found.`);return m}function Qs(N,t){let m=N??"organization";if(m==="organization")return{type:"organization"};if(!["project","environment","resource"].includes(m)||!t)throw Error("Scoped access requires --scope project|environment|resource and --scope-id <id>.");return{type:m,id:t}}function Xs(N,t){let m=t.includes("@")?N.getIdentityByEmail(t):N.getIdentityByUsername(t);if(!m)throw Error(`Authentication identity '${t}' was not found.`);return m}function ra(N,t){return new ke(N,{encryptionKey:k(sa(t??process.cwd()))})}function Dd(N,t="TS_CLOUD_API_TOKEN"){let m=process.env[t]?.trim();if(!m)throw Error(`Set ${t} to a one-time-issued API token.`);return new Ri({baseUrl:N??process.env.TS_CLOUD_API_URL??"http://127.0.0.1:7676",token:m})}function Uc(N){N.command("dashboard:serve","Run the local Forge-style cloud management UI").option("--host <host>","Host to bind",{default:"127.0.0.1"}).option("--port <port>","Port to bind",{default:"7676"}).option("--env <environment>","Environment to manage").option("--box","Box mode: run on the provisioned server (operate on localhost)").option("--open","Print the URL for opening in a browser").option("--verbose","Print server errors").action(async(t)=>{let m=await tr({host:t?.host,port:Number(t?.port??7676),environment:t?.env,box:t?.box,verbose:t?.verbose});i("ts-cloud Local Dashboard"),n(`Serving ${m.url}`),e("Use Ctrl+C to stop."),await new Promise((A)=>{let L=()=>{m.server.stop(!0),A()};process.once("SIGINT",L),process.once("SIGTERM",L)})}),N.command("control-plane:status","Inspect local control-plane storage health").option("--path <path>","Use a non-default control-plane database").action((t)=>{let m=sn(t?.path);try{Sf(m)}finally{m.close()}}),N.command("control-plane:backup","Create a consistent local control-plane backup").option("--path <path>","Use a non-default control-plane database").action((t)=>{let m=sn(t?.path);try{let A=m.createBackup("cli");n(`Backup written to ${A}`)}finally{m.close()}}),N.command("control-plane:export <file>","Export portable control-plane metadata and history").option("--path <path>","Use a non-default control-plane database").action((t,m)=>{let A=sa(t),L=sn(m?.path);try{yf(wf(A),{recursive:!0}),bf(A,`${JSON.stringify(L.exportSnapshot(),null,2)}
465
+ `,{mode:384}),hf(A,384),n(`Control-plane snapshot exported to ${A}`)}finally{L.close()}}),N.command("control-plane:import <file>","Import a portable control-plane snapshot").option("--path <path>","Use a non-default control-plane database").option("--replace","Replace existing control-plane records").action((t,m)=>{let A=sa(t),L=JSON.parse(vf(A,"utf8")),j=sn(m?.path);try{let G=m?.replace?j.createBackup("pre-import"):void 0;if(j.importSnapshot(L,{replace:m?.replace}),n(`Imported control-plane snapshot from ${A}`),G)e(`Previous state backed up to ${G}`)}finally{j.close()}}),N.command("control-plane:compact","Apply history retention and compact control-plane storage").option("--path <path>","Use a non-default control-plane database").option("--event-days <days>","Retain event history for this many days",{default:"90"}).option("--operation-days <days>","Retain terminal operations for this many days",{default:"365"}).option("--no-vacuum","Delete expired records without reclaiming file space").action((t)=>{let m=sn(t?.path);try{let A=m.compact({eventRetentionDays:Number(t?.eventDays??90),operationRetentionDays:Number(t?.operationDays??365),vacuum:t?.vacuum!==!1});if(n(`Removed ${A.deletedEvents} event(s) and ${A.deletedOperations} terminal operation(s).`),A.vacuumed)e("Database file compacted.")}finally{m.close()}}),N.command("organization:list","List organizations in the local control plane").option("--path <path>","Use a non-default control-plane database").action((t)=>{let m=sn(t?.path);try{i("Organizations");for(let A of m.listOrganizations())e(`${A.slug} ${A.name} ${A.id}`)}finally{m.close()}}),N.command("organization:members <organization>","List memberships and scoped grants").option("--path <path>","Use a non-default control-plane database").option("--all","Include revoked memberships").action((t,m)=>{let A=sn(m?.path);try{let L=qi(A,t);i(`${L.name} members`);for(let j of A.listMemberships(L.id,{includeRevoked:m?.all})){let G=A.getActor(j.actorId);e(`${G?.displayName??j.actorId} ${j.roleTemplate} ${j.scope.type}:${j.scope.id??L.slug} ${j.status} ${j.id}`);for(let Z of A.listGrants(j.id))e(` ${Z.effect} ${Z.capability} @ ${Z.scope.type}:${Z.scope.id??L.slug}`)}}finally{A.close()}}),N.command("organization:invite <organization> <email>","Create a hashed, expiring organization invitation").option("--path <path>","Use a non-default control-plane database").option("--role <role>","owner|admin|deployer|operator|viewer|auditor",{default:"viewer"}).option("--scope <scope>","organization|project|environment|resource",{default:"organization"}).option("--scope-id <id>","Project, environment, or resource ID").option("--days <days>","Invitation lifetime in days",{default:"7"}).option("--base-url <url>","Dashboard URL used to print the acceptance link",{default:"http://127.0.0.1:7676"}).action((t,m,A)=>{let L=sn(A?.path);try{let j=qi(L,t);if(!["owner","admin","deployer","operator","viewer","auditor"].includes(A?.role??"viewer"))throw Error("Unknown role template.");let G=L.createInvitation({organizationId:j.id,email:m,roleTemplate:A?.role??"viewer",scope:Qs(A?.scope,A?.scopeId),expiresInMs:Number(A?.days??7)*86400000});n(`Invitation created for ${G.invitation.email}; expires ${G.invitation.expiresAt}`),e(`${String(A?.baseUrl??"http://127.0.0.1:7676").replace(/\/$/,"")}/accept-invitation?token=${encodeURIComponent(G.token)}`),e("This token is shown once. Resending revokes it and creates a new token.")}finally{L.close()}}),N.command("organization:invitations <organization>","List invitation states without exposing tokens").option("--path <path>","Use a non-default control-plane database").action((t,m)=>{let A=sn(m?.path);try{let L=qi(A,t);i(`${L.name} invitations`);for(let j of A.listInvitations(L.id))e(`${j.email} ${j.roleTemplate} ${j.scope.type}:${j.scope.id??L.slug} ${j.state} ${j.id}`)}finally{A.close()}}),N.command("organization:revoke-invitation <id>","Revoke a pending organization invitation").option("--path <path>","Use a non-default control-plane database").action((t,m)=>{let A=sn(m?.path);try{let L=A.revokeInvitation(t);n(`Invitation ${L.id} is ${L.state}.`)}finally{A.close()}}),N.command("organization:grant <organization> <membership> <capability>","Add an explicit scoped allow or deny").option("--path <path>","Use a non-default control-plane database").option("--effect <effect>","allow|deny",{default:"allow"}).option("--scope <scope>","organization|project|environment|resource",{default:"organization"}).option("--scope-id <id>","Project, environment, or resource ID").action((t,m,A,L)=>{let j=sn(L?.path);try{let G=qi(j,t);if(!ie.includes(A))throw Error(`Unknown capability '${A}'.`);if(L?.effect!==void 0&&!["allow","deny"].includes(L.effect))throw Error("Effect must be allow or deny.");let Z=j.upsertGrant({organizationId:G.id,membershipId:m,effect:L?.effect??"allow",capability:A,scope:Qs(L?.scope,L?.scopeId)});n(`${Z.effect} ${Z.capability} added at ${Z.scope.type}:${Z.scope.id??G.slug}.`)}finally{j.close()}}),N.command("organization:revoke-member <membership>","Revoke a membership and invalidate its sessions").option("--path <path>","Use a non-default control-plane database").option("--confirm <id>","Type the membership ID to confirm").action((t,m)=>{if(m?.confirm!==t)throw Error(`Pass --confirm ${t} to revoke this membership.`);let A=sn(m?.path);try{let L=A.revokeMembership(t);n(`Membership ${L.id} revoked; session version is now ${L.sessionVersion}.`)}finally{A.close()}}),N.command("auth:identities","List local authentication identities without credential material").option("--path <path>","Use a non-default control-plane database").action((t)=>{let m=sn(t?.path);try{let A=new ke(m);i("Authentication identities");for(let L of A.listIdentities())e(`${L.username} ${L.email??"no-email"} ${L.disabledAt?"disabled":"active"} ${L.id}`)}finally{m.close()}}),N.command("auth:reset-link <identity>","Create a one-time password reset link for offline recovery").option("--path <path>","Use a non-default control-plane database").option("--base-url <url>","Dashboard URL used to print the reset link",{default:"http://127.0.0.1:7676"}).action((t,m)=>{let A=sn(m?.path);try{let L=new ke(A),j=Xs(L,t);L.revokeActionTokens(j.id,"password_reset");let G=L.createActionToken(j.id,"password_reset");n(`Created a one-time reset link for ${j.username}; it expires ${G.actionToken.expiresAt}.`),e(`${String(m?.baseUrl??"http://127.0.0.1:7676").replace(/\/$/,"")}/reset-password?token=${encodeURIComponent(G.token)}`)}finally{A.close()}}),N.command("auth:sessions <identity>","List active and expired sessions for an identity").option("--path <path>","Use a non-default control-plane database").action((t,m)=>{let A=sn(m?.path);try{let L=new ke(A),j=Xs(L,t);i(`${j.username} sessions`);for(let G of L.listSessions(j.id,{includeInactive:!0}))e(`${G.state} ${G.authMethod} ${G.userAgent??"unknown-device"} ${G.lastUsedAt} ${G.id}`)}finally{A.close()}}),N.command("auth:revoke-sessions <identity>","Revoke every dashboard session for an identity").option("--path <path>","Use a non-default control-plane database").option("--confirm <identity>","Type the username or email to confirm").action((t,m)=>{if(m?.confirm!==t)throw Error(`Pass --confirm ${t} to revoke these sessions.`);let A=sn(m?.path);try{let L=new ke(A),j=Xs(L,t),G=L.listSessions(j.id);for(let Z of G)L.revokeSession(j.id,Z.id);n(`Revoked ${G.length} session(s) for ${j.username}.`)}finally{A.close()}}),N.command("auth:disable-mfa <identity>","Disable MFA through the offline administrative recovery path").option("--path <path>","Use a non-default control-plane database").option("--confirm <identity>","Type the username or email to confirm").action((t,m)=>{if(m?.confirm!==t)throw Error(`Pass --confirm ${t} to disable MFA.`);let A=sn(m?.path);try{let L=new ke(A),j=Xs(L,t);L.disableMfa(j.id);for(let G of L.listSessions(j.id))L.revokeSession(j.id,G.id);n(`Disabled MFA and revoked active sessions for ${j.username}.`)}finally{A.close()}}),N.command("auth:oidc:list <organization>","List OIDC providers without credential material").option("--path <path>","Use a non-default control-plane database").action((t,m)=>{let A=sn(m?.path);try{let L=qi(A,t),j=ra(A);i(`${L.name} OIDC providers`);let G=j.listOidcProviders(L.id,{includeDisabled:!0});if(G.length===0)e("No providers configured.");for(let Z of G)e(`${Z.enabled?"enabled":"disabled"} ${Z.slug} ${Z.issuer} role=${Z.defaultRole} enforce=${Z.enforceSso}`)}finally{A.close()}}),N.command("auth:oidc:configure <organization> <slug>","Create or update an encrypted OIDC provider configuration").option("--path <path>","Use a non-default control-plane database").option("--auth-root <path>","Directory containing .ts-cloud/auth-encryption-key").option("--name <name>","Provider label shown on the sign-in page").option("--issuer <url>","Exact OIDC issuer URL").option("--client-id <id>","OIDC client ID").option("--secret-env <name>","Environment variable containing the client secret",{default:"TS_CLOUD_OIDC_CLIENT_SECRET"}).option("--domains <domains>","Comma-separated verified email domains").option("--scopes <scopes>","Comma-separated scopes (openid is always included)",{default:"email,profile"}).option("--default-role <role>","Role for provisioned users: admin, deployer, operator, viewer, or auditor",{default:"viewer"}).option("--enforce-sso","Require SSO for matching non-owner local accounts").option("--disabled","Save the provider disabled").action((t,m,A)=>{let L=sn(A?.path);try{let j=qi(L,t),G=ra(L,A?.authRoot),Z=G.getOidcProviderBySlug(m),ee=A?.secretEnv??"TS_CLOUD_OIDC_CLIENT_SECRET",ne=process.env[ee]?.trim();if(!Z&&!ne)throw Error(`Set ${ee} to the client secret before creating this provider.`);let oe=(A?.domains??Z?.allowedDomains.join(",")??"").split(",").map((Ce)=>Ce.trim()).filter(Boolean),be=A?.defaultRole??Z?.defaultRole??"viewer";if(!["admin","deployer","operator","viewer","auditor"].includes(be))throw Error("Default role must be admin, deployer, operator, viewer, or auditor.");let we=G.upsertOidcProvider({id:Z?.id,organizationId:j.id,slug:m,name:A?.name??Z?.name??m,issuer:A?.issuer??Z?.issuer??"",clientId:A?.clientId??Z?.clientId??"",clientSecret:ne,allowedDomains:oe,scopes:(A?.scopes??Z?.scopes.join(",")??"email,profile").split(",").map((Ce)=>Ce.trim()).filter(Boolean),defaultRole:be,enforceSso:A?.enforceSso??Z?.enforceSso??!1,enabled:A?.disabled?!1:Z?.enabled??!0});if(n(`${we.name} saved as ${we.enabled?"enabled":"disabled"} with encrypted credentials.`),e(`Callback path: /auth/oidc/${we.slug}/callback`),we.enforceSso)e(`Offline recovery: cloud auth:oidc:disable ${we.slug} --confirm ${we.slug}`)}finally{L.close()}}),N.command("auth:oidc:test <slug>","Validate OIDC discovery for a configured provider").option("--path <path>","Use a non-default control-plane database").option("--auth-root <path>","Directory containing .ts-cloud/auth-encryption-key").action(async(t,m)=>{let A=sn(m?.path);try{let j=ra(A,m?.authRoot).getOidcProviderBySlug(t);if(!j)throw Error(`OIDC provider '${t}' was not found.`);let G=await dn(j);n(`${j.name} discovery is valid and issuer-bound.`),e(`Authorization: ${G.authorization_endpoint}`),e(`Token: ${G.token_endpoint}`),e(`JWKS: ${G.jwks_uri}`)}finally{A.close()}}),N.command("auth:oidc:disable <slug>","Disable an OIDC provider through the offline recovery path").option("--path <path>","Use a non-default control-plane database").option("--auth-root <path>","Directory containing .ts-cloud/auth-encryption-key").option("--confirm <slug>","Type the provider slug to confirm").action((t,m)=>{if(m?.confirm!==t)throw Error(`Pass --confirm ${t} to disable this provider.`);let A=sn(m?.path);try{let L=ra(A,m?.authRoot),j=L.getOidcProviderBySlug(t);if(!j)throw Error(`OIDC provider '${t}' was not found.`);L.setOidcProviderEnabled(j.id,!1),n(`Disabled ${j.name}, cancelled pending sign-ins, and removed SSO enforcement.`)}finally{A.close()}}),N.command("api:service-accounts <organization>","List service accounts and safe token metadata").option("--path <path>","Use a non-default control-plane database").action((t,m)=>{let A=sn(m?.path);try{let L=qi(A,t),j=new Be(A);i(`${L.name} service accounts`);for(let G of j.listServiceAccounts(L.id,{includeDisabled:!0})){e(`${G.state} ${G.slug} ${G.name} ${G.id}`);for(let Z of j.listTokens(G.id,{includeInactive:!0}))e(` ${Z.state} ${Z.prefix} ${Z.name} expires=${Z.expiresAt} last-used=${Z.lastUsedAt??"never"}`)}}finally{A.close()}}),N.command("api:service-account:create <organization> <slug>","Create a scoped service account").option("--path <path>","Use a non-default control-plane database").option("--name <name>","Display name").option("--role <role>","admin, deployer, operator, viewer, or auditor",{default:"deployer"}).option("--scope <scope>","organization, project, environment, or resource",{default:"organization"}).option("--scope-id <id>","Required for non-organization scope").action((t,m,A)=>{let L=sn(A?.path);try{let j=qi(L,t),G=A?.role??"deployer";if(!["admin","deployer","operator","viewer","auditor"].includes(G))throw Error("Service-account role cannot be owner and must be a supported role.");let ee=new Be(L).createServiceAccount({organizationId:j.id,slug:m,name:A?.name??m,roleTemplate:G,scope:Qs(A?.scope,A?.scopeId)});n(`Created ${ee.serviceAccount.name} (${ee.serviceAccount.id}) with ${ee.membership.roleTemplate} access.`)}finally{L.close()}}),N.command("api:token:create <organization> <account>","Create and reveal a scoped API token exactly once").option("--path <path>","Use a non-default control-plane database").option("--name <name>","Token label",{default:"CLI token"}).option("--capabilities <list>","Comma-separated capabilities").option("--scope <scope>","organization, project, environment, or resource",{default:"organization"}).option("--scope-id <id>","Required for non-organization scope").option("--expires-days <days>","Expiry in days, maximum 365",{default:"90"}).action((t,m,A)=>{let L=sn(A?.path);try{let j=qi(L,t),G=new Be(L),Z=G.getServiceAccount(m)??G.getServiceAccountBySlug(j.id,m);if(!Z||Z.organizationId!==j.id)throw Error(`Service account '${m}' was not found.`);let ee=(A?.capabilities??"").split(",").map((be)=>be.trim()).filter((be)=>ie.includes(be));if(!ee.length)throw Error(`Pass --capabilities with one or more of: ${ie.join(", ")}`);let ne=Math.min(365,Math.max(1,Number(A?.expiresDays??90))),oe=G.createToken({serviceAccountId:Z.id,name:A?.name??"CLI token",capabilities:ee,scope:Qs(A?.scope,A?.scopeId),expiresAt:new Date(Date.now()+ne*24*60*60*1000).toISOString()});n(`Created ${oe.token.prefix}; copy this secret now. It will not be shown again.`),e(oe.secret)}finally{L.close()}}),N.command("api:token:rotate <token>","Create an overlapping replacement for an API token").option("--path <path>","Use a non-default control-plane database").action((t,m)=>{let A=sn(m?.path);try{let L=new Be(A).rotateToken(t);n(`Created overlapping replacement ${L.token.prefix}; revoke the old token after rollout.`),e(L.secret)}finally{A.close()}}),N.command("api:token:revoke <token>","Immediately revoke an API token").option("--path <path>","Use a non-default control-plane database").option("--confirm <token>","Type the token ID to confirm").action((t,m)=>{if(m?.confirm!==t)throw Error(`Pass --confirm ${t} to revoke this token.`);let A=sn(m?.path);try{let L=new Be(A).revokeToken(t);n(`Revoked ${L.prefix}; bearer requests stop immediately.`)}finally{A.close()}}),N.command("api:projects","List projects through the public v1 API").option("--base-url <url>","Dashboard base URL").option("--token-env <name>","Environment variable containing the API token",{default:"TS_CLOUD_API_TOKEN"}).action(async(t)=>{let m=await Dd(t?.baseUrl,t?.tokenEnv).listProjects();for(let A of m.data)e(`${A.slug} ${A.name} ${A.id}`)}),N.command("api:deploy <project> <environment>","Queue a deployment through the public v1 API").option("--base-url <url>","Dashboard base URL").option("--token-env <name>","Environment variable containing the API token",{default:"TS_CLOUD_API_TOKEN"}).option("--service <id>","Optional service resource ID").option("--revision <revision>","Revision or release identifier").option("--idempotency-key <key>","Stable retry key").action(async(t,m,A)=>{let L=A?.idempotencyKey??`cli-${Date.now()}-${crypto.randomUUID().slice(0,8)}`,j=await Dd(A?.baseUrl,A?.tokenEnv).createDeployment({projectId:t,environmentId:m,serviceId:A?.service,revision:A?.revision},L);n(`${j.idempotentReplay?"Replayed":"Queued"} operation ${j.operation.id} (${j.operation.state}).`)})}import{createHash as $f}from"node:crypto";var Cf=`let coldStart = true
466
466
  const initializedAt = new Date().toISOString()
467
467
  export const handler = async (event) => {
468
468
  const currentColdStart = coldStart
@@ -475,200 +475,200 @@ 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 Cd(N){let t=N;return String(t?.code||t?.name||t?.statusCode||"")}function pf(N){return["NoSuchEntity","ResourceNotFoundException","404"].includes(Cd(N))}function Sd(N){return["EntityAlreadyExists","ResourceAlreadyExistsException","ResourceConflictException","409"].includes(Cd(N))}function $d(N,t){let p=N?.Items;if(!p)return[];if(Array.isArray(p))return p;let A=p[t];if(A===void 0)return[];return Array.isArray(A)?A:[A]}function Nc(N){let t=N?.Aliases?.Items;if(Array.isArray(t))return t.map(String);if(typeof t==="string")return[t];return[]}function ff(N){if(!/^[A-Z0-9]{8,32}$/.test(N.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(N.expectedAlias))throw Error("Expected CloudFront alias is invalid");if(!/^[A-Za-z0-9-_]{1,64}$/.test(N.functionName))throw Error("Lambda function name is invalid");let t=N.pathPattern?.startsWith("/")?N.pathPattern:`/${N.pathPattern||"api/*"}`;if(t==="/"||t==="/*"||!t.endsWith("*"))throw Error("API path must be a non-default wildcard pattern such as /api/*");let p=N.originId||`${N.functionName}-url`,A=N.roleName||`${N.functionName}-execution`;if(!/^[A-Za-z0-9_.-]{1,64}$/.test(A))throw Error("IAM role name is invalid");let L=N.memorySize??256,j=N.timeout??10,G=N.logRetentionDays??14;if(L<128||L>10240)throw Error("Lambda memory must be between 128 and 10240 MB");if(j<1||j>900)throw Error("Lambda timeout must be between 1 and 900 seconds");return{region:N.region||"us-east-1",pathPattern:t,originId:p,roleName:A,memorySize:L,timeout:j,logRetentionDays:G}}function gf(N){let t=N.region||"us-east-1";return{cloudfront:new w(N.profile),iam:new Y(t,N.profile),lambda:new P(t,N.profile),logs:new ne(t,N.profile),sts:new st(t,N.profile),sleep:(p)=>new Promise((A)=>setTimeout(A,p))}}async function Ad(N,t){let p=ff(N),A=t||gf({profile:N.profile,region:p.region}),[L,j,G]=await Promise.all([A.sts.getCallerIdentity(),A.cloudfront.getDistribution(N.distributionId),A.cloudfront.getDistributionConfig(N.distributionId)]);if(!L.Account)throw Error("AWS identity did not return an account ID");if(!j.Enabled)throw Error(`CloudFront distribution ${N.distributionId} is disabled`);if(!(Nc(j).length?Nc(j):Nc(G.DistributionConfig)).includes(N.expectedAlias))throw Error(`Distribution ${N.distributionId} does not contain expected alias ${N.expectedAlias}`);let ee,re=!0;try{ee=await A.iam.getRole({RoleName:p.roleName})}catch(jt){if(!pf(jt))throw jt;re=!1}let oe=ee?.Arn||`arn:aws:iam::${L.Account}:role/${p.roleName}`,be=await A.lambda.functionExists(N.functionName),we=be?await A.lambda.getFunctionUrl(N.functionName):null,Ce=await A.cloudfront.listOriginAccessControls(),Ee=`${N.functionName}-lambda-url`,Se=Ce.some((jt)=>jt.Name===Ee&&jt.OriginAccessControlOriginType==="lambda"),De=$d(G.DistributionConfig.Origins,"Origin"),_e=$d(G.DistributionConfig.CacheBehaviors,"CacheBehavior"),Ae=De.some((jt)=>String(jt.Id)===p.originId),Te=_e.some((jt)=>String(jt.PathPattern)===p.pathPattern),Ne=[...!re?[`create IAM execution role ${p.roleName}`]:[`reconcile IAM execution policy ${p.roleName}`],...be?[`update Lambda ${N.functionName} code and configuration`]:[`create Lambda ${N.functionName}`],...!we?["create private AWS_IAM function URL"]:[],...!Se?[`create Lambda origin access control ${Ee}`]:[],"grant CloudFront function-URL and invocation permissions",`${Te?"reconcile":"add"} ${p.pathPattern} behavior without changing the default behavior`],Xe={command:`cloud cdn:origin:remove ${N.distributionId} FUNCTION_URL_HOST --id ${p.originId} --path '${p.pathPattern}' --apply --confirm 'remove:${N.distributionId}:${p.pathPattern}'`,removeFunction:!be,removeRole:!re},Ge={mode:N.apply?"apply":"plan",accountId:L.Account,identityArn:L.Arn,distribution:{id:j.Id,arn:j.ARN,alias:N.expectedAlias,domainName:j.DomainName,status:j.Status},function:{name:N.functionName,existed:be,runtime:"nodejs22.x",urlExisted:!!we,url:we?.FunctionUrl},role:{name:p.roleName,existed:re,arn:oe},origin:{id:p.originId,pathPattern:p.pathPattern,existed:Ae,behaviorExisted:Te,accessControlExisted:Se},changes:Ne,rollback:Xe,applied:!1};if(!N.apply)return Ge;let Ye=`${N.distributionId}:${p.pathPattern}`;if(N.confirm!==Ye)throw Error(`Pass exact confirmation token ${Ye} to mutate the live distribution`);if(!ee)ee=await A.iam.createRole({RoleName:p.roleName,Description:`Execution role for ${N.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:p.roleName,PolicyName:"lambda-logs",PolicyDocument:JSON.stringify({Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:`arn:aws:logs:${p.region}:${L.Account}:log-group:/aws/lambda/${N.functionName}:*`}]})});try{await A.logs.createLogGroup(`/aws/lambda/${N.functionName}`,{ManagedBy:"ts-cloud"})}catch(jt){if(!Sd(jt))throw jt}await A.logs.putRetentionPolicy(`/aws/lambda/${N.functionName}`,p.logRetentionDays);let et=N.code||mf;if(be)await A.lambda.updateFunctionCodeInline(N.functionName,et,"index.mjs"),await A.lambda.waitForFunctionActive(N.functionName),await A.lambda.updateFunctionConfiguration({FunctionName:N.functionName,Runtime:"nodejs22.x",Role:ee.Arn,Handler:"index.handler",Description:`Private API origin for ${N.expectedAlias}`,Timeout:p.timeout,MemorySize:p.memorySize});else{let jt;for(let ft=0;ft<8;ft++)try{await A.lambda.createFunctionWithCode({FunctionName:N.functionName,Runtime:"nodejs22.x",Role:ee.Arn,Handler:"index.handler",Code:et,Filename:"index.mjs",Description:`Private API origin for ${N.expectedAlias}`,Timeout:p.timeout,MemorySize:p.memorySize}),jt=void 0;break}catch(Vt){if(jt=Vt,!String(Vt.message).toLowerCase().includes("role"))throw Vt;await A.sleep(1000)}if(jt)throw jt}await A.lambda.waitForFunctionActive(N.functionName);let Pt=we||await A.lambda.createFunctionUrl({FunctionName:N.functionName,AuthType:"AWS_IAM",InvokeMode:"BUFFERED"});if(!Pt.FunctionUrl)throw Error("Lambda did not return a function URL");let wt=new URL(Pt.FunctionUrl).hostname,Ot=await A.cloudfront.findOrCreateOriginAccessControl(Ee,"lambda"),Ut=j.ARN||`arn:aws:cloudfront::${L.Account}:distribution/${N.distributionId}`,sn=[{FunctionName:N.functionName,StatementId:"AllowCloudFrontFunctionUrl",Action:"lambda:InvokeFunctionUrl",Principal:"cloudfront.amazonaws.com",SourceArn:Ut,FunctionUrlAuthType:"AWS_IAM"},{FunctionName:N.functionName,StatementId:"AllowCloudFrontInvoke",Action:"lambda:InvokeFunction",Principal:"cloudfront.amazonaws.com",SourceArn:Ut,InvokedViaFunctionUrl:!0}];for(let jt of sn)try{await A.lambda.addPermission(jt)}catch(ft){if(!Sd(ft))throw ft}return await A.cloudfront.upsertExistingDistributionOrigin(N.distributionId,{id:p.originId,domainName:wt,pathPattern:p.pathPattern,originAccessControlId:Ot.Id}),{...Ge,function:{...Ge.function,url:Pt.FunctionUrl},applied:!0}}async function kd(N){let t=`https://${N.alias}/`,p=`https://${N.alias}/api/health`,A=await fetch(t,{redirect:"follow",headers:{"cache-control":"no-cache"}}),L=new Uint8Array(await A.arrayBuffer()),j=uf("sha256").update(L).digest("hex"),G=Date.now(),Z=await fetch(p,{redirect:"follow",headers:{"cache-control":"no-cache"}}),ee=Date.now()-G,re=await Z.text(),oe=re;try{oe=JSON.parse(re)}catch{}let be;if(N.logs&&N.functionName){let we=await N.logs.filterLogEvents({logGroupName:`/aws/lambda/${N.functionName}`,startTime:G-60000,filterPattern:'"Init Duration"',limit:25});for(let Ce of we.events||[]){let Ee=Ce.message?.match(/Init Duration:\s*([0-9.]+)\s*ms/i);if(Ee)be=Number(Ee[1])}}return{frontend:{url:t,status:A.status,sha256:j,unchanged:N.expectedFrontendSha256?j===N.expectedFrontendSha256:void 0,bytes:L.byteLength},api:{url:p,status:Z.status,latencyMs:ee,healthy:Z.ok&&!!oe?.ok,body:oe},coldStart:{observed:be!==void 0,initDurationMs:be},verifiedAt:(N.now?.()||new Date).toISOString()}}function Rd(N){let t=(N.memoryMb||256)/1024,p=Math.max(0,N.requests-(N.includeFreeTier===!1?0:1e6)),A=Math.max(0,N.requests*(N.averageDurationMs/1000)*t-(N.includeFreeTier===!1?0:400000)),L=p/1e6*0.2+A*0.0000166667,j=9.010025,G=16.425;return{lambdaUsd:Number(L.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 Oc(N){N.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,p,A)=>{try{let L=await Ad({distributionId:t,expectedAlias:p,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(L,null,2)),L.applied)n("Private API origin submitted to CloudFront. Run cdn:api:verify after propagation.");else e(`Plan only. Re-run with --apply --confirm '${t}:${L.origin.pathPattern}'.`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.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,p)=>{try{let A=new ne(p.region||"us-east-1",p.profile),L=await kd({alias:t,expectedFrontendSha256:p.frontendSha256,logs:A,functionName:p.function||"ts-cloud-static-api"});if(e(JSON.stringify(L,null,2)),!L.api.healthy||L.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}}),N.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,p)=>{let A=Rd({requests:Number(t),averageDurationMs:Number(p.duration),memoryMb:Number(p.memory),includeFreeTier:p.freeTier!==!1});e(JSON.stringify(A,null,2))}),N.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,p,A)=>{try{let L=new w(A.profile),j={id:A.id??"api-backend",domainName:p,pathPattern:A.path??"/api/*",replaceExisting:!!A.replace},G=await L.upsertExistingDistributionOrigin(t,{...j,dryRun:!0});if(e(JSON.stringify(G,null,2)),!A.apply){e("Dry run only. Re-run with --apply and the exact --confirm token.");return}let Z=`${t}:${G.pathPattern}`;if(A.confirm!==Z)throw Error(`Pass --confirm "${Z}" to apply this live distribution change`);let ee=await L.upsertExistingDistributionOrigin(t,j);n(ee.changed?"Backend origin patch submitted.":"Backend origin already matches."),e(JSON.stringify(ee,null,2))}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.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,p,A)=>{try{let L=new w(A.profile),j={id:A.id??"api-backend",domainName:p,pathPattern:A.path??"/api/*"},G=await L.removeExistingDistributionOrigin(t,{...j,dryRun:!0});if(e(JSON.stringify(G,null,2)),!A.apply){e("Dry run only. Re-run with --apply and the exact --confirm token.");return}let Z=`remove:${t}:${G.pathPattern}`;if(A.confirm!==Z)throw Error(`Pass --confirm "${Z}" to apply this live distribution rollback`);let ee=await L.removeExistingDistributionOrigin(t,j);n(ee.changed?"Backend behavior rollback submitted.":"Backend behavior was already absent."),e(JSON.stringify(ee,null,2))}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.command("cdn:list","List all CloudFront distributions").action(async()=>{i("CloudFront Distributions");try{let t=await Me(),p=new w,A=new o("Fetching distributions...");A.start();let L=await p.listDistributions();if(A.succeed(`Found ${L.length} distribution(s)`),L.length===0){e("No CloudFront distributions found"),e("Use `cloud cdn:create` to create a new distribution");return}c(["ID","Domain","Status","Enabled","Aliases"],L.map((j)=>[j.Id||"N/A",j.DomainName||"N/A",j.Status||"N/A",j.Enabled?"Yes":"No",(j.Aliases?.Items?.length||0).toString()]))}catch(t){r(`Failed to list distributions: ${t.message}`),process.exit(1)}}),N.command("cdn:status <distributionId>","Get CloudFront distribution status").action(async(t)=>{i(`CloudFront Distribution: ${t}`);try{let p=new w,A=new o("Fetching distribution details...");A.start();let L=await p.getDistribution(t);if(A.succeed("Distribution details loaded"),e(`
479
- Distribution Information:`),e(` ID: ${L.Id}`),e(` Domain: ${L.DomainName}`),e(` Status: ${L.Status}`),e(` ARN: ${L.ARN}`),e(` Enabled: ${L.Enabled?"Yes":"No"}`),L.Aliases?.Items?.length)e(` Aliases: ${L.Aliases.Items.join(", ")}`);let j=await p.getDistributionConfig(t);e(`
478
+ `;function Nd(N){let t=N;return String(t?.code||t?.name||t?.statusCode||"")}function Af(N){return["NoSuchEntity","ResourceNotFoundException","404"].includes(Nd(N))}function Ed(N){return["EntityAlreadyExists","ResourceAlreadyExistsException","ResourceConflictException","409"].includes(Nd(N))}function Td(N,t){let m=N?.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 Vc(N){let t=N?.Aliases?.Items;if(Array.isArray(t))return t.map(String);if(typeof t==="string")return[t];return[]}function kf(N){if(!/^[A-Z0-9]{8,32}$/.test(N.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(N.expectedAlias))throw Error("Expected CloudFront alias is invalid");if(!/^[A-Za-z0-9-_]{1,64}$/.test(N.functionName))throw Error("Lambda function name is invalid");let t=N.pathPattern?.startsWith("/")?N.pathPattern:`/${N.pathPattern||"api/*"}`;if(t==="/"||t==="/*"||!t.endsWith("*"))throw Error("API path must be a non-default wildcard pattern such as /api/*");let m=N.originId||`${N.functionName}-url`,A=N.roleName||`${N.functionName}-execution`;if(!/^[A-Za-z0-9_.-]{1,64}$/.test(A))throw Error("IAM role name is invalid");let L=N.memorySize??256,j=N.timeout??10,G=N.logRetentionDays??14;if(L<128||L>10240)throw Error("Lambda memory must be between 128 and 10240 MB");if(j<1||j>900)throw Error("Lambda timeout must be between 1 and 900 seconds");return{region:N.region||"us-east-1",pathPattern:t,originId:m,roleName:A,memorySize:L,timeout:j,logRetentionDays:G}}function Rf(N){let t=N.region||"us-east-1";return{cloudfront:new w(N.profile),iam:new Y(t,N.profile),lambda:new R(t,N.profile),logs:new re(t,N.profile),sts:new st(t,N.profile),sleep:(m)=>new Promise((A)=>setTimeout(A,m))}}async function Od(N,t){let m=kf(N),A=t||Rf({profile:N.profile,region:m.region}),[L,j,G]=await Promise.all([A.sts.getCallerIdentity(),A.cloudfront.getDistribution(N.distributionId),A.cloudfront.getDistributionConfig(N.distributionId)]);if(!L.Account)throw Error("AWS identity did not return an account ID");if(!j.Enabled)throw Error(`CloudFront distribution ${N.distributionId} is disabled`);if(!(Vc(j).length?Vc(j):Vc(G.DistributionConfig)).includes(N.expectedAlias))throw Error(`Distribution ${N.distributionId} does not contain expected alias ${N.expectedAlias}`);let ee,ne=!0;try{ee=await A.iam.getRole({RoleName:m.roleName})}catch(zt){if(!Af(zt))throw zt;ne=!1}let oe=ee?.Arn||`arn:aws:iam::${L.Account}:role/${m.roleName}`,be=await A.lambda.functionExists(N.functionName),we=be?await A.lambda.getFunctionUrl(N.functionName):null,Ce=await A.cloudfront.listOriginAccessControls(),De=`${N.functionName}-lambda-url`,Se=Ce.some((zt)=>zt.Name===De&&zt.OriginAccessControlOriginType==="lambda"),xe=Td(G.DistributionConfig.Origins,"Origin"),Me=Td(G.DistributionConfig.CacheBehaviors,"CacheBehavior"),Ae=xe.some((zt)=>String(zt.Id)===m.originId),Ee=Me.some((zt)=>String(zt.PathPattern)===m.pathPattern),Te=[...!ne?[`create IAM execution role ${m.roleName}`]:[`reconcile IAM execution policy ${m.roleName}`],...be?[`update Lambda ${N.functionName} code and configuration`]:[`create Lambda ${N.functionName}`],...!we?["create private AWS_IAM function URL"]:[],...!Se?[`create Lambda origin access control ${De}`]:[],"grant CloudFront function-URL and invocation permissions",`${Ee?"reconcile":"add"} ${m.pathPattern} behavior without changing the default behavior`],Qe={command:`cloud cdn:origin:remove ${N.distributionId} FUNCTION_URL_HOST --id ${m.originId} --path '${m.pathPattern}' --apply --confirm 'remove:${N.distributionId}:${m.pathPattern}'`,removeFunction:!be,removeRole:!ne},Ke={mode:N.apply?"apply":"plan",accountId:L.Account,identityArn:L.Arn,distribution:{id:j.Id,arn:j.ARN,alias:N.expectedAlias,domainName:j.DomainName,status:j.Status},function:{name:N.functionName,existed:be,runtime:"nodejs22.x",urlExisted:!!we,url:we?.FunctionUrl},role:{name:m.roleName,existed:ne,arn:oe},origin:{id:m.originId,pathPattern:m.pathPattern,existed:Ae,behaviorExisted:Ee,accessControlExisted:Se},changes:Te,rollback:Qe,applied:!1};if(!N.apply)return Ke;let Ye=`${N.distributionId}:${m.pathPattern}`;if(N.confirm!==Ye)throw Error(`Pass exact confirmation token ${Ye} to mutate the live distribution`);if(!ee)ee=await A.iam.createRole({RoleName:m.roleName,Description:`Execution role for ${N.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}:${L.Account}:log-group:/aws/lambda/${N.functionName}:*`}]})});try{await A.logs.createLogGroup(`/aws/lambda/${N.functionName}`,{ManagedBy:"ts-cloud"})}catch(zt){if(!Ed(zt))throw zt}await A.logs.putRetentionPolicy(`/aws/lambda/${N.functionName}`,m.logRetentionDays);let et=N.code||Cf;if(be)await A.lambda.updateFunctionCodeInline(N.functionName,et,"index.mjs"),await A.lambda.waitForFunctionActive(N.functionName),await A.lambda.updateFunctionConfiguration({FunctionName:N.functionName,Runtime:"nodejs22.x",Role:ee.Arn,Handler:"index.handler",Description:`Private API origin for ${N.expectedAlias}`,Timeout:m.timeout,MemorySize:m.memorySize});else{let zt;for(let ht=0;ht<8;ht++)try{await A.lambda.createFunctionWithCode({FunctionName:N.functionName,Runtime:"nodejs22.x",Role:ee.Arn,Handler:"index.handler",Code:et,Filename:"index.mjs",Description:`Private API origin for ${N.expectedAlias}`,Timeout:m.timeout,MemorySize:m.memorySize}),zt=void 0;break}catch(Vt){if(zt=Vt,!String(Vt.message).toLowerCase().includes("role"))throw Vt;await A.sleep(1000)}if(zt)throw zt}await A.lambda.waitForFunctionActive(N.functionName);let Pt=we||await A.lambda.createFunctionUrl({FunctionName:N.functionName,AuthType:"AWS_IAM",InvokeMode:"BUFFERED"});if(!Pt.FunctionUrl)throw Error("Lambda did not return a function URL");let Bt=new URL(Pt.FunctionUrl).hostname,vt=await A.cloudfront.findOrCreateOriginAccessControl(De,"lambda"),Ot=j.ARN||`arn:aws:cloudfront::${L.Account}:distribution/${N.distributionId}`,an=[{FunctionName:N.functionName,StatementId:"AllowCloudFrontFunctionUrl",Action:"lambda:InvokeFunctionUrl",Principal:"cloudfront.amazonaws.com",SourceArn:Ot,FunctionUrlAuthType:"AWS_IAM"},{FunctionName:N.functionName,StatementId:"AllowCloudFrontInvoke",Action:"lambda:InvokeFunction",Principal:"cloudfront.amazonaws.com",SourceArn:Ot,InvokedViaFunctionUrl:!0}];for(let zt of an)try{await A.lambda.addPermission(zt)}catch(ht){if(!Ed(ht))throw ht}return await A.cloudfront.upsertExistingDistributionOrigin(N.distributionId,{id:m.originId,domainName:Bt,pathPattern:m.pathPattern,originAccessControlId:vt.Id}),{...Ke,function:{...Ke.function,url:Pt.FunctionUrl},applied:!0}}async function Ld(N){let t=`https://${N.alias}/`,m=`https://${N.alias}/api/health`,A=await fetch(t,{redirect:"follow",headers:{"cache-control":"no-cache"}}),L=new Uint8Array(await A.arrayBuffer()),j=$f("sha256").update(L).digest("hex"),G=Date.now(),Z=await fetch(m,{redirect:"follow",headers:{"cache-control":"no-cache"}}),ee=Date.now()-G,ne=await Z.text(),oe=ne;try{oe=JSON.parse(ne)}catch{}let be;if(N.logs&&N.functionName){let we=await N.logs.filterLogEvents({logGroupName:`/aws/lambda/${N.functionName}`,startTime:G-60000,filterPattern:'"Init Duration"',limit:25});for(let Ce of we.events||[]){let De=Ce.message?.match(/Init Duration:\s*([0-9.]+)\s*ms/i);if(De)be=Number(De[1])}}return{frontend:{url:t,status:A.status,sha256:j,unchanged:N.expectedFrontendSha256?j===N.expectedFrontendSha256:void 0,bytes:L.byteLength},api:{url:m,status:Z.status,latencyMs:ee,healthy:Z.ok&&!!oe?.ok,body:oe},coldStart:{observed:be!==void 0,initDurationMs:be},verifiedAt:(N.now?.()||new Date).toISOString()}}function jd(N){let t=(N.memoryMb||256)/1024,m=Math.max(0,N.requests-(N.includeFreeTier===!1?0:1e6)),A=Math.max(0,N.requests*(N.averageDurationMs/1000)*t-(N.includeFreeTier===!1?0:400000)),L=m/1e6*0.2+A*0.0000166667,j=9.010025,G=16.425;return{lambdaUsd:Number(L.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 Wc(N){N.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 L=await Od({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(L,null,2)),L.applied)n("Private API origin submitted to CloudFront. Run cdn:api:verify after propagation.");else e(`Plan only. Re-run with --apply --confirm '${t}:${L.origin.pathPattern}'.`)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.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 re(m.region||"us-east-1",m.profile),L=await Ld({alias:t,expectedFrontendSha256:m.frontendSha256,logs:A,functionName:m.function||"ts-cloud-static-api"});if(e(JSON.stringify(L,null,2)),!L.api.healthy||L.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}}),N.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=jd({requests:Number(t),averageDurationMs:Number(m.duration),memoryMb:Number(m.memory),includeFreeTier:m.freeTier!==!1});e(JSON.stringify(A,null,2))}),N.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 L=new w(A.profile),j={id:A.id??"api-backend",domainName:m,pathPattern:A.path??"/api/*",replaceExisting:!!A.replace},G=await L.upsertExistingDistributionOrigin(t,{...j,dryRun:!0});if(e(JSON.stringify(G,null,2)),!A.apply){e("Dry run only. Re-run with --apply and the exact --confirm token.");return}let Z=`${t}:${G.pathPattern}`;if(A.confirm!==Z)throw Error(`Pass --confirm "${Z}" to apply this live distribution change`);let ee=await L.upsertExistingDistributionOrigin(t,j);n(ee.changed?"Backend origin patch submitted.":"Backend origin already matches."),e(JSON.stringify(ee,null,2))}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.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 L=new w(A.profile),j={id:A.id??"api-backend",domainName:m,pathPattern:A.path??"/api/*"},G=await L.removeExistingDistributionOrigin(t,{...j,dryRun:!0});if(e(JSON.stringify(G,null,2)),!A.apply){e("Dry run only. Re-run with --apply and the exact --confirm token.");return}let Z=`remove:${t}:${G.pathPattern}`;if(A.confirm!==Z)throw Error(`Pass --confirm "${Z}" to apply this live distribution rollback`);let ee=await L.removeExistingDistributionOrigin(t,j);n(ee.changed?"Backend behavior rollback submitted.":"Backend behavior was already absent."),e(JSON.stringify(ee,null,2))}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.command("cdn:list","List all CloudFront distributions").action(async()=>{i("CloudFront Distributions");try{let t=await Ne(),m=new w,A=new o("Fetching distributions...");A.start();let L=await m.listDistributions();if(A.succeed(`Found ${L.length} distribution(s)`),L.length===0){e("No CloudFront distributions found"),e("Use `cloud cdn:create` to create a new distribution");return}c(["ID","Domain","Status","Enabled","Aliases"],L.map((j)=>[j.Id||"N/A",j.DomainName||"N/A",j.Status||"N/A",j.Enabled?"Yes":"No",(j.Aliases?.Items?.length||0).toString()]))}catch(t){r(`Failed to list distributions: ${t.message}`),process.exit(1)}}),N.command("cdn:status <distributionId>","Get CloudFront distribution status").action(async(t)=>{i(`CloudFront Distribution: ${t}`);try{let m=new w,A=new o("Fetching distribution details...");A.start();let L=await m.getDistribution(t);if(A.succeed("Distribution details loaded"),e(`
479
+ Distribution Information:`),e(` ID: ${L.Id}`),e(` Domain: ${L.DomainName}`),e(` Status: ${L.Status}`),e(` ARN: ${L.ARN}`),e(` Enabled: ${L.Enabled?"Yes":"No"}`),L.Aliases?.Items?.length)e(` Aliases: ${L.Aliases.Items.join(", ")}`);let j=await m.getDistributionConfig(t);e(`
480
480
  Origins:`);let G=j.DistributionConfig?.Origins?.Items||[];if(Array.isArray(G))for(let Z of G)e(` - ${Z.Id}: ${Z.DomainName}`);if(j.DistributionConfig?.DefaultCacheBehavior){let Z=j.DistributionConfig.DefaultCacheBehavior;e(`
481
- Default Cache Behavior:`),e(` Target Origin: ${Z.TargetOriginId}`),e(` Viewer Protocol: ${Z.ViewerProtocolPolicy}`)}}catch(p){r(`Failed to get distribution: ${p.message}`),process.exit(1)}}),N.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 p=new w;if(e(`Bucket: ${t.bucket}`),e(`Region: ${t.region}`),t.alias)e(`Alias: ${t.alias}`);if(!await d(`
482
- Create this distribution?`,!0)){e("Operation cancelled");return}let L=new o("Setting up Origin Access Control...");L.start();let j=await p.findOrCreateOriginAccessControl(`OAC-${t.bucket}`);if(j.isNew)L.text="Created new Origin Access Control";L.text="Creating distribution...";let G=t.alias?[t.alias]:[],Z=await p.createDistributionForS3({bucketName:t.bucket,bucketRegion:t.region,originAccessControlId:j.Id,aliases:G,certificateArn:t.certificate,comment:t.comment||"Created by ts-cloud CLI"});L.succeed("Distribution created"),n(`
481
+ Default Cache Behavior:`),e(` Target Origin: ${Z.TargetOriginId}`),e(` Viewer Protocol: ${Z.ViewerProtocolPolicy}`)}}catch(m){r(`Failed to get distribution: ${m.message}`),process.exit(1)}}),N.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 w;if(e(`Bucket: ${t.bucket}`),e(`Region: ${t.region}`),t.alias)e(`Alias: ${t.alias}`);if(!await d(`
482
+ Create this distribution?`,!0)){e("Operation cancelled");return}let L=new o("Setting up Origin Access Control...");L.start();let j=await m.findOrCreateOriginAccessControl(`OAC-${t.bucket}`);if(j.isNew)L.text="Created new Origin Access Control";L.text="Creating distribution...";let G=t.alias?[t.alias]:[],Z=await m.createDistributionForS3({bucketName:t.bucket,bucketRegion:t.region,originAccessControlId:j.Id,aliases:G,certificateArn:t.certificate,comment:t.comment||"Created by ts-cloud CLI"});L.succeed("Distribution created"),n(`
483
483
  Distribution ID: ${Z.Id}`),e(`Domain: ${Z.DomainName}`),e(`Status: ${Z.Status}`),e(`
484
484
  Note: Distribution deployment may take 15-30 minutes.`),e(`
485
- IMPORTANT: Update your S3 bucket policy to allow CloudFront access:`);let ee=w.getS3BucketPolicyForCloudFront(t.bucket,Z.ARN);e(JSON.stringify(ee,null,2))}catch(p){r(`Failed to create distribution: ${p.message}`),process.exit(1)}}),N.command("cdn:invalidate <distributionId>","Invalidate CloudFront cache").option("--paths <paths>","Paths to invalidate (comma-separated)",{default:"/*"}).action(async(t,p)=>{i("Invalidate CloudFront Cache");try{let A=new w,L=p.paths.split(",").map((ee)=>ee.trim());if(e(`Distribution: ${t}`),e(`Paths: ${L.join(", ")}`),!await d(`
485
+ IMPORTANT: Update your S3 bucket policy to allow CloudFront access:`);let ee=w.getS3BucketPolicyForCloudFront(t.bucket,Z.ARN);e(JSON.stringify(ee,null,2))}catch(m){r(`Failed to create distribution: ${m.message}`),process.exit(1)}}),N.command("cdn:invalidate <distributionId>","Invalidate CloudFront cache").option("--paths <paths>","Paths to invalidate (comma-separated)",{default:"/*"}).action(async(t,m)=>{i("Invalidate CloudFront Cache");try{let A=new w,L=m.paths.split(",").map((ee)=>ee.trim());if(e(`Distribution: ${t}`),e(`Paths: ${L.join(", ")}`),!await d(`
486
486
  Create invalidation?`,!0)){e("Operation cancelled");return}let G=new o("Creating invalidation...");G.start();let Z=await A.createInvalidation({distributionId:t,paths:L,callerReference:`cli-${Date.now()}`});G.succeed("Invalidation created"),n(`
487
487
  Invalidation ID: ${Z.Id}`),e(`Status: ${Z.Status}`),e(`
488
- Note: Invalidation typically completes in 5-10 minutes.`)}catch(A){r(`Failed to create invalidation: ${A.message}`),process.exit(1)}}),N.command("cdn:disable <distributionId>","Disable a CloudFront distribution").action(async(t)=>{i("Disable CloudFront Distribution");try{let p=new w;if(a(`This will disable distribution: ${t}`),e("The distribution will stop serving content."),!await d(`
489
- Disable this distribution?`,!1)){e("Operation cancelled");return}let L=new o("Disabling distribution...");L.start(),await p.disableDistribution(t),L.succeed("Distribution disabled"),e(`
490
- Note: Changes may take 15-30 minutes to propagate.`),e("To delete the distribution, wait for it to be fully disabled first.")}catch(p){r(`Failed to disable distribution: ${p.message}`),process.exit(1)}}),N.command("cdn:delete <distributionId>","Delete a CloudFront distribution").action(async(t)=>{i("Delete CloudFront Distribution");try{let p=new w;if(a(`This will permanently delete distribution: ${t}`),a("The distribution must be disabled first."),!await d(`
491
- Delete this distribution?`,!1)){e("Operation cancelled");return}let L=new o("Checking distribution status...");L.start();let j=await p.getDistribution(t);if(j.Status!=="Deployed"||j.Enabled){L.fail("Distribution must be disabled and deployed before deletion"),e('\nRun `cloud cdn:disable` first and wait for status to be "Deployed"');return}L.text="Deleting distribution...",await p.deleteDistribution(t),L.succeed("Distribution deleted")}catch(p){r(`Failed to delete distribution: ${p.message}`),process.exit(1)}})}import{readdirSync as hf,statSync as yf}from"node:fs";import{join as Id,relative as vf}from"node:path";async function ai(N){let t=await Me(),p=y(process.cwd(),t),A=N??Object.keys(t.environments??{})[0]??"production",L=p.environments.get(A);if(!L)throw p.store.close(),Error(`Environment ${A} was not found.`);let j=p.store.getActorByExternalId("system","cli")??p.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"}),G=new dt(p.store),Z=new Vr(G,[new Dt,new zr(Id(process.cwd(),".ts-cloud","volumes"))]);return{controlPlane:p,environmentRecord:L,actor:j,store:G,service:Z}}function bf(N){return N.map((t)=>[t.name,`${t.provider}/${t.type}`,t.status,t.capacityBytes==null?"—":ps(t.capacityBytes),t.usedBytes==null?"—":ps(t.usedBytes),String(t.attachments.filter((p)=>p.observedState==="attached").length),t.backupState,t.orphaned?"orphan":"managed"])}function Lc(N){N.command("storage:list","List all S3 buckets").option("--region <region>","AWS region").action(async(p)=>{i("S3 Buckets");try{let A=await Me(),L=p.region||A.project.region||"us-east-1",j=new f(L),G=new o("Fetching buckets...");G.start();let ee=(await j.listBuckets()).Buckets||[];if(G.succeed(`Found ${ee.length} bucket(s)`),ee.length===0){e("No S3 buckets found"),e("Use `cloud storage:create` to create a new bucket");return}c(["Name","Created"],ee.map((re)=>[re.Name||"N/A",re.CreationDate?new Date(re.CreationDate).toLocaleDateString():"N/A"]))}catch(A){r(`Failed to list buckets: ${A.message}`),process.exit(1)}}),N.command("storage:create <name>","Create a new S3 bucket").option("--region <region>","AWS region",{default:"us-east-1"}).option("--public","Enable public access").option("--versioning","Enable versioning").action(async(p,A)=>{i("Create S3 Bucket");try{let L=new f(A.region);if(e(`Bucket name: ${p}`),e(`Region: ${A.region}`),e(`Public access: ${A.public?"Yes":"No"}`),e(`Versioning: ${A.versioning?"Yes":"No"}`),!await d(`
492
- Create this bucket?`,!0)){e("Operation cancelled");return}let G=new o("Creating bucket...");if(G.start(),await L.createBucket(p),!A.public)G.text="Configuring public access block...",await L.putPublicAccessBlock(p,{BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!0,RestrictPublicBuckets:!0});if(A.versioning)G.text="Enabling versioning...",await L.putBucketVersioning(p,"Enabled");G.succeed("Bucket created"),n(`
493
- Bucket: ${p}`),e(`Region: ${A.region}`),e(`URL: s3://${p}`)}catch(L){r(`Failed to create bucket: ${L.message}`),process.exit(1)}}),N.command("storage:delete <name>","Delete an S3 bucket").option("--force","Delete all objects first").action(async(p,A)=>{i("Delete S3 Bucket");try{let L=new f("us-east-1");if(a(`This will permanently delete bucket: ${p}`),A.force)a("All objects in the bucket will be deleted!");if(!await d(`
494
- Delete this bucket?`,!1)){e("Operation cancelled");return}let G=new o("Deleting bucket...");if(G.start(),A.force)G.text="Emptying bucket...",await L.emptyBucket(p);await L.deleteBucket(p),G.succeed("Bucket deleted")}catch(L){r(`Failed to delete bucket: ${L.message}`),process.exit(1)}}),N.command("storage:sync <source> <bucket>","Sync local directory to S3 bucket").option("--prefix <prefix>","S3 key prefix").option("--delete","Delete files in S3 that are not in source").option("--dry-run","Show what would be synced without making changes").action(async(p,A,L)=>{i("Sync to S3");try{let ee=function(we,Ce){let Ee=hf(we,{withFileTypes:!0});for(let Se of Ee){let De=Id(we,Se.name);if(Se.isDirectory())ee(De,Ce);else{let _e=vf(Ce,De),Ae=L.prefix?`${L.prefix}/${_e}`:_e,Te=yf(De);Z.push({path:De,key:Ae,size:Te.size})}}},j=new f("us-east-1");if(e(`Source: ${p}`),e(`Destination: s3://${A}/${L.prefix||""}`),L.dryRun)e("Dry run mode - no changes will be made");let G=new o("Scanning files...");G.start();let Z=[];if(ee(p,p),G.succeed(`Found ${Z.length} local file(s)`),Z.length===0){e("No files to sync");return}e(`
488
+ Note: Invalidation typically completes in 5-10 minutes.`)}catch(A){r(`Failed to create invalidation: ${A.message}`),process.exit(1)}}),N.command("cdn:disable <distributionId>","Disable a CloudFront distribution").action(async(t)=>{i("Disable CloudFront Distribution");try{let m=new w;if(a(`This will disable distribution: ${t}`),e("The distribution will stop serving content."),!await d(`
489
+ Disable this distribution?`,!1)){e("Operation cancelled");return}let L=new o("Disabling distribution...");L.start(),await m.disableDistribution(t),L.succeed("Distribution disabled"),e(`
490
+ Note: Changes may take 15-30 minutes to propagate.`),e("To delete the distribution, wait for it to be fully disabled first.")}catch(m){r(`Failed to disable distribution: ${m.message}`),process.exit(1)}}),N.command("cdn:delete <distributionId>","Delete a CloudFront distribution").action(async(t)=>{i("Delete CloudFront Distribution");try{let m=new w;if(a(`This will permanently delete distribution: ${t}`),a("The distribution must be disabled first."),!await d(`
491
+ Delete this distribution?`,!1)){e("Operation cancelled");return}let L=new o("Checking distribution status...");L.start();let j=await m.getDistribution(t);if(j.Status!=="Deployed"||j.Enabled){L.fail("Distribution must be disabled and deployed before deletion"),e('\nRun `cloud cdn:disable` first and wait for status to be "Deployed"');return}L.text="Deleting distribution...",await m.deleteDistribution(t),L.succeed("Distribution deleted")}catch(m){r(`Failed to delete distribution: ${m.message}`),process.exit(1)}})}import{readdirSync as If,statSync as Pf}from"node:fs";import{join as Fd,relative as xf}from"node:path";async function ui(N){let t=await Ne(),m=y(process.cwd(),t),A=N??Object.keys(t.environments??{})[0]??"production",L=m.environments.get(A);if(!L)throw m.store.close(),Error(`Environment ${A} was not found.`);let j=m.store.getActorByExternalId("system","cli")??m.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"}),G=new dt(m.store),Z=new Hr(G,[new Dt,new qr(Fd(process.cwd(),".ts-cloud","volumes"))]);return{controlPlane:m,environmentRecord:L,actor:j,store:G,service:Z}}function Df(N){return N.map((t)=>[t.name,`${t.provider}/${t.type}`,t.status,t.capacityBytes==null?"—":ys(t.capacityBytes),t.usedBytes==null?"—":ys(t.usedBytes),String(t.attachments.filter((m)=>m.observedState==="attached").length),t.backupState,t.orphaned?"orphan":"managed"])}function qc(N){N.command("storage:list","List all S3 buckets").option("--region <region>","AWS region").action(async(m)=>{i("S3 Buckets");try{let A=await Ne(),L=m.region||A.project.region||"us-east-1",j=new g(L),G=new o("Fetching buckets...");G.start();let ee=(await j.listBuckets()).Buckets||[];if(G.succeed(`Found ${ee.length} bucket(s)`),ee.length===0){e("No S3 buckets found"),e("Use `cloud storage:create` to create a new bucket");return}c(["Name","Created"],ee.map((ne)=>[ne.Name||"N/A",ne.CreationDate?new Date(ne.CreationDate).toLocaleDateString():"N/A"]))}catch(A){r(`Failed to list buckets: ${A.message}`),process.exit(1)}}),N.command("storage:create <name>","Create a new S3 bucket").option("--region <region>","AWS region",{default:"us-east-1"}).option("--public","Enable public access").option("--versioning","Enable versioning").action(async(m,A)=>{i("Create S3 Bucket");try{let L=new g(A.region);if(e(`Bucket name: ${m}`),e(`Region: ${A.region}`),e(`Public access: ${A.public?"Yes":"No"}`),e(`Versioning: ${A.versioning?"Yes":"No"}`),!await d(`
492
+ Create this bucket?`,!0)){e("Operation cancelled");return}let G=new o("Creating bucket...");if(G.start(),await L.createBucket(m),!A.public)G.text="Configuring public access block...",await L.putPublicAccessBlock(m,{BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!0,RestrictPublicBuckets:!0});if(A.versioning)G.text="Enabling versioning...",await L.putBucketVersioning(m,"Enabled");G.succeed("Bucket created"),n(`
493
+ Bucket: ${m}`),e(`Region: ${A.region}`),e(`URL: s3://${m}`)}catch(L){r(`Failed to create bucket: ${L.message}`),process.exit(1)}}),N.command("storage:delete <name>","Delete an S3 bucket").option("--force","Delete all objects first").action(async(m,A)=>{i("Delete S3 Bucket");try{let L=new g("us-east-1");if(a(`This will permanently delete bucket: ${m}`),A.force)a("All objects in the bucket will be deleted!");if(!await d(`
494
+ Delete this bucket?`,!1)){e("Operation cancelled");return}let G=new o("Deleting bucket...");if(G.start(),A.force)G.text="Emptying bucket...",await L.emptyBucket(m);await L.deleteBucket(m),G.succeed("Bucket deleted")}catch(L){r(`Failed to delete bucket: ${L.message}`),process.exit(1)}}),N.command("storage:sync <source> <bucket>","Sync local directory to S3 bucket").option("--prefix <prefix>","S3 key prefix").option("--delete","Delete files in S3 that are not in source").option("--dry-run","Show what would be synced without making changes").action(async(m,A,L)=>{i("Sync to S3");try{let ee=function(we,Ce){let De=If(we,{withFileTypes:!0});for(let Se of De){let xe=Fd(we,Se.name);if(Se.isDirectory())ee(xe,Ce);else{let Me=xf(Ce,xe),Ae=L.prefix?`${L.prefix}/${Me}`:Me,Ee=Pf(xe);Z.push({path:xe,key:Ae,size:Ee.size})}}},j=new g("us-east-1");if(e(`Source: ${m}`),e(`Destination: s3://${A}/${L.prefix||""}`),L.dryRun)e("Dry run mode - no changes will be made");let G=new o("Scanning files...");G.start();let Z=[];if(ee(m,m),G.succeed(`Found ${Z.length} local file(s)`),Z.length===0){e("No files to sync");return}e(`
495
495
  Files to sync:`);for(let we of Z.slice(0,10))e(` ${we.key} (${(we.size/1024).toFixed(2)} KB)`);if(Z.length>10)e(` ... and ${Z.length-10} more`);if(L.dryRun){e(`
496
496
  Dry run complete - no changes made`);return}if(!await d(`
497
- Sync these files?`,!0)){e("Operation cancelled");return}let oe=new o("Uploading files...");oe.start();let be=0;for(let we of Z){oe.text=`Uploading ${we.key}... (${be+1}/${Z.length})`;let Ee=await Bun.file(we.path).arrayBuffer();await j.putObject({bucket:A,key:we.key,body:Buffer.from(Ee),contentType:wf(we.key)}),be++}if(oe.succeed(`Uploaded ${be} file(s)`),L.delete){let we=new o("Checking for files to delete...");we.start();let Ce=await j.listObjects({bucket:A,prefix:L.prefix}),Ee=new Set(Z.map((De)=>De.key)),Se=Ce.objects.filter((De)=>De.Key&&!Ee.has(De.Key)).map((De)=>De.Key);if(Se.length>0){we.text=`Deleting ${Se.length} remote file(s)...`;for(let De of Se)await j.deleteObject(A,De);we.succeed(`Deleted ${Se.length} remote file(s)`)}else we.succeed("No files to delete")}n(`
498
- Sync complete!`)}catch(j){r(`Failed to sync: ${j.message}`),process.exit(1)}}),N.command("storage:policy <bucket>","Show or set bucket policy").option("--set <file>","Set policy from JSON file").option("--public-read","Set a public read policy").option("--delete","Delete the bucket policy").action(async(p,A)=>{i("S3 Bucket Policy");try{let L=new f("us-east-1");if(A.delete){if(a(`This will delete the policy for bucket: ${p}`),!await d(`
499
- Delete bucket policy?`,!1)){e("Operation cancelled");return}let Z=new o("Deleting policy...");Z.start(),await L.deleteBucketPolicy(p),Z.succeed("Policy deleted");return}if(A.publicRead){if(a(`This will make bucket ${p} publicly readable!`),!await d(`
500
- Set public read policy?`,!1)){e("Operation cancelled");return}let Z=new o("Setting policy...");Z.start();let ee={Version:"2012-10-17",Statement:[{Sid:"PublicReadGetObject",Effect:"Allow",Principal:"*",Action:"s3:GetObject",Resource:`arn:aws:s3:::${p}/*`}]};await L.putBucketPolicy(p,ee),Z.succeed("Public read policy set");return}if(A.set){let G=new o("Setting policy...");G.start();let ee=await Bun.file(A.set).text();await L.putBucketPolicy(p,ee),G.succeed("Policy set");return}let j=new o("Fetching policy...");j.start();try{let G=await L.getBucketPolicy(p);j.succeed("Policy loaded"),e(`
501
- Bucket Policy:`),console.log(JSON.stringify(G||{},null,2))}catch(G){if(G.message?.includes("NoSuchBucketPolicy"))j.succeed("No policy set"),e("This bucket has no policy configured.");else throw G}}catch(L){r(`Failed to manage policy: ${L.message}`),process.exit(1)}}),N.command("storage:ls <bucket>","List objects in a bucket").option("--prefix <prefix>","Filter by prefix").option("--limit <number>","Limit number of results",{default:"100"}).action(async(p,A)=>{i(`Objects in ${p}`);try{let L=new f("us-east-1"),j=new o("Listing objects...");j.start();let G=await L.listObjects({bucket:p,prefix:A.prefix,maxKeys:Number.parseInt(A.limit||"100")}),Z=G.objects;if(j.succeed(`Found ${Z.length} object(s)${G.nextContinuationToken?" (truncated)":""}`),Z.length===0){e("No objects found");return}if(c(["Key","Size","Last Modified"],Z.map((ee)=>[ee.Key||"N/A",ps(ee.Size||0),ee.LastModified?new Date(ee.LastModified).toLocaleString():"N/A"])),G.nextContinuationToken)e(`
502
- More objects available. Use --limit to see more.`)}catch(L){r(`Failed to list objects: ${L.message}`),process.exit(1)}});let t=(p,A)=>N.command(p,A).option("--env <environment>","Dashboard environment");t("volume:list","List persistent volumes, consumers, usage, and protection").option("--json","Print structured JSON").action(async(p)=>{let A=await ai(p.env);try{let L=A.store.inventory(A.controlPlane.project.id,A.environmentRecord.id);if(p.json)console.log(JSON.stringify(L,null,2));else c(["Name","Driver","Status","Capacity","Used","Consumers","Backup","Ownership"],bf(L))}catch(L){r(L instanceof Error?L.message:String(L))}finally{A.controlPlane.store.close()}}),t("volume:show <name>","Inspect a persistent volume and its dependency graph").option("--json","Print structured JSON").action(async(p,A)=>{let L=await ai(A.env);try{let j=L.store.inventory(L.controlPlane.project.id,L.environmentRecord.id).find((G)=>G.id===p||G.name===p);if(!j)throw Error(`Volume ${p} was not found.`);if(A.json)console.log(JSON.stringify(j,null,2));else c(["Property","Value"],[["ID",j.id],["Driver",`${j.provider}/${j.type}`],["Provider ID",j.providerId??"—"],["Status",j.status],["Capacity",j.capacityBytes==null?"—":ps(j.capacityBytes)],["Used",j.usedBytes==null?"—":ps(j.usedBytes)],["Backup",j.backupState]]),c(["Resource","Target","Mode","State"],j.attachments.map((G)=>[G.resourceId,G.targetPath,G.readOnly?"read only":"read/write",G.observedState]))}catch(j){r(j instanceof Error?j.message:String(j))}finally{L.controlPlane.store.close()}}),t("volume:create <name>","Create a stable named or managed-path volume").option("--driver <driver>","docker or server",{default:"docker"}).option("--capacity-bytes <bytes>","Advisory capacity in bytes").option("--encrypted","Request provider encryption").action(async(p,A)=>{let L=await ai(A.env);try{let j=A.driver==="server";if(!j&&A.driver!=="docker")throw Error("--driver must be docker or server.");let G=L.service.create({organizationId:L.controlPlane.organization.id,projectId:L.controlPlane.project.id,environmentId:L.environmentRecord.id,name:p,provider:j?"server":"docker",type:j?"server_path":"docker",capacityBytes:A.capacityBytes?Number(A.capacityBytes):void 0,encrypted:!!A.encrypted,actorId:L.actor.id});n(`Volume create queued: ${G.operation.id}`)}catch(j){r(j instanceof Error?j.message:String(j))}finally{L.controlPlane.store.close()}}),t("volume:attach <volume> <resource> <target>","Validate and attach a volume to a workload").option("--read-only","Mount read only").option("--uid <uid>","Filesystem UID").option("--gid <gid>","Filesystem GID").option("--mode <mode>","Octal mode").action(async(p,A,L,j)=>{let G=await ai(j.env);try{let Z=G.store.list({projectId:G.controlPlane.project.id,environmentId:G.environmentRecord.id}).find((oe)=>oe.id===p||oe.name===p),ee=G.controlPlane.store.listResources(G.controlPlane.project.id,G.environmentRecord.id).find((oe)=>oe.id===A||oe.slug===A);if(!Z||!ee)throw Error("Volume or resource was not found in the selected environment.");let re=G.service.attach(Z.id,{resourceId:ee.id,targetPath:L,readOnly:!!j.readOnly,uid:j.uid?Number(j.uid):void 0,gid:j.gid?Number(j.gid):void 0,mode:j.mode,actorId:G.actor.id});n(`Volume attachment queued: ${re.operation.id}`)}catch(Z){r(Z instanceof Error?Z.message:String(Z))}finally{G.controlPlane.store.close()}}),t("volume:detach <volume> <attachment>","Drain, unmount, and detach a volume consumer").option("--force","Force after exact confirmation").option("--confirm <text>","Exact force-detach confirmation").action(async(p,A,L)=>{let j=await ai(L.env);try{let G=j.store.list({projectId:j.controlPlane.project.id,environmentId:j.environmentRecord.id}).find((ee)=>ee.id===p||ee.name===p);if(!G)throw Error("Volume was not found.");let Z=j.service.detach(G.id,A,{drained:!0,unmounted:!0,force:!!L.force,confirmation:L.confirm,actorId:j.actor.id});n(`Volume detach queued: ${Z.operation.id}`)}catch(G){r(G instanceof Error?G.message:String(G))}finally{j.controlPlane.store.close()}}),t("volume:resize <volume> <bytes>","Grow a capable persistent volume").option("--drained","Confirm offline workload drain").action(async(p,A,L)=>{let j=await ai(L.env);try{let G=j.store.list({projectId:j.controlPlane.project.id,environmentId:j.environmentRecord.id}).find((ee)=>ee.id===p||ee.name===p);if(!G)throw Error("Volume was not found.");let Z=j.service.resize(G.id,Number(A),{drained:!!L.drained,actorId:j.actor.id});n(`Volume resize queued: ${Z.operation.id}`)}catch(G){r(G instanceof Error?G.message:String(G))}finally{j.controlPlane.store.close()}}),t("volume:snapshot <volume>","Create a provider snapshot when supported").action(async(p,A)=>{let L=await ai(A.env);try{let j=L.store.list({projectId:L.controlPlane.project.id,environmentId:L.environmentRecord.id}).find((Z)=>Z.id===p||Z.name===p);if(!j)throw Error("Volume was not found.");let G=L.service.snapshot(j.id,{actorId:L.actor.id});n(`Volume snapshot queued: ${G.operation.id}`)}catch(j){r(j instanceof Error?j.message:String(j))}finally{L.controlPlane.store.close()}}),t("volume:restore <snapshot> <name>","Restore a snapshot into a replacement volume").action(async(p,A,L)=>{let j=await ai(L.env);try{let G=j.service.restore(p,{name:A,actorId:j.actor.id});n(`Volume restore queued: ${G.operation.id}`)}catch(G){r(G instanceof Error?G.message:String(G))}finally{j.controlPlane.store.close()}}),t("volume:discover","Discover provider volumes without adopting or deleting them").action(async(p)=>{let A=await ai(p.env);try{let L=await A.service.discover({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,environmentId:A.environmentRecord.id,actorId:A.actor.id});n(`Discovered ${L.filter((j)=>j.status==="orphaned").length} orphan volume(s).`)}catch(L){r(L instanceof Error?L.message:String(L))}finally{A.controlPlane.store.close()}}),t("volume:adopt <volume> <name>","Adopt a discovered orphan under a managed name").action(async(p,A,L)=>{let j=await ai(L.env);try{j.service.adopt(p,{name:A,actorId:j.actor.id}),n(`Adopted ${A}.`)}catch(G){r(G instanceof Error?G.message:String(G))}finally{j.controlPlane.store.close()}}),t("volume:delete <volume>","Permanently delete a detached volume").option("--confirm <name>","Exact volume name").option("--without-backup","Explicitly override the recent-backup gate").option("--backup-confirm <text>","Exact backup override confirmation").action(async(p,A)=>{let L=await ai(A.env);try{let j=L.store.list({projectId:L.controlPlane.project.id,environmentId:L.environmentRecord.id}).find((Z)=>Z.id===p||Z.name===p);if(!j)throw Error("Volume was not found.");let G=L.service.delete(j.id,{recentAuthAt:new Date().toISOString(),confirmation:A.confirm,backupOverride:!!A.withoutBackup,backupOverrideConfirmation:A.backupConfirm,actorId:L.actor.id});n(`Volume deletion queued: ${G.operation.id}`)}catch(j){r(j instanceof Error?j.message:String(j))}finally{L.controlPlane.store.close()}})}function wf(N){let t=N.split(".").pop()?.toLowerCase();return{html:"text/html",css:"text/css",js:"application/javascript",json:"application/json",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",svg:"image/svg+xml",ico:"image/x-icon",woff:"font/woff",woff2:"font/woff2",ttf:"font/ttf",eot:"application/vnd.ms-fontobject",pdf:"application/pdf",zip:"application/zip",xml:"application/xml",txt:"text/plain",md:"text/markdown"}[t||""]||"application/octet-stream"}function ps(N){if(N===0)return"0 B";let t=1024,p=["B","KB","MB","GB","TB"],A=Math.floor(Math.log(N)/Math.log(t));return`${Number.parseFloat((N/t**A).toFixed(2))} ${p[A]}`}var Pd=["aws","backblaze","hetzner"];function xd(N,t){let p=N.indexOf(":");if(p===-1)throw Error(`--${t} must be in the form <provider:bucket> (e.g. aws:my-bucket), got "${N}"`);let A=N.slice(0,p),L=N.slice(p+1);if(!Pd.includes(A))throw Error(`Unknown provider "${A}" for --${t}. Supported: ${Pd.join(", ")}`);if(!L)throw Error(`--${t} is missing a bucket name (expected <provider:bucket>)`);return{provider:A,bucket:L}}function Dd(N){if(!N)return;let t=N.split(",").map((p)=>p.trim()).filter(Boolean);return t.length>0?t:void 0}function jc(N){N.command("migrate:storage","Migrate object-storage data between providers (AWS S3 ↔ Hetzner ↔ Backblaze)").option("--from <provider:bucket>","Source endpoint, e.g. aws:stacks-production-email").option("--to <provider:bucket>","Destination endpoint, e.g. hetzner:stacks-mail").option("--from-region <region>","Source region/location (default: provider default)").option("--to-region <region>","Destination region/location (default: provider default)").option("--from-endpoint <host>","Source endpoint host override (no scheme)").option("--to-endpoint <host>","Destination endpoint host override (no scheme)").option("--from-prefix <prefix>","Only read source keys under this prefix (stripped from dest key)").option("--to-prefix <prefix>","Prepend this prefix to every destination key").option("--include <csv>","Only copy keys under these comma-separated prefixes").option("--exclude <csv>","Skip keys under these comma-separated prefixes").option("--dry-run","Print the plan (what would copy / what is excluded) without writing").option("--force","Re-copy even if the destination already has an object of the same size").option("--delete-extraneous","Delete destination keys not present in the source (default OFF)").option("--concurrency <n>","Max concurrent copies",{default:"8"}).option("--verify","After copying, re-list the destination and assert object count + sizes match").action(async(t)=>{i("Migrate Object Storage");try{if(!t.from||!t.to)throw Error("Both --from <provider:bucket> and --to <provider:bucket> are required");let p=xd(t.from,"from"),A=xd(t.to,"to"),L=Dd(t.include),j=Dd(t.exclude),G=Number.parseInt(t.concurrency||"8",10),Z={provider:p.provider,bucket:p.bucket,region:t.fromRegion,endpoint:t.fromEndpoint,prefix:t.fromPrefix},ee={provider:A.provider,bucket:A.bucket,region:t.toRegion,endpoint:t.toEndpoint,prefix:t.toPrefix};if(e(`Source: ${p.provider}:${p.bucket}${t.fromPrefix?`/${t.fromPrefix}`:""}`),e(`Destination: ${A.provider}:${A.bucket}${t.toPrefix?`/${t.toPrefix}`:""}`),L)e(`Include: ${L.join(", ")}`);if(j)e(`Exclude: ${j.join(", ")}`);if(e(`Concurrency: ${G}`),t.dryRun)a("Dry run — no objects will be written");if(t.deleteExtraneous&&!t.dryRun)a("Delete-extraneous ON — destination keys not in the source will be removed");let re=new o("Listing source objects...");re.start();let oe=0,be=await Oo({from:Z,to:ee,include:L,exclude:j,dryRun:t.dryRun,force:t.force,deleteExtraneous:t.deleteExtraneous,concurrency:G,verify:t.verify,onProgress:(we)=>{if(we.index-oe>=1||we.index===we.total){oe=we.index;let Ce=we.action==="planned"?"plan":we.action;re.text=`[${we.index}/${we.total}] ${Ce}: ${we.key}`}}});if(re.succeed(t.dryRun?"Plan ready":"Migration complete"),t.dryRun&&be.plan){e(`
503
- Would copy ${be.plan.length} object(s):`);for(let we of be.plan.slice(0,50))e(` ${we.key} -> ${we.destKey} (${Yr(we.size)})`);if(be.plan.length>50)e(` ... and ${be.plan.length-50} more`)}if(be.excludedKeys.length>0){e(`
497
+ Sync these files?`,!0)){e("Operation cancelled");return}let oe=new o("Uploading files...");oe.start();let be=0;for(let we of Z){oe.text=`Uploading ${we.key}... (${be+1}/${Z.length})`;let De=await Bun.file(we.path).arrayBuffer();await j.putObject({bucket:A,key:we.key,body:Buffer.from(De),contentType:Ef(we.key)}),be++}if(oe.succeed(`Uploaded ${be} file(s)`),L.delete){let we=new o("Checking for files to delete...");we.start();let Ce=await j.listObjects({bucket:A,prefix:L.prefix}),De=new Set(Z.map((xe)=>xe.key)),Se=Ce.objects.filter((xe)=>xe.Key&&!De.has(xe.Key)).map((xe)=>xe.Key);if(Se.length>0){we.text=`Deleting ${Se.length} remote file(s)...`;for(let xe of Se)await j.deleteObject(A,xe);we.succeed(`Deleted ${Se.length} remote file(s)`)}else we.succeed("No files to delete")}n(`
498
+ Sync complete!`)}catch(j){r(`Failed to sync: ${j.message}`),process.exit(1)}}),N.command("storage:policy <bucket>","Show or set bucket policy").option("--set <file>","Set policy from JSON file").option("--public-read","Set a public read policy").option("--delete","Delete the bucket policy").action(async(m,A)=>{i("S3 Bucket Policy");try{let L=new g("us-east-1");if(A.delete){if(a(`This will delete the policy for bucket: ${m}`),!await d(`
499
+ Delete bucket policy?`,!1)){e("Operation cancelled");return}let Z=new o("Deleting policy...");Z.start(),await L.deleteBucketPolicy(m),Z.succeed("Policy deleted");return}if(A.publicRead){if(a(`This will make bucket ${m} publicly readable!`),!await d(`
500
+ Set public read policy?`,!1)){e("Operation cancelled");return}let Z=new o("Setting policy...");Z.start();let ee={Version:"2012-10-17",Statement:[{Sid:"PublicReadGetObject",Effect:"Allow",Principal:"*",Action:"s3:GetObject",Resource:`arn:aws:s3:::${m}/*`}]};await L.putBucketPolicy(m,ee),Z.succeed("Public read policy set");return}if(A.set){let G=new o("Setting policy...");G.start();let ee=await Bun.file(A.set).text();await L.putBucketPolicy(m,ee),G.succeed("Policy set");return}let j=new o("Fetching policy...");j.start();try{let G=await L.getBucketPolicy(m);j.succeed("Policy loaded"),e(`
501
+ Bucket Policy:`),console.log(JSON.stringify(G||{},null,2))}catch(G){if(G.message?.includes("NoSuchBucketPolicy"))j.succeed("No policy set"),e("This bucket has no policy configured.");else throw G}}catch(L){r(`Failed to manage policy: ${L.message}`),process.exit(1)}}),N.command("storage:ls <bucket>","List objects in a bucket").option("--prefix <prefix>","Filter by prefix").option("--limit <number>","Limit number of results",{default:"100"}).action(async(m,A)=>{i(`Objects in ${m}`);try{let L=new g("us-east-1"),j=new o("Listing objects...");j.start();let G=await L.listObjects({bucket:m,prefix:A.prefix,maxKeys:Number.parseInt(A.limit||"100")}),Z=G.objects;if(j.succeed(`Found ${Z.length} object(s)${G.nextContinuationToken?" (truncated)":""}`),Z.length===0){e("No objects found");return}if(c(["Key","Size","Last Modified"],Z.map((ee)=>[ee.Key||"N/A",ys(ee.Size||0),ee.LastModified?new Date(ee.LastModified).toLocaleString():"N/A"])),G.nextContinuationToken)e(`
502
+ More objects available. Use --limit to see more.`)}catch(L){r(`Failed to list objects: ${L.message}`),process.exit(1)}});let t=(m,A)=>N.command(m,A).option("--env <environment>","Dashboard environment");t("volume:list","List persistent volumes, consumers, usage, and protection").option("--json","Print structured JSON").action(async(m)=>{let A=await ui(m.env);try{let L=A.store.inventory(A.controlPlane.project.id,A.environmentRecord.id);if(m.json)console.log(JSON.stringify(L,null,2));else c(["Name","Driver","Status","Capacity","Used","Consumers","Backup","Ownership"],Df(L))}catch(L){r(L instanceof Error?L.message:String(L))}finally{A.controlPlane.store.close()}}),t("volume:show <name>","Inspect a persistent volume and its dependency graph").option("--json","Print structured JSON").action(async(m,A)=>{let L=await ui(A.env);try{let j=L.store.inventory(L.controlPlane.project.id,L.environmentRecord.id).find((G)=>G.id===m||G.name===m);if(!j)throw Error(`Volume ${m} was not found.`);if(A.json)console.log(JSON.stringify(j,null,2));else c(["Property","Value"],[["ID",j.id],["Driver",`${j.provider}/${j.type}`],["Provider ID",j.providerId??"—"],["Status",j.status],["Capacity",j.capacityBytes==null?"—":ys(j.capacityBytes)],["Used",j.usedBytes==null?"—":ys(j.usedBytes)],["Backup",j.backupState]]),c(["Resource","Target","Mode","State"],j.attachments.map((G)=>[G.resourceId,G.targetPath,G.readOnly?"read only":"read/write",G.observedState]))}catch(j){r(j instanceof Error?j.message:String(j))}finally{L.controlPlane.store.close()}}),t("volume:create <name>","Create a stable named or managed-path volume").option("--driver <driver>","docker or server",{default:"docker"}).option("--capacity-bytes <bytes>","Advisory capacity in bytes").option("--encrypted","Request provider encryption").action(async(m,A)=>{let L=await ui(A.env);try{let j=A.driver==="server";if(!j&&A.driver!=="docker")throw Error("--driver must be docker or server.");let G=L.service.create({organizationId:L.controlPlane.organization.id,projectId:L.controlPlane.project.id,environmentId:L.environmentRecord.id,name:m,provider:j?"server":"docker",type:j?"server_path":"docker",capacityBytes:A.capacityBytes?Number(A.capacityBytes):void 0,encrypted:!!A.encrypted,actorId:L.actor.id});n(`Volume create queued: ${G.operation.id}`)}catch(j){r(j instanceof Error?j.message:String(j))}finally{L.controlPlane.store.close()}}),t("volume:attach <volume> <resource> <target>","Validate and attach a volume to a workload").option("--read-only","Mount read only").option("--uid <uid>","Filesystem UID").option("--gid <gid>","Filesystem GID").option("--mode <mode>","Octal mode").action(async(m,A,L,j)=>{let G=await ui(j.env);try{let Z=G.store.list({projectId:G.controlPlane.project.id,environmentId:G.environmentRecord.id}).find((oe)=>oe.id===m||oe.name===m),ee=G.controlPlane.store.listResources(G.controlPlane.project.id,G.environmentRecord.id).find((oe)=>oe.id===A||oe.slug===A);if(!Z||!ee)throw Error("Volume or resource was not found in the selected environment.");let ne=G.service.attach(Z.id,{resourceId:ee.id,targetPath:L,readOnly:!!j.readOnly,uid:j.uid?Number(j.uid):void 0,gid:j.gid?Number(j.gid):void 0,mode:j.mode,actorId:G.actor.id});n(`Volume attachment queued: ${ne.operation.id}`)}catch(Z){r(Z instanceof Error?Z.message:String(Z))}finally{G.controlPlane.store.close()}}),t("volume:detach <volume> <attachment>","Drain, unmount, and detach a volume consumer").option("--force","Force after exact confirmation").option("--confirm <text>","Exact force-detach confirmation").action(async(m,A,L)=>{let j=await ui(L.env);try{let G=j.store.list({projectId:j.controlPlane.project.id,environmentId:j.environmentRecord.id}).find((ee)=>ee.id===m||ee.name===m);if(!G)throw Error("Volume was not found.");let Z=j.service.detach(G.id,A,{drained:!0,unmounted:!0,force:!!L.force,confirmation:L.confirm,actorId:j.actor.id});n(`Volume detach queued: ${Z.operation.id}`)}catch(G){r(G instanceof Error?G.message:String(G))}finally{j.controlPlane.store.close()}}),t("volume:resize <volume> <bytes>","Grow a capable persistent volume").option("--drained","Confirm offline workload drain").action(async(m,A,L)=>{let j=await ui(L.env);try{let G=j.store.list({projectId:j.controlPlane.project.id,environmentId:j.environmentRecord.id}).find((ee)=>ee.id===m||ee.name===m);if(!G)throw Error("Volume was not found.");let Z=j.service.resize(G.id,Number(A),{drained:!!L.drained,actorId:j.actor.id});n(`Volume resize queued: ${Z.operation.id}`)}catch(G){r(G instanceof Error?G.message:String(G))}finally{j.controlPlane.store.close()}}),t("volume:snapshot <volume>","Create a provider snapshot when supported").action(async(m,A)=>{let L=await ui(A.env);try{let j=L.store.list({projectId:L.controlPlane.project.id,environmentId:L.environmentRecord.id}).find((Z)=>Z.id===m||Z.name===m);if(!j)throw Error("Volume was not found.");let G=L.service.snapshot(j.id,{actorId:L.actor.id});n(`Volume snapshot queued: ${G.operation.id}`)}catch(j){r(j instanceof Error?j.message:String(j))}finally{L.controlPlane.store.close()}}),t("volume:restore <snapshot> <name>","Restore a snapshot into a replacement volume").action(async(m,A,L)=>{let j=await ui(L.env);try{let G=j.service.restore(m,{name:A,actorId:j.actor.id});n(`Volume restore queued: ${G.operation.id}`)}catch(G){r(G instanceof Error?G.message:String(G))}finally{j.controlPlane.store.close()}}),t("volume:discover","Discover provider volumes without adopting or deleting them").action(async(m)=>{let A=await ui(m.env);try{let L=await A.service.discover({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,environmentId:A.environmentRecord.id,actorId:A.actor.id});n(`Discovered ${L.filter((j)=>j.status==="orphaned").length} orphan volume(s).`)}catch(L){r(L instanceof Error?L.message:String(L))}finally{A.controlPlane.store.close()}}),t("volume:adopt <volume> <name>","Adopt a discovered orphan under a managed name").action(async(m,A,L)=>{let j=await ui(L.env);try{j.service.adopt(m,{name:A,actorId:j.actor.id}),n(`Adopted ${A}.`)}catch(G){r(G instanceof Error?G.message:String(G))}finally{j.controlPlane.store.close()}}),t("volume:delete <volume>","Permanently delete a detached volume").option("--confirm <name>","Exact volume name").option("--without-backup","Explicitly override the recent-backup gate").option("--backup-confirm <text>","Exact backup override confirmation").action(async(m,A)=>{let L=await ui(A.env);try{let j=L.store.list({projectId:L.controlPlane.project.id,environmentId:L.environmentRecord.id}).find((Z)=>Z.id===m||Z.name===m);if(!j)throw Error("Volume was not found.");let G=L.service.delete(j.id,{recentAuthAt:new Date().toISOString(),confirmation:A.confirm,backupOverride:!!A.withoutBackup,backupOverrideConfirmation:A.backupConfirm,actorId:L.actor.id});n(`Volume deletion queued: ${G.operation.id}`)}catch(j){r(j instanceof Error?j.message:String(j))}finally{L.controlPlane.store.close()}})}function Ef(N){let t=N.split(".").pop()?.toLowerCase();return{html:"text/html",css:"text/css",js:"application/javascript",json:"application/json",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",svg:"image/svg+xml",ico:"image/x-icon",woff:"font/woff",woff2:"font/woff2",ttf:"font/ttf",eot:"application/vnd.ms-fontobject",pdf:"application/pdf",zip:"application/zip",xml:"application/xml",txt:"text/plain",md:"text/markdown"}[t||""]||"application/octet-stream"}function ys(N){if(N===0)return"0 B";let t=1024,m=["B","KB","MB","GB","TB"],A=Math.floor(Math.log(N)/Math.log(t));return`${Number.parseFloat((N/t**A).toFixed(2))} ${m[A]}`}var Bd=["aws","backblaze","hetzner"];function Md(N,t){let m=N.indexOf(":");if(m===-1)throw Error(`--${t} must be in the form <provider:bucket> (e.g. aws:my-bucket), got "${N}"`);let A=N.slice(0,m),L=N.slice(m+1);if(!Bd.includes(A))throw Error(`Unknown provider "${A}" for --${t}. Supported: ${Bd.join(", ")}`);if(!L)throw Error(`--${t} is missing a bucket name (expected <provider:bucket>)`);return{provider:A,bucket:L}}function _d(N){if(!N)return;let t=N.split(",").map((m)=>m.trim()).filter(Boolean);return t.length>0?t:void 0}function Gc(N){N.command("migrate:storage","Migrate object-storage data between providers (AWS S3 ↔ Hetzner ↔ Backblaze)").option("--from <provider:bucket>","Source endpoint, e.g. aws:stacks-production-email").option("--to <provider:bucket>","Destination endpoint, e.g. hetzner:stacks-mail").option("--from-region <region>","Source region/location (default: provider default)").option("--to-region <region>","Destination region/location (default: provider default)").option("--from-endpoint <host>","Source endpoint host override (no scheme)").option("--to-endpoint <host>","Destination endpoint host override (no scheme)").option("--from-prefix <prefix>","Only read source keys under this prefix (stripped from dest key)").option("--to-prefix <prefix>","Prepend this prefix to every destination key").option("--include <csv>","Only copy keys under these comma-separated prefixes").option("--exclude <csv>","Skip keys under these comma-separated prefixes").option("--dry-run","Print the plan (what would copy / what is excluded) without writing").option("--force","Re-copy even if the destination already has an object of the same size").option("--delete-extraneous","Delete destination keys not present in the source (default OFF)").option("--concurrency <n>","Max concurrent copies",{default:"8"}).option("--verify","After copying, re-list the destination and assert object count + sizes match").action(async(t)=>{i("Migrate Object Storage");try{if(!t.from||!t.to)throw Error("Both --from <provider:bucket> and --to <provider:bucket> are required");let m=Md(t.from,"from"),A=Md(t.to,"to"),L=_d(t.include),j=_d(t.exclude),G=Number.parseInt(t.concurrency||"8",10),Z={provider:m.provider,bucket:m.bucket,region:t.fromRegion,endpoint:t.fromEndpoint,prefix:t.fromPrefix},ee={provider:A.provider,bucket:A.bucket,region:t.toRegion,endpoint:t.toEndpoint,prefix:t.toPrefix};if(e(`Source: ${m.provider}:${m.bucket}${t.fromPrefix?`/${t.fromPrefix}`:""}`),e(`Destination: ${A.provider}:${A.bucket}${t.toPrefix?`/${t.toPrefix}`:""}`),L)e(`Include: ${L.join(", ")}`);if(j)e(`Exclude: ${j.join(", ")}`);if(e(`Concurrency: ${G}`),t.dryRun)a("Dry run — no objects will be written");if(t.deleteExtraneous&&!t.dryRun)a("Delete-extraneous ON — destination keys not in the source will be removed");let ne=new o("Listing source objects...");ne.start();let oe=0,be=await Fo({from:Z,to:ee,include:L,exclude:j,dryRun:t.dryRun,force:t.force,deleteExtraneous:t.deleteExtraneous,concurrency:G,verify:t.verify,onProgress:(we)=>{if(we.index-oe>=1||we.index===we.total){oe=we.index;let Ce=we.action==="planned"?"plan":we.action;ne.text=`[${we.index}/${we.total}] ${Ce}: ${we.key}`}}});if(ne.succeed(t.dryRun?"Plan ready":"Migration complete"),t.dryRun&&be.plan){e(`
503
+ Would copy ${be.plan.length} object(s):`);for(let we of be.plan.slice(0,50))e(` ${we.key} -> ${we.destKey} (${Xr(we.size)})`);if(be.plan.length>50)e(` ... and ${be.plan.length-50} more`)}if(be.excludedKeys.length>0){e(`
504
504
  EXCLUDED ${be.excludedKeys.length} key(s) (deliberately not migrated):`);for(let we of be.excludedKeys.slice(0,50))e(` ${we}`);if(be.excludedKeys.length>50)e(` ... and ${be.excludedKeys.length-50} more`)}if(be.deleted.length>0){a(`
505
505
  Deleted ${be.deleted.length} extraneous destination key(s):`);for(let we of be.deleted.slice(0,50))a(` ${we}`)}if(e(`
506
- Summary:`),t.dryRun)e(` would copy: ${be.plan?.length??0}`);else n(` copied: ${be.copied} (${Yr(be.bytesCopied)})`),e(` skipped: ${be.skipped} (already present)`);if(e(` excluded: ${be.excluded}`),be.deleted.length>0)e(` deleted: ${be.deleted.length}`);if(be.errors.length>0){r(` errors: ${be.errors.length}`);for(let we of be.errors.slice(0,20))r(` ${we.key}: ${we.message}`)}if(be.verification){let we=be.verification;if(we.ok)n(`
506
+ Summary:`),t.dryRun)e(` would copy: ${be.plan?.length??0}`);else n(` copied: ${be.copied} (${Xr(be.bytesCopied)})`),e(` skipped: ${be.skipped} (already present)`);if(e(` excluded: ${be.excluded}`),be.deleted.length>0)e(` deleted: ${be.deleted.length}`);if(be.errors.length>0){r(` errors: ${be.errors.length}`);for(let we of be.errors.slice(0,20))r(` ${we.key}: ${we.message}`)}if(be.verification){let we=be.verification;if(we.ok)n(`
507
507
  Verification PASSED — ${we.matched} object(s) present at destination with matching sizes`);else{r(`
508
- Verification FAILED — ${we.missing.length} missing, ${we.sizeMismatches.length} size mismatch(es)`);for(let Ce of we.missing.slice(0,20))r(` missing: ${Ce}`);for(let Ce of we.sizeMismatches.slice(0,20))r(` size: ${Ce.key} expected ${Ce.expected} got ${Ce.actual}`)}}if(be.errors.length>0||be.verification?.ok===!1)process.exit(1)}catch(p){r(`Migration failed: ${p.message}`),process.exit(1)}})}function Fc(N){N.command("cache:list","List all ElastiCache clusters").option("--region <region>","AWS region").action(async(t)=>{i("ElastiCache Clusters");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new te(A),j=new o("Fetching clusters...");j.start();let Z=(await L.describeCacheClusters()).CacheClusters||[];if(j.succeed(`Found ${Z.length} cluster(s)`),Z.length===0){e("No ElastiCache clusters found"),e("Use `cloud cache:create` to create a new cluster");return}c(["Cluster ID","Engine","Node Type","Nodes","Status"],Z.map((ee)=>[ee.CacheClusterId||"N/A",`${ee.Engine||"N/A"} ${ee.EngineVersion||""}`,ee.CacheNodeType||"N/A",(ee.NumCacheNodes||0).toString(),ee.CacheClusterStatus||"N/A"]))}catch(p){r(`Failed to list clusters: ${p.message}`),process.exit(1)}}),N.command("cache:create <name>","Create a new ElastiCache cluster").option("--engine <engine>","Cache engine (redis or memcached)",{default:"redis"}).option("--node-type <type>","Node instance type",{default:"cache.t3.micro"}).option("--nodes <number>","Number of cache nodes",{default:"1"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--version <version>","Engine version").action(async(t,p)=>{i("Create ElastiCache Cluster");try{let A=new te(p.region);if(e(`Cluster ID: ${t}`),e(`Engine: ${p.engine}`),e(`Node Type: ${p.nodeType}`),e(`Number of Nodes: ${p.nodes}`),e(`Region: ${p.region}`),!await d(`
509
- Create this cluster?`,!0)){e("Operation cancelled");return}let j=new o("Creating cluster...");j.start(),await A.createCacheCluster({cacheClusterId:t,engine:p.engine,cacheNodeType:p.nodeType,numCacheNodes:Number.parseInt(p.nodes),engineVersion:p.version}),j.succeed("Cluster creation initiated"),n(`
508
+ Verification FAILED — ${we.missing.length} missing, ${we.sizeMismatches.length} size mismatch(es)`);for(let Ce of we.missing.slice(0,20))r(` missing: ${Ce}`);for(let Ce of we.sizeMismatches.slice(0,20))r(` size: ${Ce.key} expected ${Ce.expected} got ${Ce.actual}`)}}if(be.errors.length>0||be.verification?.ok===!1)process.exit(1)}catch(m){r(`Migration failed: ${m.message}`),process.exit(1)}})}function Hc(N){N.command("cache:list","List all ElastiCache clusters").option("--region <region>","AWS region").action(async(t)=>{i("ElastiCache Clusters");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new te(A),j=new o("Fetching clusters...");j.start();let Z=(await L.describeCacheClusters()).CacheClusters||[];if(j.succeed(`Found ${Z.length} cluster(s)`),Z.length===0){e("No ElastiCache clusters found"),e("Use `cloud cache:create` to create a new cluster");return}c(["Cluster ID","Engine","Node Type","Nodes","Status"],Z.map((ee)=>[ee.CacheClusterId||"N/A",`${ee.Engine||"N/A"} ${ee.EngineVersion||""}`,ee.CacheNodeType||"N/A",(ee.NumCacheNodes||0).toString(),ee.CacheClusterStatus||"N/A"]))}catch(m){r(`Failed to list clusters: ${m.message}`),process.exit(1)}}),N.command("cache:create <name>","Create a new ElastiCache cluster").option("--engine <engine>","Cache engine (redis or memcached)",{default:"redis"}).option("--node-type <type>","Node instance type",{default:"cache.t3.micro"}).option("--nodes <number>","Number of cache nodes",{default:"1"}).option("--region <region>","AWS region",{default:"us-east-1"}).option("--version <version>","Engine version").action(async(t,m)=>{i("Create ElastiCache Cluster");try{let A=new te(m.region);if(e(`Cluster ID: ${t}`),e(`Engine: ${m.engine}`),e(`Node Type: ${m.nodeType}`),e(`Number of Nodes: ${m.nodes}`),e(`Region: ${m.region}`),!await d(`
509
+ Create this cluster?`,!0)){e("Operation cancelled");return}let j=new o("Creating cluster...");j.start(),await A.createCacheCluster({cacheClusterId:t,engine:m.engine,cacheNodeType:m.nodeType,numCacheNodes:Number.parseInt(m.nodes),engineVersion:m.version}),j.succeed("Cluster creation initiated"),n(`
510
510
  Cluster: ${t}`),e("Status: creating"),e(`
511
- Note: Cluster creation may take several minutes.`),e("Use `cloud cache:list` to check status.")}catch(A){r(`Failed to create cluster: ${A.message}`),process.exit(1)}}),N.command("cache:delete <clusterId>","Delete an ElastiCache cluster").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Delete ElastiCache Cluster");try{let A=new te(p.region);if(a(`This will permanently delete cluster: ${t}`),a("All data in the cluster will be lost!"),!await d(`
511
+ Note: Cluster creation may take several minutes.`),e("Use `cloud cache:list` to check status.")}catch(A){r(`Failed to create cluster: ${A.message}`),process.exit(1)}}),N.command("cache:delete <clusterId>","Delete an ElastiCache cluster").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Delete ElastiCache Cluster");try{let A=new te(m.region);if(a(`This will permanently delete cluster: ${t}`),a("All data in the cluster will be lost!"),!await d(`
512
512
  Delete this cluster?`,!1)){e("Operation cancelled");return}let j=new o("Deleting cluster...");j.start(),await A.deleteCacheCluster(t),j.succeed("Cluster deletion initiated"),e(`
513
- Note: Cluster deletion may take several minutes.`)}catch(A){r(`Failed to delete cluster: ${A.message}`),process.exit(1)}}),N.command("cache:stats <clusterId>","Show ElastiCache cluster statistics").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i(`ElastiCache Stats: ${t}`);try{let A=new te(p.region),L=new o("Fetching cluster details...");L.start();let G=(await A.describeCacheClusters(t)).CacheClusters?.[0];if(!G){L.fail("Cluster not found");return}if(L.succeed("Cluster details loaded"),e(`
513
+ Note: Cluster deletion may take several minutes.`)}catch(A){r(`Failed to delete cluster: ${A.message}`),process.exit(1)}}),N.command("cache:stats <clusterId>","Show ElastiCache cluster statistics").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i(`ElastiCache Stats: ${t}`);try{let A=new te(m.region),L=new o("Fetching cluster details...");L.start();let G=(await A.describeCacheClusters(t)).CacheClusters?.[0];if(!G){L.fail("Cluster not found");return}if(L.succeed("Cluster details loaded"),e(`
514
514
  Cluster Information:`),e(` Cluster ID: ${G.CacheClusterId}`),e(` Engine: ${G.Engine} ${G.EngineVersion}`),e(` Node Type: ${G.CacheNodeType}`),e(` Status: ${G.CacheClusterStatus}`),e(` Nodes: ${G.NumCacheNodes}`),e(` Availability Zone: ${G.PreferredAvailabilityZone||"Not set"}`),G.CacheNodes&&G.CacheNodes.length>0){e(`
515
- Cache Nodes:`);for(let Z of G.CacheNodes)if(e(` - ${Z.CacheNodeId}: ${Z.CacheNodeStatus}`),Z.Endpoint)e(` Endpoint: ${Z.Endpoint.Address}:${Z.Endpoint.Port}`)}}catch(A){r(`Failed to get cluster stats: ${A.message}`),process.exit(1)}}),N.command("cache:flush <clusterId>","Flush all data from a Redis cluster").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Flush ElastiCache Cluster");try{let A=new te(p.region),L=new o("Getting cluster endpoint...");L.start();let G=(await A.describeCacheClusters(t)).CacheClusters?.[0];if(!G){L.fail("Cluster not found");return}if(G.Engine!=="redis"){L.fail("Flush is only supported for Redis clusters"),e("For Memcached, items expire automatically based on TTL.");return}L.stop();let Z=G.CacheNodes?.[0]?.Endpoint;if(!Z){r("Could not determine cluster endpoint");return}if(a(`This will delete ALL data in cluster: ${t}`),a(`Endpoint: ${Z.Address}:${Z.Port}`),a("This action cannot be undone!"),!await d(`
515
+ Cache Nodes:`);for(let Z of G.CacheNodes)if(e(` - ${Z.CacheNodeId}: ${Z.CacheNodeStatus}`),Z.Endpoint)e(` Endpoint: ${Z.Endpoint.Address}:${Z.Endpoint.Port}`)}}catch(A){r(`Failed to get cluster stats: ${A.message}`),process.exit(1)}}),N.command("cache:flush <clusterId>","Flush all data from a Redis cluster").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Flush ElastiCache Cluster");try{let A=new te(m.region),L=new o("Getting cluster endpoint...");L.start();let G=(await A.describeCacheClusters(t)).CacheClusters?.[0];if(!G){L.fail("Cluster not found");return}if(G.Engine!=="redis"){L.fail("Flush is only supported for Redis clusters"),e("For Memcached, items expire automatically based on TTL.");return}L.stop();let Z=G.CacheNodes?.[0]?.Endpoint;if(!Z){r("Could not determine cluster endpoint");return}if(a(`This will delete ALL data in cluster: ${t}`),a(`Endpoint: ${Z.Address}:${Z.Port}`),a("This action cannot be undone!"),!await d(`
516
516
  Flush all data?`,!1)){e("Operation cancelled");return}e(`
517
517
  To flush the Redis cluster, connect and run FLUSHALL:`),e(` redis-cli -h ${Z.Address} -p ${Z.Port} FLUSHALL`),e(`
518
- Note: Direct FLUSHALL via AWS API is not supported.`),e("You must connect to the cluster directly to execute this command.")}catch(A){r(`Failed to get cluster info: ${A.message}`),process.exit(1)}}),N.command("cache:reboot <clusterId>","Reboot cache cluster nodes").option("--region <region>","AWS region",{default:"us-east-1"}).option("--node <nodeId>","Specific node ID to reboot").action(async(t,p)=>{i("Reboot ElastiCache Cluster");try{let A=new te(p.region),L=new o("Getting cluster info...");L.start();let G=(await A.describeCacheClusters(t)).CacheClusters?.[0];if(!G){L.fail("Cluster not found");return}L.stop();let Z=p.node?[p.node]:G.CacheNodes?.map((oe)=>oe.CacheNodeId).filter(Boolean)||[];if(Z.length===0){r("No nodes found to reboot");return}if(a(`This will reboot the following nodes: ${Z.join(", ")}`),a("The cluster may be temporarily unavailable during reboot."),!await d(`
519
- Reboot these nodes?`,!1)){e("Operation cancelled");return}let re=new o("Rebooting nodes...");re.start(),await A.rebootCacheCluster(t,Z),re.succeed("Reboot initiated"),e(`
520
- Note: Reboot may take several minutes to complete.`),e("Use `cloud cache:stats` to check status.")}catch(A){r(`Failed to reboot cluster: ${A.message}`),process.exit(1)}})}function Bc(N){N.command("queue:list","List all SQS queues").option("--region <region>","AWS region").action(async(t)=>{i("SQS Queues");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new F(A),j=new o("Fetching queues...");j.start();let Z=(await L.listQueues()).QueueUrls||[];if(j.succeed(`Found ${Z.length} queue(s)`),Z.length===0){e("No SQS queues found"),e("Use `cloud queue:create` to create a new queue");return}let ee=[];for(let re of Z)try{let oe=await L.getQueueAttributes(re),be=re.split("/").pop()||re;ee.push({url:re,name:be,messages:oe.Attributes?.ApproximateNumberOfMessages||"0",type:oe.Attributes?.FifoQueue==="true"?"FIFO":"Standard"})}catch{let oe=re.split("/").pop()||re;ee.push({url:re,name:oe,messages:"N/A",type:"Unknown"})}c(["Queue Name","Messages","Type"],ee.map((re)=>[re.name,re.messages,re.type]))}catch(p){r(`Failed to list queues: ${p.message}`),process.exit(1)}}),N.command("queue:create <name>","Create a new SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).option("--fifo","Create a FIFO queue").option("--dlq <queueArn>","Dead letter queue ARN").option("--max-retries <number>","Max receive count before DLQ",{default:"3"}).option("--visibility <seconds>","Visibility timeout in seconds",{default:"30"}).option("--retention <days>","Message retention in days",{default:"4"}).action(async(t,p)=>{i("Create SQS Queue");try{let A=new F(p.region),L=p.fifo&&!t.endsWith(".fifo")?`${t}.fifo`:t;if(e(`Queue name: ${L}`),e(`Type: ${p.fifo?"FIFO":"Standard"}`),e(`Visibility timeout: ${p.visibility} seconds`),e(`Message retention: ${p.retention} days`),p.dlq)e(`Dead letter queue: ${p.dlq}`),e(`Max retries: ${p.maxRetries}`);if(!await d(`
521
- Create this queue?`,!0)){e("Operation cancelled");return}let G=new o("Creating queue...");G.start();let Z=await A.createQueue({queueName:L,fifo:p.fifo,visibilityTimeout:Number.parseInt(p.visibility),messageRetentionPeriod:Number.parseInt(p.retention)*24*60*60,contentBasedDeduplication:p.fifo?!0:void 0,deadLetterTargetArn:p.dlq,maxReceiveCount:p.dlq?Number.parseInt(p.maxRetries):void 0});G.succeed("Queue created"),n(`
518
+ Note: Direct FLUSHALL via AWS API is not supported.`),e("You must connect to the cluster directly to execute this command.")}catch(A){r(`Failed to get cluster info: ${A.message}`),process.exit(1)}}),N.command("cache:reboot <clusterId>","Reboot cache cluster nodes").option("--region <region>","AWS region",{default:"us-east-1"}).option("--node <nodeId>","Specific node ID to reboot").action(async(t,m)=>{i("Reboot ElastiCache Cluster");try{let A=new te(m.region),L=new o("Getting cluster info...");L.start();let G=(await A.describeCacheClusters(t)).CacheClusters?.[0];if(!G){L.fail("Cluster not found");return}L.stop();let Z=m.node?[m.node]:G.CacheNodes?.map((oe)=>oe.CacheNodeId).filter(Boolean)||[];if(Z.length===0){r("No nodes found to reboot");return}if(a(`This will reboot the following nodes: ${Z.join(", ")}`),a("The cluster may be temporarily unavailable during reboot."),!await d(`
519
+ Reboot these nodes?`,!1)){e("Operation cancelled");return}let ne=new o("Rebooting nodes...");ne.start(),await A.rebootCacheCluster(t,Z),ne.succeed("Reboot initiated"),e(`
520
+ Note: Reboot may take several minutes to complete.`),e("Use `cloud cache:stats` to check status.")}catch(A){r(`Failed to reboot cluster: ${A.message}`),process.exit(1)}})}function Kc(N){N.command("queue:list","List all SQS queues").option("--region <region>","AWS region").action(async(t)=>{i("SQS Queues");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new F(A),j=new o("Fetching queues...");j.start();let Z=(await L.listQueues()).QueueUrls||[];if(j.succeed(`Found ${Z.length} queue(s)`),Z.length===0){e("No SQS queues found"),e("Use `cloud queue:create` to create a new queue");return}let ee=[];for(let ne of Z)try{let oe=await L.getQueueAttributes(ne),be=ne.split("/").pop()||ne;ee.push({url:ne,name:be,messages:oe.Attributes?.ApproximateNumberOfMessages||"0",type:oe.Attributes?.FifoQueue==="true"?"FIFO":"Standard"})}catch{let oe=ne.split("/").pop()||ne;ee.push({url:ne,name:oe,messages:"N/A",type:"Unknown"})}c(["Queue Name","Messages","Type"],ee.map((ne)=>[ne.name,ne.messages,ne.type]))}catch(m){r(`Failed to list queues: ${m.message}`),process.exit(1)}}),N.command("queue:create <name>","Create a new SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).option("--fifo","Create a FIFO queue").option("--dlq <queueArn>","Dead letter queue ARN").option("--max-retries <number>","Max receive count before DLQ",{default:"3"}).option("--visibility <seconds>","Visibility timeout in seconds",{default:"30"}).option("--retention <days>","Message retention in days",{default:"4"}).action(async(t,m)=>{i("Create SQS Queue");try{let A=new F(m.region),L=m.fifo&&!t.endsWith(".fifo")?`${t}.fifo`:t;if(e(`Queue name: ${L}`),e(`Type: ${m.fifo?"FIFO":"Standard"}`),e(`Visibility timeout: ${m.visibility} seconds`),e(`Message retention: ${m.retention} days`),m.dlq)e(`Dead letter queue: ${m.dlq}`),e(`Max retries: ${m.maxRetries}`);if(!await d(`
521
+ Create this queue?`,!0)){e("Operation cancelled");return}let G=new o("Creating queue...");G.start();let Z=await A.createQueue({queueName:L,fifo:m.fifo,visibilityTimeout:Number.parseInt(m.visibility),messageRetentionPeriod:Number.parseInt(m.retention)*24*60*60,contentBasedDeduplication:m.fifo?!0:void 0,deadLetterTargetArn:m.dlq,maxReceiveCount:m.dlq?Number.parseInt(m.maxRetries):void 0});G.succeed("Queue created"),n(`
522
522
  Queue URL: ${Z.QueueUrl}`),e(`
523
- To send a message:`),e(` cloud queue:send ${L} --message "Hello World"`)}catch(A){r(`Failed to create queue: ${A.message}`),process.exit(1)}}),N.command("queue:delete <name>","Delete an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Delete SQS Queue");try{let A=new F(p.region);if(a(`This will permanently delete queue: ${t}`),a("All messages in the queue will be lost!"),!await d(`
524
- Delete this queue?`,!1)){e("Operation cancelled");return}let j=new o("Getting queue URL...");j.start();let G=await A.getQueueUrl(t);if(!G.QueueUrl){j.fail("Queue not found");return}j.text="Deleting queue...",await A.deleteQueue(G.QueueUrl),j.succeed("Queue deleted")}catch(A){r(`Failed to delete queue: ${A.message}`),process.exit(1)}}),N.command("queue:send <name>","Send a message to an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).option("--message <body>","Message body").option("--file <path>","Read message body from file").option("--group <id>","Message group ID (for FIFO queues)").option("--dedup <id>","Deduplication ID (for FIFO queues)").option("--delay <seconds>","Delay delivery in seconds",{default:"0"}).action(async(t,p)=>{i("Send SQS Message");try{let A=new F(p.region),L;if(p.file)L=await Bun.file(p.file).text();else if(p.message)L=p.message;else L=await I("Message body");if(!L){r("Message body is required");return}let j=new o("Getting queue URL...");j.start();let G=await A.getQueueUrl(t);if(!G.QueueUrl){j.fail("Queue not found");return}j.text="Sending message...";let Z=await A.sendMessage({queueUrl:G.QueueUrl,messageBody:L,delaySeconds:Number.parseInt(p.delay),messageGroupId:p.group,messageDeduplicationId:p.dedup});j.succeed("Message sent"),n(`
525
- Message ID: ${Z.MessageId}`)}catch(A){r(`Failed to send message: ${A.message}`),process.exit(1)}}),N.command("queue:receive <name>","Receive messages from an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).option("--max <number>","Maximum number of messages",{default:"1"}).option("--wait <seconds>","Long polling wait time",{default:"0"}).option("--delete","Delete messages after receiving").action(async(t,p)=>{i("Receive SQS Messages");try{let A=new F(p.region),L=new o("Getting queue URL...");L.start();let j=await A.getQueueUrl(t);if(!j.QueueUrl){L.fail("Queue not found");return}L.text="Receiving messages...";let Z=(await A.receiveMessages({queueUrl:j.QueueUrl,maxMessages:Number.parseInt(p.max),waitTimeSeconds:Number.parseInt(p.wait)})).Messages||[];if(L.succeed(`Received ${Z.length} message(s)`),Z.length===0){e("No messages available");return}for(let ee of Z){if(e(`
526
- --- Message: ${ee.MessageId} ---`),e(`Body: ${ee.Body}`),ee.Attributes)e(`Sent: ${ee.Attributes.SentTimestamp?new Date(Number.parseInt(ee.Attributes.SentTimestamp)).toISOString():"N/A"}`),e(`Receive Count: ${ee.Attributes.ApproximateReceiveCount||"N/A"}`);if(p.delete&&ee.ReceiptHandle)await A.deleteMessage(j.QueueUrl,ee.ReceiptHandle),e("(Deleted)")}}catch(A){r(`Failed to receive messages: ${A.message}`),process.exit(1)}}),N.command("queue:purge <name>","Purge all messages from an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Purge SQS Queue");try{let A=new F(p.region);if(a(`This will delete ALL messages in queue: ${t}`),a("This action cannot be undone!"),!await d(`
523
+ To send a message:`),e(` cloud queue:send ${L} --message "Hello World"`)}catch(A){r(`Failed to create queue: ${A.message}`),process.exit(1)}}),N.command("queue:delete <name>","Delete an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Delete SQS Queue");try{let A=new F(m.region);if(a(`This will permanently delete queue: ${t}`),a("All messages in the queue will be lost!"),!await d(`
524
+ Delete this queue?`,!1)){e("Operation cancelled");return}let j=new o("Getting queue URL...");j.start();let G=await A.getQueueUrl(t);if(!G.QueueUrl){j.fail("Queue not found");return}j.text="Deleting queue...",await A.deleteQueue(G.QueueUrl),j.succeed("Queue deleted")}catch(A){r(`Failed to delete queue: ${A.message}`),process.exit(1)}}),N.command("queue:send <name>","Send a message to an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).option("--message <body>","Message body").option("--file <path>","Read message body from file").option("--group <id>","Message group ID (for FIFO queues)").option("--dedup <id>","Deduplication ID (for FIFO queues)").option("--delay <seconds>","Delay delivery in seconds",{default:"0"}).action(async(t,m)=>{i("Send SQS Message");try{let A=new F(m.region),L;if(m.file)L=await Bun.file(m.file).text();else if(m.message)L=m.message;else L=await I("Message body");if(!L){r("Message body is required");return}let j=new o("Getting queue URL...");j.start();let G=await A.getQueueUrl(t);if(!G.QueueUrl){j.fail("Queue not found");return}j.text="Sending message...";let Z=await A.sendMessage({queueUrl:G.QueueUrl,messageBody:L,delaySeconds:Number.parseInt(m.delay),messageGroupId:m.group,messageDeduplicationId:m.dedup});j.succeed("Message sent"),n(`
525
+ Message ID: ${Z.MessageId}`)}catch(A){r(`Failed to send message: ${A.message}`),process.exit(1)}}),N.command("queue:receive <name>","Receive messages from an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).option("--max <number>","Maximum number of messages",{default:"1"}).option("--wait <seconds>","Long polling wait time",{default:"0"}).option("--delete","Delete messages after receiving").action(async(t,m)=>{i("Receive SQS Messages");try{let A=new F(m.region),L=new o("Getting queue URL...");L.start();let j=await A.getQueueUrl(t);if(!j.QueueUrl){L.fail("Queue not found");return}L.text="Receiving messages...";let Z=(await A.receiveMessages({queueUrl:j.QueueUrl,maxMessages:Number.parseInt(m.max),waitTimeSeconds:Number.parseInt(m.wait)})).Messages||[];if(L.succeed(`Received ${Z.length} message(s)`),Z.length===0){e("No messages available");return}for(let ee of Z){if(e(`
526
+ --- Message: ${ee.MessageId} ---`),e(`Body: ${ee.Body}`),ee.Attributes)e(`Sent: ${ee.Attributes.SentTimestamp?new Date(Number.parseInt(ee.Attributes.SentTimestamp)).toISOString():"N/A"}`),e(`Receive Count: ${ee.Attributes.ApproximateReceiveCount||"N/A"}`);if(m.delete&&ee.ReceiptHandle)await A.deleteMessage(j.QueueUrl,ee.ReceiptHandle),e("(Deleted)")}}catch(A){r(`Failed to receive messages: ${A.message}`),process.exit(1)}}),N.command("queue:purge <name>","Purge all messages from an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Purge SQS Queue");try{let A=new F(m.region);if(a(`This will delete ALL messages in queue: ${t}`),a("This action cannot be undone!"),!await d(`
527
527
  Purge this queue?`,!1)){e("Operation cancelled");return}let j=new o("Getting queue URL...");j.start();let G=await A.getQueueUrl(t);if(!G.QueueUrl){j.fail("Queue not found");return}j.text="Purging queue...",await A.purgeQueue(G.QueueUrl),j.succeed("Queue purged"),e(`
528
- Note: It may take up to 60 seconds for the purge to complete.`)}catch(A){r(`Failed to purge queue: ${A.message}`),process.exit(1)}}),N.command("queue:stats <name>","Show SQS queue statistics").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i(`SQS Queue Stats: ${t}`);try{let A=new F(p.region),L=new o("Fetching queue stats...");L.start();let j=await A.getQueueUrl(t);if(!j.QueueUrl){L.fail("Queue not found");return}let G=await A.getQueueAttributes(j.QueueUrl);L.succeed("Stats loaded");let Z=G.Attributes||{};if(e(`
528
+ Note: It may take up to 60 seconds for the purge to complete.`)}catch(A){r(`Failed to purge queue: ${A.message}`),process.exit(1)}}),N.command("queue:stats <name>","Show SQS queue statistics").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i(`SQS Queue Stats: ${t}`);try{let A=new F(m.region),L=new o("Fetching queue stats...");L.start();let j=await A.getQueueUrl(t);if(!j.QueueUrl){L.fail("Queue not found");return}let G=await A.getQueueAttributes(j.QueueUrl);L.succeed("Stats loaded");let Z=G.Attributes||{};if(e(`
529
529
  Queue Information:`),e(` URL: ${j.QueueUrl}`),e(` ARN: ${Z.QueueArn||"N/A"}`),e(` Type: ${Z.FifoQueue==="true"?"FIFO":"Standard"}`),e(`
530
530
  Messages:`),e(` Available: ${Z.ApproximateNumberOfMessages||"0"}`),e(` In Flight: ${Z.ApproximateNumberOfMessagesNotVisible||"0"}`),e(` Delayed: ${Z.ApproximateNumberOfMessagesDelayed||"0"}`),e(`
531
531
  Configuration:`),e(` Visibility Timeout: ${Z.VisibilityTimeout||"30"} seconds`),e(` Message Retention: ${Number.parseInt(Z.MessageRetentionPeriod||"345600")/86400} days`),e(` Max Message Size: ${Number.parseInt(Z.MaximumMessageSize||"262144")/1024} KB`),e(` Receive Wait Time: ${Z.ReceiveMessageWaitTimeSeconds||"0"} seconds`),Z.RedrivePolicy){let ee=JSON.parse(Z.RedrivePolicy);e(`
532
532
  Dead Letter Queue:`),e(` Target ARN: ${ee.deadLetterTargetArn}`),e(` Max Receives: ${ee.maxReceiveCount}`)}e(`
533
- Timestamps:`),e(` Created: ${Z.CreatedTimestamp?new Date(Number.parseInt(Z.CreatedTimestamp)*1000).toISOString():"N/A"}`),e(` Last Modified: ${Z.LastModifiedTimestamp?new Date(Number.parseInt(Z.LastModifiedTimestamp)*1000).toISOString():"N/A"}`)}catch(A){r(`Failed to get queue stats: ${A.message}`),process.exit(1)}})}function Mc(N){N.command("network:list","List all VPCs").option("--region <region>","AWS region").action(async(t)=>{i("VPCs");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new K(A),j=new o("Fetching VPCs...");j.start();let Z=(await L.describeVpcs()).Vpcs||[];if(j.succeed(`Found ${Z.length} VPC(s)`),Z.length===0){e("No VPCs found");return}c(["VPC ID","Name","CIDR","State","Default"],Z.map((ee)=>[ee.VpcId||"N/A",ee.Tags?.find((re)=>re.Key==="Name")?.Value||"-",ee.CidrBlock||"N/A",ee.State||"N/A",ee.IsDefault?"Yes":"No"]))}catch(p){r(`Failed to list VPCs: ${p.message}`),process.exit(1)}}),N.command("network:subnets","List all subnets").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(t)=>{i("Subnets");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new K(A),j=new o("Fetching subnets...");j.start();let G=t.vpc?[{Name:"vpc-id",Values:[t.vpc]}]:void 0,ee=(await L.describeSubnets({Filters:G})).Subnets||[];if(j.succeed(`Found ${ee.length} subnet(s)`),ee.length===0){e("No subnets found");return}c(["Subnet ID","Name","VPC","CIDR","AZ","IPs Available","Public"],ee.map((re)=>[re.SubnetId||"N/A",re.Tags?.find((oe)=>oe.Key==="Name")?.Value||"-",re.VpcId||"N/A",re.CidrBlock||"N/A",re.AvailabilityZone||"N/A",(re.AvailableIpAddressCount||0).toString(),re.MapPublicIpOnLaunch?"Yes":"No"]))}catch(p){r(`Failed to list subnets: ${p.message}`),process.exit(1)}}),N.command("network:security-groups","List all security groups").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(t)=>{i("Security Groups");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new K(A),j=new o("Fetching security groups...");j.start();let G=t.vpc?[{Name:"vpc-id",Values:[t.vpc]}]:void 0,ee=(await L.describeSecurityGroups({Filters:G})).SecurityGroups||[];if(j.succeed(`Found ${ee.length} security group(s)`),ee.length===0){e("No security groups found");return}c(["Group ID","Name","VPC","Description","Inbound Rules","Outbound Rules"],ee.map((re)=>[re.GroupId||"N/A",re.GroupName||"N/A",re.VpcId||"N/A",(re.Description||"").substring(0,30),(re.IpPermissions?.length||0).toString(),(re.IpPermissionsEgress?.length||0).toString()]))}catch(p){r(`Failed to list security groups: ${p.message}`),process.exit(1)}}),N.command("network:security-group <groupId>","Show security group details").option("--region <region>","AWS region").action(async(t,p)=>{i(`Security Group: ${t}`);try{let A=await Me(),L=p.region||A.project.region||"us-east-1",j=new K(L),G=new o("Fetching security group...");G.start();let ee=(await j.describeSecurityGroups({GroupIds:[t]})).SecurityGroups?.[0];if(!ee){G.fail("Security group not found");return}if(G.succeed("Security group loaded"),e(`
533
+ Timestamps:`),e(` Created: ${Z.CreatedTimestamp?new Date(Number.parseInt(Z.CreatedTimestamp)*1000).toISOString():"N/A"}`),e(` Last Modified: ${Z.LastModifiedTimestamp?new Date(Number.parseInt(Z.LastModifiedTimestamp)*1000).toISOString():"N/A"}`)}catch(A){r(`Failed to get queue stats: ${A.message}`),process.exit(1)}})}function Jc(N){N.command("network:list","List all VPCs").option("--region <region>","AWS region").action(async(t)=>{i("VPCs");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new K(A),j=new o("Fetching VPCs...");j.start();let Z=(await L.describeVpcs()).Vpcs||[];if(j.succeed(`Found ${Z.length} VPC(s)`),Z.length===0){e("No VPCs found");return}c(["VPC ID","Name","CIDR","State","Default"],Z.map((ee)=>[ee.VpcId||"N/A",ee.Tags?.find((ne)=>ne.Key==="Name")?.Value||"-",ee.CidrBlock||"N/A",ee.State||"N/A",ee.IsDefault?"Yes":"No"]))}catch(m){r(`Failed to list VPCs: ${m.message}`),process.exit(1)}}),N.command("network:subnets","List all subnets").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(t)=>{i("Subnets");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new K(A),j=new o("Fetching subnets...");j.start();let G=t.vpc?[{Name:"vpc-id",Values:[t.vpc]}]:void 0,ee=(await L.describeSubnets({Filters:G})).Subnets||[];if(j.succeed(`Found ${ee.length} subnet(s)`),ee.length===0){e("No subnets found");return}c(["Subnet ID","Name","VPC","CIDR","AZ","IPs Available","Public"],ee.map((ne)=>[ne.SubnetId||"N/A",ne.Tags?.find((oe)=>oe.Key==="Name")?.Value||"-",ne.VpcId||"N/A",ne.CidrBlock||"N/A",ne.AvailabilityZone||"N/A",(ne.AvailableIpAddressCount||0).toString(),ne.MapPublicIpOnLaunch?"Yes":"No"]))}catch(m){r(`Failed to list subnets: ${m.message}`),process.exit(1)}}),N.command("network:security-groups","List all security groups").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(t)=>{i("Security Groups");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new K(A),j=new o("Fetching security groups...");j.start();let G=t.vpc?[{Name:"vpc-id",Values:[t.vpc]}]:void 0,ee=(await L.describeSecurityGroups({Filters:G})).SecurityGroups||[];if(j.succeed(`Found ${ee.length} security group(s)`),ee.length===0){e("No security groups found");return}c(["Group ID","Name","VPC","Description","Inbound Rules","Outbound Rules"],ee.map((ne)=>[ne.GroupId||"N/A",ne.GroupName||"N/A",ne.VpcId||"N/A",(ne.Description||"").substring(0,30),(ne.IpPermissions?.length||0).toString(),(ne.IpPermissionsEgress?.length||0).toString()]))}catch(m){r(`Failed to list security groups: ${m.message}`),process.exit(1)}}),N.command("network:security-group <groupId>","Show security group details").option("--region <region>","AWS region").action(async(t,m)=>{i(`Security Group: ${t}`);try{let A=await Ne(),L=m.region||A.project.region||"us-east-1",j=new K(L),G=new o("Fetching security group...");G.start();let ee=(await j.describeSecurityGroups({GroupIds:[t]})).SecurityGroups?.[0];if(!ee){G.fail("Security group not found");return}if(G.succeed("Security group loaded"),e(`
534
534
  General Information:`),e(` Group ID: ${ee.GroupId}`),e(` Name: ${ee.GroupName}`),e(` VPC: ${ee.VpcId}`),e(` Description: ${ee.Description}`),ee.IpPermissions&&ee.IpPermissions.length>0){e(`
535
- Inbound Rules:`);for(let re of ee.IpPermissions){let oe=re.IpProtocol==="-1"?"All":re.IpProtocol?.toUpperCase(),be=re.FromPort===re.ToPort?re.FromPort?.toString()||"All":`${re.FromPort}-${re.ToPort}`;for(let we of re.IpRanges||[])e(` - ${oe} ${be} from ${we.CidrIp}${we.Description?` (${we.Description})`:""}`);for(let we of re.UserIdGroupPairs||[])e(` - ${oe} ${be} from ${we.GroupId}${we.Description?` (${we.Description})`:""}`)}}else e(`
535
+ Inbound Rules:`);for(let ne of ee.IpPermissions){let oe=ne.IpProtocol==="-1"?"All":ne.IpProtocol?.toUpperCase(),be=ne.FromPort===ne.ToPort?ne.FromPort?.toString()||"All":`${ne.FromPort}-${ne.ToPort}`;for(let we of ne.IpRanges||[])e(` - ${oe} ${be} from ${we.CidrIp}${we.Description?` (${we.Description})`:""}`);for(let we of ne.UserIdGroupPairs||[])e(` - ${oe} ${be} from ${we.GroupId}${we.Description?` (${we.Description})`:""}`)}}else e(`
536
536
  No inbound rules configured.`);if(ee.IpPermissionsEgress&&ee.IpPermissionsEgress.length>0){e(`
537
- Outbound Rules:`);for(let re of ee.IpPermissionsEgress){let oe=re.IpProtocol==="-1"?"All":re.IpProtocol?.toUpperCase(),be=re.FromPort===re.ToPort?re.FromPort?.toString()||"All":`${re.FromPort}-${re.ToPort}`;for(let we of re.IpRanges||[])e(` - ${oe} ${be} to ${we.CidrIp}${we.Description?` (${we.Description})`:""}`);for(let we of re.UserIdGroupPairs||[])e(` - ${oe} ${be} to ${we.GroupId}${we.Description?` (${we.Description})`:""}`)}}else e(`
537
+ Outbound Rules:`);for(let ne of ee.IpPermissionsEgress){let oe=ne.IpProtocol==="-1"?"All":ne.IpProtocol?.toUpperCase(),be=ne.FromPort===ne.ToPort?ne.FromPort?.toString()||"All":`${ne.FromPort}-${ne.ToPort}`;for(let we of ne.IpRanges||[])e(` - ${oe} ${be} to ${we.CidrIp}${we.Description?` (${we.Description})`:""}`);for(let we of ne.UserIdGroupPairs||[])e(` - ${oe} ${be} to ${we.GroupId}${we.Description?` (${we.Description})`:""}`)}}else e(`
538
538
  No outbound rules configured.`);if(ee.Tags&&ee.Tags.length>0){e(`
539
- Tags:`);for(let re of ee.Tags)e(` ${re.Key}: ${re.Value}`)}}catch(A){r(`Failed to get security group: ${A.message}`),process.exit(1)}}),N.command("network:create-vpc <cidr>","Create a new VPC").option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","VPC name tag").option("--tenancy <tenancy>","Instance tenancy (default or dedicated)",{default:"default"}).action(async(t,p)=>{i("Create VPC");try{let A=new K(p.region);if(e(`CIDR Block: ${t}`),e(`Region: ${p.region}`),e(`Tenancy: ${p.tenancy}`),p.name)e(`Name: ${p.name}`);if(!await d(`
540
- Create this VPC?`,!0)){e("Operation cancelled");return}let j=new o("Creating VPC...");j.start();let G=await A.createVpc({CidrBlock:t,InstanceTenancy:p.tenancy,TagSpecifications:p.name?[{ResourceType:"vpc",Tags:[{Key:"Name",Value:p.name}]}]:void 0});j.succeed("VPC created"),n(`
541
- VPC ID: ${G.Vpc?.VpcId}`),e(`CIDR: ${G.Vpc?.CidrBlock}`),e(`State: ${G.Vpc?.State}`)}catch(A){r(`Failed to create VPC: ${A.message}`),process.exit(1)}}),N.command("network:create-subnet <vpcId> <cidr>","Create a new subnet").option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","Subnet name tag").option("--az <zone>","Availability zone").option("--public","Auto-assign public IPs").action(async(t,p,A)=>{i("Create Subnet");try{let L=new K(A.region);if(e(`VPC: ${t}`),e(`CIDR Block: ${p}`),A.az)e(`Availability Zone: ${A.az}`);if(A.name)e(`Name: ${A.name}`);if(e(`Auto-assign Public IP: ${A.public?"Yes":"No"}`),!await d(`
542
- Create this subnet?`,!0)){e("Operation cancelled");return}let G=new o("Creating subnet...");G.start();let Z=await L.createSubnet({VpcId:t,CidrBlock:p,AvailabilityZone:A.az,TagSpecifications:A.name?[{ResourceType:"subnet",Tags:[{Key:"Name",Value:A.name}]}]:void 0});if(A.public&&Z.Subnet?.SubnetId)G.text="Enabling auto-assign public IP...",await L.modifySubnetAttribute({SubnetId:Z.Subnet.SubnetId,MapPublicIpOnLaunch:{Value:!0}});G.succeed("Subnet created"),n(`
543
- Subnet ID: ${Z.Subnet?.SubnetId}`),e(`CIDR: ${Z.Subnet?.CidrBlock}`),e(`Availability Zone: ${Z.Subnet?.AvailabilityZone}`)}catch(L){r(`Failed to create subnet: ${L.message}`),process.exit(1)}}),N.command("network:create-sg <vpcId> <name>","Create a new security group").option("--region <region>","AWS region",{default:"us-east-1"}).option("--description <desc>","Security group description").action(async(t,p,A)=>{i("Create Security Group");try{let L=new K(A.region),j=A.description||`Security group for ${p}`;if(e(`VPC: ${t}`),e(`Name: ${p}`),e(`Description: ${j}`),!await d(`
544
- Create this security group?`,!0)){e("Operation cancelled");return}let Z=new o("Creating security group...");Z.start();let ee=await L.createSecurityGroup({GroupName:p,Description:j,VpcId:t,TagSpecifications:[{ResourceType:"security-group",Tags:[{Key:"Name",Value:p}]}]});Z.succeed("Security group created"),n(`
539
+ Tags:`);for(let ne of ee.Tags)e(` ${ne.Key}: ${ne.Value}`)}}catch(A){r(`Failed to get security group: ${A.message}`),process.exit(1)}}),N.command("network:create-vpc <cidr>","Create a new VPC").option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","VPC name tag").option("--tenancy <tenancy>","Instance tenancy (default or dedicated)",{default:"default"}).action(async(t,m)=>{i("Create VPC");try{let A=new K(m.region);if(e(`CIDR Block: ${t}`),e(`Region: ${m.region}`),e(`Tenancy: ${m.tenancy}`),m.name)e(`Name: ${m.name}`);if(!await d(`
540
+ Create this VPC?`,!0)){e("Operation cancelled");return}let j=new o("Creating VPC...");j.start();let G=await A.createVpc({CidrBlock:t,InstanceTenancy:m.tenancy,TagSpecifications:m.name?[{ResourceType:"vpc",Tags:[{Key:"Name",Value:m.name}]}]:void 0});j.succeed("VPC created"),n(`
541
+ VPC ID: ${G.Vpc?.VpcId}`),e(`CIDR: ${G.Vpc?.CidrBlock}`),e(`State: ${G.Vpc?.State}`)}catch(A){r(`Failed to create VPC: ${A.message}`),process.exit(1)}}),N.command("network:create-subnet <vpcId> <cidr>","Create a new subnet").option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","Subnet name tag").option("--az <zone>","Availability zone").option("--public","Auto-assign public IPs").action(async(t,m,A)=>{i("Create Subnet");try{let L=new K(A.region);if(e(`VPC: ${t}`),e(`CIDR Block: ${m}`),A.az)e(`Availability Zone: ${A.az}`);if(A.name)e(`Name: ${A.name}`);if(e(`Auto-assign Public IP: ${A.public?"Yes":"No"}`),!await d(`
542
+ Create this subnet?`,!0)){e("Operation cancelled");return}let G=new o("Creating subnet...");G.start();let Z=await L.createSubnet({VpcId:t,CidrBlock:m,AvailabilityZone:A.az,TagSpecifications:A.name?[{ResourceType:"subnet",Tags:[{Key:"Name",Value:A.name}]}]:void 0});if(A.public&&Z.Subnet?.SubnetId)G.text="Enabling auto-assign public IP...",await L.modifySubnetAttribute({SubnetId:Z.Subnet.SubnetId,MapPublicIpOnLaunch:{Value:!0}});G.succeed("Subnet created"),n(`
543
+ Subnet ID: ${Z.Subnet?.SubnetId}`),e(`CIDR: ${Z.Subnet?.CidrBlock}`),e(`Availability Zone: ${Z.Subnet?.AvailabilityZone}`)}catch(L){r(`Failed to create subnet: ${L.message}`),process.exit(1)}}),N.command("network:create-sg <vpcId> <name>","Create a new security group").option("--region <region>","AWS region",{default:"us-east-1"}).option("--description <desc>","Security group description").action(async(t,m,A)=>{i("Create Security Group");try{let L=new K(A.region),j=A.description||`Security group for ${m}`;if(e(`VPC: ${t}`),e(`Name: ${m}`),e(`Description: ${j}`),!await d(`
544
+ Create this security group?`,!0)){e("Operation cancelled");return}let Z=new o("Creating security group...");Z.start();let ee=await L.createSecurityGroup({GroupName:m,Description:j,VpcId:t,TagSpecifications:[{ResourceType:"security-group",Tags:[{Key:"Name",Value:m}]}]});Z.succeed("Security group created"),n(`
545
545
  Group ID: ${ee.GroupId}`),e(`
546
- To add rules:`),e(` cloud network:sg-rule ${ee.GroupId} --inbound --port 443 --cidr 0.0.0.0/0`)}catch(L){r(`Failed to create security group: ${L.message}`),process.exit(1)}}),N.command("network:sg-rule <groupId>","Add a rule to a security group").option("--region <region>","AWS region",{default:"us-east-1"}).option("--inbound","Add inbound rule").option("--outbound","Add outbound rule").option("--protocol <protocol>","Protocol (tcp, udp, icmp, or -1 for all)",{default:"tcp"}).option("--port <port>","Port number or range (e.g., 80 or 80-443)").option("--cidr <cidr>","CIDR block (e.g., 0.0.0.0/0)").option("--source-group <groupId>","Source security group").option("--description <desc>","Rule description").action(async(t,p)=>{i("Add Security Group Rule");try{let A=new K(p.region);if(!p.inbound&&!p.outbound){r("Specify --inbound or --outbound");return}if(!p.cidr&&!p.sourceGroup){r("Specify --cidr or --source-group");return}let L,j;if(p.port)if(p.port.includes("-")){let[be,we]=p.port.split("-");L=Number.parseInt(be),j=Number.parseInt(we)}else L=Number.parseInt(p.port),j=L;else if(p.protocol!=="-1"){r("Port is required for TCP/UDP protocols");return}let G=p.inbound?"Inbound":"Outbound",Z=p.protocol==="-1"?"All":L===j?L:`${L}-${j}`;if(e(`Security Group: ${t}`),e(`Direction: ${G}`),e(`Protocol: ${p.protocol}`),e(`Port(s): ${Z}`),e(`Source: ${p.cidr||p.sourceGroup}`),!await d(`
547
- Add this rule?`,!0)){e("Operation cancelled");return}let re=new o("Adding rule...");re.start();let oe={IpProtocol:p.protocol,FromPort:L,ToPort:j};if(p.cidr)oe.IpRanges=[{CidrIp:p.cidr,Description:p.description}];if(p.sourceGroup)oe.UserIdGroupPairs=[{GroupId:p.sourceGroup,Description:p.description}];if(p.inbound)await A.authorizeSecurityGroupIngress({GroupId:t,IpPermissions:[oe]});else await A.authorizeSecurityGroupEgress({GroupId:t,IpPermissions:[oe]});re.succeed("Rule added")}catch(A){r(`Failed to add rule: ${A.message}`),process.exit(1)}}),N.command("network:elastic-ips","List Elastic IP addresses").option("--region <region>","AWS region").action(async(t)=>{i("Elastic IP Addresses");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new K(A),j=new o("Fetching Elastic IPs...");j.start();let Z=(await L.describeAddresses()).Addresses||[];if(j.succeed(`Found ${Z.length} Elastic IP(s)`),Z.length===0){e("No Elastic IPs found");return}c(["Public IP","Allocation ID","Association ID","Instance","Name"],Z.map((ee)=>[ee.PublicIp||"N/A",ee.AllocationId||"N/A",ee.AssociationId||"-",ee.InstanceId||"-",ee.Tags?.find((re)=>re.Key==="Name")?.Value||"-"]))}catch(p){r(`Failed to list Elastic IPs: ${p.message}`),process.exit(1)}}),N.command("network:route-tables","List route tables").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(t)=>{i("Route Tables");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new K(A),j=new o("Fetching route tables...");j.start();let G=t.vpc?[{Name:"vpc-id",Values:[t.vpc]}]:void 0,ee=(await L.describeRouteTables({Filters:G})).RouteTables||[];if(j.succeed(`Found ${ee.length} route table(s)`),ee.length===0){e("No route tables found");return}c(["Route Table ID","VPC","Name","Main","Associations","Routes"],ee.map((re)=>[re.RouteTableId||"N/A",re.VpcId||"N/A",re.Tags?.find((oe)=>oe.Key==="Name")?.Value||"-",re.Associations?.some((oe)=>oe.Main)?"Yes":"No",(re.Associations?.length||0).toString(),(re.Routes?.length||0).toString()]))}catch(p){r(`Failed to list route tables: ${p.message}`),process.exit(1)}})}function _c(N){N.command("scheduler:list","List all EventBridge schedules").option("--region <region>","AWS region").option("--group <name>","Schedule group name",{default:"default"}).action(async(t)=>{i("EventBridge Schedules");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new xe(A),j=new o("Fetching schedules...");j.start();let Z=(await L.listSchedules({GroupName:t.group})).Schedules||[];if(j.succeed(`Found ${Z.length} schedule(s)`),Z.length===0){e("No schedules found"),e("Use `cloud scheduler:create` to create a new schedule");return}c(["Name","State","Schedule Expression","Group"],Z.map((ee)=>[ee.Name||"N/A",ee.State||"N/A",ee.ScheduleExpression||"N/A",ee.GroupName||"default"]))}catch(p){r(`Failed to list schedules: ${p.message}`),process.exit(1)}}),N.command("scheduler:create <name>","Create a new EventBridge schedule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--schedule <expression>","Schedule expression (rate or cron)").option("--target-arn <arn>","Target ARN (Lambda, SQS, SNS, etc.)").option("--role-arn <arn>","IAM role ARN for scheduler").option("--input <json>","JSON input to pass to target").option("--group <name>","Schedule group name",{default:"default"}).option("--timezone <tz>","Timezone for cron expressions",{default:"UTC"}).option("--description <text>","Schedule description").option("--start <datetime>","Start date/time (ISO 8601)").option("--end <datetime>","End date/time (ISO 8601)").action(async(t,p)=>{i("Create EventBridge Schedule");try{if(!p.schedule){r("--schedule is required"),e("Examples:"),e(" Rate: rate(5 minutes), rate(1 hour), rate(1 day)"),e(" Cron: cron(0 12 * * ? *) - every day at 12:00 UTC");return}if(!p.targetArn){r("--target-arn is required");return}if(!p.roleArn){r("--role-arn is required"),e("The role must have permissions to invoke the target.");return}let A=new xe(p.region);if(e(`Name: ${t}`),e(`Schedule: ${p.schedule}`),e(`Target: ${p.targetArn}`),e(`Group: ${p.group}`),e(`Timezone: ${p.timezone}`),!await d(`
548
- Create this schedule?`,!0)){e("Operation cancelled");return}let j=new o("Creating schedule...");j.start(),await A.createSchedule({Name:t,GroupName:p.group,ScheduleExpression:p.schedule,ScheduleExpressionTimezone:p.timezone,Description:p.description,State:"ENABLED",FlexibleTimeWindow:{Mode:"OFF"},Target:{Arn:p.targetArn,RoleArn:p.roleArn,Input:p.input},StartDate:p.start?new Date(p.start):void 0,EndDate:p.end?new Date(p.end):void 0}),j.succeed("Schedule created"),n(`
549
- Schedule: ${t}`),e(`Expression: ${p.schedule}`)}catch(A){r(`Failed to create schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:delete <name>","Delete an EventBridge schedule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--group <name>","Schedule group name",{default:"default"}).action(async(t,p)=>{i("Delete EventBridge Schedule");try{let A=new xe(p.region);if(a(`This will delete schedule: ${t}`),!await d(`
550
- Delete this schedule?`,!1)){e("Operation cancelled");return}let j=new o("Deleting schedule...");j.start(),await A.deleteSchedule({Name:t,GroupName:p.group}),j.succeed("Schedule deleted")}catch(A){r(`Failed to delete schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:enable <name>","Enable an EventBridge schedule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--group <name>","Schedule group name",{default:"default"}).action(async(t,p)=>{i("Enable EventBridge Schedule");try{let A=new xe(p.region),L=new o("Getting schedule...");L.start();let j=await A.getSchedule({Name:t,GroupName:p.group});if(!j){L.fail("Schedule not found");return}L.text="Enabling schedule...",await A.updateSchedule({Name:t,GroupName:p.group,ScheduleExpression:j.ScheduleExpression,ScheduleExpressionTimezone:j.ScheduleExpressionTimezone,FlexibleTimeWindow:j.FlexibleTimeWindow,Target:j.Target,State:"ENABLED"}),L.succeed("Schedule enabled")}catch(A){r(`Failed to enable schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:disable <name>","Disable an EventBridge schedule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--group <name>","Schedule group name",{default:"default"}).action(async(t,p)=>{i("Disable EventBridge Schedule");try{let A=new xe(p.region),L=new o("Getting schedule...");L.start();let j=await A.getSchedule({Name:t,GroupName:p.group});if(!j){L.fail("Schedule not found");return}L.text="Disabling schedule...",await A.updateSchedule({Name:t,GroupName:p.group,ScheduleExpression:j.ScheduleExpression,ScheduleExpressionTimezone:j.ScheduleExpressionTimezone,FlexibleTimeWindow:j.FlexibleTimeWindow,Target:j.Target,State:"DISABLED"}),L.succeed("Schedule disabled")}catch(A){r(`Failed to disable schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:describe <name>","Show EventBridge schedule details").option("--region <region>","AWS region",{default:"us-east-1"}).option("--group <name>","Schedule group name",{default:"default"}).action(async(t,p)=>{i(`Schedule: ${t}`);try{let A=new xe(p.region),L=new o("Fetching schedule...");L.start();let j=await A.getSchedule({Name:t,GroupName:p.group});if(!j){L.fail("Schedule not found");return}if(L.succeed("Schedule loaded"),e(`
546
+ To add rules:`),e(` cloud network:sg-rule ${ee.GroupId} --inbound --port 443 --cidr 0.0.0.0/0`)}catch(L){r(`Failed to create security group: ${L.message}`),process.exit(1)}}),N.command("network:sg-rule <groupId>","Add a rule to a security group").option("--region <region>","AWS region",{default:"us-east-1"}).option("--inbound","Add inbound rule").option("--outbound","Add outbound rule").option("--protocol <protocol>","Protocol (tcp, udp, icmp, or -1 for all)",{default:"tcp"}).option("--port <port>","Port number or range (e.g., 80 or 80-443)").option("--cidr <cidr>","CIDR block (e.g., 0.0.0.0/0)").option("--source-group <groupId>","Source security group").option("--description <desc>","Rule description").action(async(t,m)=>{i("Add Security Group Rule");try{let A=new K(m.region);if(!m.inbound&&!m.outbound){r("Specify --inbound or --outbound");return}if(!m.cidr&&!m.sourceGroup){r("Specify --cidr or --source-group");return}let L,j;if(m.port)if(m.port.includes("-")){let[be,we]=m.port.split("-");L=Number.parseInt(be),j=Number.parseInt(we)}else L=Number.parseInt(m.port),j=L;else if(m.protocol!=="-1"){r("Port is required for TCP/UDP protocols");return}let G=m.inbound?"Inbound":"Outbound",Z=m.protocol==="-1"?"All":L===j?L:`${L}-${j}`;if(e(`Security Group: ${t}`),e(`Direction: ${G}`),e(`Protocol: ${m.protocol}`),e(`Port(s): ${Z}`),e(`Source: ${m.cidr||m.sourceGroup}`),!await d(`
547
+ Add this rule?`,!0)){e("Operation cancelled");return}let ne=new o("Adding rule...");ne.start();let oe={IpProtocol:m.protocol,FromPort:L,ToPort:j};if(m.cidr)oe.IpRanges=[{CidrIp:m.cidr,Description:m.description}];if(m.sourceGroup)oe.UserIdGroupPairs=[{GroupId:m.sourceGroup,Description:m.description}];if(m.inbound)await A.authorizeSecurityGroupIngress({GroupId:t,IpPermissions:[oe]});else await A.authorizeSecurityGroupEgress({GroupId:t,IpPermissions:[oe]});ne.succeed("Rule added")}catch(A){r(`Failed to add rule: ${A.message}`),process.exit(1)}}),N.command("network:elastic-ips","List Elastic IP addresses").option("--region <region>","AWS region").action(async(t)=>{i("Elastic IP Addresses");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new K(A),j=new o("Fetching Elastic IPs...");j.start();let Z=(await L.describeAddresses()).Addresses||[];if(j.succeed(`Found ${Z.length} Elastic IP(s)`),Z.length===0){e("No Elastic IPs found");return}c(["Public IP","Allocation ID","Association ID","Instance","Name"],Z.map((ee)=>[ee.PublicIp||"N/A",ee.AllocationId||"N/A",ee.AssociationId||"-",ee.InstanceId||"-",ee.Tags?.find((ne)=>ne.Key==="Name")?.Value||"-"]))}catch(m){r(`Failed to list Elastic IPs: ${m.message}`),process.exit(1)}}),N.command("network:route-tables","List route tables").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(t)=>{i("Route Tables");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new K(A),j=new o("Fetching route tables...");j.start();let G=t.vpc?[{Name:"vpc-id",Values:[t.vpc]}]:void 0,ee=(await L.describeRouteTables({Filters:G})).RouteTables||[];if(j.succeed(`Found ${ee.length} route table(s)`),ee.length===0){e("No route tables found");return}c(["Route Table ID","VPC","Name","Main","Associations","Routes"],ee.map((ne)=>[ne.RouteTableId||"N/A",ne.VpcId||"N/A",ne.Tags?.find((oe)=>oe.Key==="Name")?.Value||"-",ne.Associations?.some((oe)=>oe.Main)?"Yes":"No",(ne.Associations?.length||0).toString(),(ne.Routes?.length||0).toString()]))}catch(m){r(`Failed to list route tables: ${m.message}`),process.exit(1)}})}function Yc(N){N.command("scheduler:list","List all EventBridge schedules").option("--region <region>","AWS region").option("--group <name>","Schedule group name",{default:"default"}).action(async(t)=>{i("EventBridge Schedules");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new _e(A),j=new o("Fetching schedules...");j.start();let Z=(await L.listSchedules({GroupName:t.group})).Schedules||[];if(j.succeed(`Found ${Z.length} schedule(s)`),Z.length===0){e("No schedules found"),e("Use `cloud scheduler:create` to create a new schedule");return}c(["Name","State","Schedule Expression","Group"],Z.map((ee)=>[ee.Name||"N/A",ee.State||"N/A",ee.ScheduleExpression||"N/A",ee.GroupName||"default"]))}catch(m){r(`Failed to list schedules: ${m.message}`),process.exit(1)}}),N.command("scheduler:create <name>","Create a new EventBridge schedule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--schedule <expression>","Schedule expression (rate or cron)").option("--target-arn <arn>","Target ARN (Lambda, SQS, SNS, etc.)").option("--role-arn <arn>","IAM role ARN for scheduler").option("--input <json>","JSON input to pass to target").option("--group <name>","Schedule group name",{default:"default"}).option("--timezone <tz>","Timezone for cron expressions",{default:"UTC"}).option("--description <text>","Schedule description").option("--start <datetime>","Start date/time (ISO 8601)").option("--end <datetime>","End date/time (ISO 8601)").action(async(t,m)=>{i("Create EventBridge Schedule");try{if(!m.schedule){r("--schedule is required"),e("Examples:"),e(" Rate: rate(5 minutes), rate(1 hour), rate(1 day)"),e(" Cron: cron(0 12 * * ? *) - every day at 12:00 UTC");return}if(!m.targetArn){r("--target-arn is required");return}if(!m.roleArn){r("--role-arn is required"),e("The role must have permissions to invoke the target.");return}let A=new _e(m.region);if(e(`Name: ${t}`),e(`Schedule: ${m.schedule}`),e(`Target: ${m.targetArn}`),e(`Group: ${m.group}`),e(`Timezone: ${m.timezone}`),!await d(`
548
+ Create this schedule?`,!0)){e("Operation cancelled");return}let j=new o("Creating schedule...");j.start(),await A.createSchedule({Name:t,GroupName:m.group,ScheduleExpression:m.schedule,ScheduleExpressionTimezone:m.timezone,Description:m.description,State:"ENABLED",FlexibleTimeWindow:{Mode:"OFF"},Target:{Arn:m.targetArn,RoleArn:m.roleArn,Input:m.input},StartDate:m.start?new Date(m.start):void 0,EndDate:m.end?new Date(m.end):void 0}),j.succeed("Schedule created"),n(`
549
+ Schedule: ${t}`),e(`Expression: ${m.schedule}`)}catch(A){r(`Failed to create schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:delete <name>","Delete an EventBridge schedule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--group <name>","Schedule group name",{default:"default"}).action(async(t,m)=>{i("Delete EventBridge Schedule");try{let A=new _e(m.region);if(a(`This will delete schedule: ${t}`),!await d(`
550
+ Delete this schedule?`,!1)){e("Operation cancelled");return}let j=new o("Deleting schedule...");j.start(),await A.deleteSchedule({Name:t,GroupName:m.group}),j.succeed("Schedule deleted")}catch(A){r(`Failed to delete schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:enable <name>","Enable an EventBridge schedule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--group <name>","Schedule group name",{default:"default"}).action(async(t,m)=>{i("Enable EventBridge Schedule");try{let A=new _e(m.region),L=new o("Getting schedule...");L.start();let j=await A.getSchedule({Name:t,GroupName:m.group});if(!j){L.fail("Schedule not found");return}L.text="Enabling schedule...",await A.updateSchedule({Name:t,GroupName:m.group,ScheduleExpression:j.ScheduleExpression,ScheduleExpressionTimezone:j.ScheduleExpressionTimezone,FlexibleTimeWindow:j.FlexibleTimeWindow,Target:j.Target,State:"ENABLED"}),L.succeed("Schedule enabled")}catch(A){r(`Failed to enable schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:disable <name>","Disable an EventBridge schedule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--group <name>","Schedule group name",{default:"default"}).action(async(t,m)=>{i("Disable EventBridge Schedule");try{let A=new _e(m.region),L=new o("Getting schedule...");L.start();let j=await A.getSchedule({Name:t,GroupName:m.group});if(!j){L.fail("Schedule not found");return}L.text="Disabling schedule...",await A.updateSchedule({Name:t,GroupName:m.group,ScheduleExpression:j.ScheduleExpression,ScheduleExpressionTimezone:j.ScheduleExpressionTimezone,FlexibleTimeWindow:j.FlexibleTimeWindow,Target:j.Target,State:"DISABLED"}),L.succeed("Schedule disabled")}catch(A){r(`Failed to disable schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:describe <name>","Show EventBridge schedule details").option("--region <region>","AWS region",{default:"us-east-1"}).option("--group <name>","Schedule group name",{default:"default"}).action(async(t,m)=>{i(`Schedule: ${t}`);try{let A=new _e(m.region),L=new o("Fetching schedule...");L.start();let j=await A.getSchedule({Name:t,GroupName:m.group});if(!j){L.fail("Schedule not found");return}if(L.succeed("Schedule loaded"),e(`
551
551
  Schedule Information:`),e(` Name: ${j.Name}`),e(` ARN: ${j.Arn}`),e(` Group: ${j.GroupName}`),e(` State: ${j.State}`),e(` Expression: ${j.ScheduleExpression}`),e(` Timezone: ${j.ScheduleExpressionTimezone}`),j.Description)e(` Description: ${j.Description}`);if(j.Target){if(e(`
552
552
  Target:`),e(` ARN: ${j.Target.Arn}`),e(` Role ARN: ${j.Target.RoleArn}`),j.Target.Input)e(` Input: ${j.Target.Input}`)}if(e(`
553
553
  Flexible Time Window:`),e(` Mode: ${j.FlexibleTimeWindow?.Mode}`),j.FlexibleTimeWindow?.MaximumWindowInMinutes)e(` Max Window: ${j.FlexibleTimeWindow.MaximumWindowInMinutes} minutes`);if(j.StartDate)e(`
554
554
  Start Date: ${j.StartDate}`);if(j.EndDate)e(`End Date: ${j.EndDate}`);e(`
555
- Created: ${j.CreationDate||"N/A"}`),e(`Last Modified: ${j.LastModificationDate||"N/A"}`)}catch(A){r(`Failed to get schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:groups","List schedule groups").option("--region <region>","AWS region").action(async(t)=>{i("Schedule Groups");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new xe(A),j=new o("Fetching groups...");j.start();let Z=(await L.listScheduleGroups()).ScheduleGroups||[];if(j.succeed(`Found ${Z.length} group(s)`),Z.length===0){e("No schedule groups found");return}c(["Name","State","Created"],Z.map((ee)=>[ee.Name||"N/A",ee.State||"N/A",ee.CreationDate?new Date(ee.CreationDate).toLocaleString():"N/A"]))}catch(p){r(`Failed to list groups: ${p.message}`),process.exit(1)}})}function zc(N){N.command("events:list","List all EventBridge rules").option("--region <region>","AWS region").option("--bus <name>","Event bus name",{default:"default"}).action(async(t)=>{i("EventBridge Rules");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new me(A),j=new o("Fetching rules...");j.start();let Z=(await L.listRules({EventBusName:t.bus})).Rules||[];if(j.succeed(`Found ${Z.length} rule(s)`),Z.length===0){e("No EventBridge rules found"),e("Use `cloud events:create` to create a new rule");return}c(["Name","State","Schedule/Pattern","Description"],Z.map((ee)=>[ee.Name||"N/A",ee.State||"N/A",ee.ScheduleExpression||(ee.EventPattern?"Event Pattern":"N/A"),(ee.Description||"").substring(0,40)]))}catch(p){r(`Failed to list rules: ${p.message}`),process.exit(1)}}),N.command("events:create <name>","Create a new EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).option("--schedule <expression>","Schedule expression (rate or cron)").option("--pattern <json>","Event pattern JSON").option("--pattern-file <path>","Event pattern from JSON file").option("--description <text>","Rule description").option("--disabled","Create in disabled state").action(async(t,p)=>{i("Create EventBridge Rule");try{if(!p.schedule&&!p.pattern&&!p.patternFile){r("Either --schedule or --pattern/--pattern-file is required"),e(`
556
- Examples:`),e(' Schedule: --schedule "rate(5 minutes)"'),e(` Pattern: --pattern '{"source": ["aws.ec2"]}'`);return}let A=new me(p.region),L;if(p.patternFile)L=await Bun.file(p.patternFile).text();else if(p.pattern)L=p.pattern;if(e(`Name: ${t}`),e(`Event Bus: ${p.bus}`),p.schedule)e(`Schedule: ${p.schedule}`);if(L)e(`Event Pattern: ${L}`);if(e(`State: ${p.disabled?"DISABLED":"ENABLED"}`),!await d(`
557
- Create this rule?`,!0)){e("Operation cancelled");return}let G=new o("Creating rule...");G.start();let Z=await A.putRule({Name:t,EventBusName:p.bus,ScheduleExpression:p.schedule,EventPattern:L,Description:p.description,State:p.disabled?"DISABLED":"ENABLED"});G.succeed("Rule created"),n(`
558
- Rule ARN: ${Z.RuleArn}`),e("\nNote: Add targets to the rule with `cloud events:target`")}catch(A){r(`Failed to create rule: ${A.message}`),process.exit(1)}}),N.command("events:delete <name>","Delete an EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).option("--force","Remove all targets and delete rule").action(async(t,p)=>{i("Delete EventBridge Rule");try{let A=new me(p.region);if(a(`This will delete rule: ${t}`),!await d(`
559
- Delete this rule?`,!1)){e("Operation cancelled");return}let j=new o("Checking targets...");j.start();let G=await A.listTargetsByRule({Rule:t,EventBusName:p.bus});if(G.Targets&&G.Targets.length>0){if(!p.force){j.fail("Rule has targets"),e(`
560
- The rule has ${G.Targets.length} target(s).`),e("Use --force to remove targets and delete the rule.");return}j.text="Removing targets...",await A.removeTargets({Rule:t,EventBusName:p.bus,Ids:G.Targets.map((Z)=>Z.Id)})}j.text="Deleting rule...",await A.deleteRule({Name:t,EventBusName:p.bus}),j.succeed("Rule deleted")}catch(A){r(`Failed to delete rule: ${A.message}`),process.exit(1)}}),N.command("events:describe <name>","Show EventBridge rule details").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).action(async(t,p)=>{i(`Rule: ${t}`);try{let A=new me(p.region),L=new o("Fetching rule...");L.start();let j=await A.describeRule({Name:t,EventBusName:p.bus});if(!j){L.fail("Rule not found");return}let G=await A.listTargetsByRule({Rule:t,EventBusName:p.bus});if(L.succeed("Rule loaded"),e(`
555
+ Created: ${j.CreationDate||"N/A"}`),e(`Last Modified: ${j.LastModificationDate||"N/A"}`)}catch(A){r(`Failed to get schedule: ${A.message}`),process.exit(1)}}),N.command("scheduler:groups","List schedule groups").option("--region <region>","AWS region").action(async(t)=>{i("Schedule Groups");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new _e(A),j=new o("Fetching groups...");j.start();let Z=(await L.listScheduleGroups()).ScheduleGroups||[];if(j.succeed(`Found ${Z.length} group(s)`),Z.length===0){e("No schedule groups found");return}c(["Name","State","Created"],Z.map((ee)=>[ee.Name||"N/A",ee.State||"N/A",ee.CreationDate?new Date(ee.CreationDate).toLocaleString():"N/A"]))}catch(m){r(`Failed to list groups: ${m.message}`),process.exit(1)}})}function Zc(N){N.command("events:list","List all EventBridge rules").option("--region <region>","AWS region").option("--bus <name>","Event bus name",{default:"default"}).action(async(t)=>{i("EventBridge Rules");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new me(A),j=new o("Fetching rules...");j.start();let Z=(await L.listRules({EventBusName:t.bus})).Rules||[];if(j.succeed(`Found ${Z.length} rule(s)`),Z.length===0){e("No EventBridge rules found"),e("Use `cloud events:create` to create a new rule");return}c(["Name","State","Schedule/Pattern","Description"],Z.map((ee)=>[ee.Name||"N/A",ee.State||"N/A",ee.ScheduleExpression||(ee.EventPattern?"Event Pattern":"N/A"),(ee.Description||"").substring(0,40)]))}catch(m){r(`Failed to list rules: ${m.message}`),process.exit(1)}}),N.command("events:create <name>","Create a new EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).option("--schedule <expression>","Schedule expression (rate or cron)").option("--pattern <json>","Event pattern JSON").option("--pattern-file <path>","Event pattern from JSON file").option("--description <text>","Rule description").option("--disabled","Create in disabled state").action(async(t,m)=>{i("Create EventBridge Rule");try{if(!m.schedule&&!m.pattern&&!m.patternFile){r("Either --schedule or --pattern/--pattern-file is required"),e(`
556
+ Examples:`),e(' Schedule: --schedule "rate(5 minutes)"'),e(` Pattern: --pattern '{"source": ["aws.ec2"]}'`);return}let A=new me(m.region),L;if(m.patternFile)L=await Bun.file(m.patternFile).text();else if(m.pattern)L=m.pattern;if(e(`Name: ${t}`),e(`Event Bus: ${m.bus}`),m.schedule)e(`Schedule: ${m.schedule}`);if(L)e(`Event Pattern: ${L}`);if(e(`State: ${m.disabled?"DISABLED":"ENABLED"}`),!await d(`
557
+ Create this rule?`,!0)){e("Operation cancelled");return}let G=new o("Creating rule...");G.start();let Z=await A.putRule({Name:t,EventBusName:m.bus,ScheduleExpression:m.schedule,EventPattern:L,Description:m.description,State:m.disabled?"DISABLED":"ENABLED"});G.succeed("Rule created"),n(`
558
+ Rule ARN: ${Z.RuleArn}`),e("\nNote: Add targets to the rule with `cloud events:target`")}catch(A){r(`Failed to create rule: ${A.message}`),process.exit(1)}}),N.command("events:delete <name>","Delete an EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).option("--force","Remove all targets and delete rule").action(async(t,m)=>{i("Delete EventBridge Rule");try{let A=new me(m.region);if(a(`This will delete rule: ${t}`),!await d(`
559
+ Delete this rule?`,!1)){e("Operation cancelled");return}let j=new o("Checking targets...");j.start();let G=await A.listTargetsByRule({Rule:t,EventBusName:m.bus});if(G.Targets&&G.Targets.length>0){if(!m.force){j.fail("Rule has targets"),e(`
560
+ The rule has ${G.Targets.length} target(s).`),e("Use --force to remove targets and delete the rule.");return}j.text="Removing targets...",await A.removeTargets({Rule:t,EventBusName:m.bus,Ids:G.Targets.map((Z)=>Z.Id)})}j.text="Deleting rule...",await A.deleteRule({Name:t,EventBusName:m.bus}),j.succeed("Rule deleted")}catch(A){r(`Failed to delete rule: ${A.message}`),process.exit(1)}}),N.command("events:describe <name>","Show EventBridge rule details").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).action(async(t,m)=>{i(`Rule: ${t}`);try{let A=new me(m.region),L=new o("Fetching rule...");L.start();let j=await A.describeRule({Name:t,EventBusName:m.bus});if(!j){L.fail("Rule not found");return}let G=await A.listTargetsByRule({Rule:t,EventBusName:m.bus});if(L.succeed("Rule loaded"),e(`
561
561
  Rule Information:`),e(` Name: ${j.Name}`),e(` ARN: ${j.Arn}`),e(` State: ${j.State}`),e(` Event Bus: ${j.EventBusName}`),j.Description)e(` Description: ${j.Description}`);if(j.ScheduleExpression)e(`
562
562
  Schedule Expression: ${j.ScheduleExpression}`);if(j.EventPattern)e(`
563
563
  Event Pattern:`),console.log(JSON.stringify(JSON.parse(j.EventPattern),null,2));if(G.Targets&&G.Targets.length>0){e(`
564
564
  Targets (${G.Targets.length}):`);for(let Z of G.Targets)if(e(` - ${Z.Id}: ${Z.Arn}`),Z.Input)e(` Input: ${Z.Input}`)}else e(`
565
- No targets configured.`)}catch(A){r(`Failed to get rule: ${A.message}`),process.exit(1)}}),N.command("events:target <ruleName>","Add a target to an EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).option("--id <id>","Target ID").option("--arn <arn>","Target ARN (Lambda, SQS, SNS, etc.)").option("--role <arn>","IAM role ARN (for some targets)").option("--input <json>","Constant JSON input").option("--input-path <path>","JSONPath expression for input").action(async(t,p)=>{i("Add EventBridge Target");try{if(!p.arn){r("--arn is required");return}let A=new me(p.region),L=p.id||`target-${Date.now()}`;if(e(`Rule: ${t}`),e(`Target ID: ${L}`),e(`Target ARN: ${p.arn}`),!await d(`
566
- Add this target?`,!0)){e("Operation cancelled");return}let G=new o("Adding target...");G.start();let Z={Id:L,Arn:p.arn};if(p.role)Z.RoleArn=p.role;if(p.input)Z.Input=p.input;if(p.inputPath)Z.InputPath=p.inputPath;await A.putTargets({Rule:t,EventBusName:p.bus,Targets:[Z]}),G.succeed("Target added"),n(`
567
- Target ${L} added to rule ${t}`)}catch(A){r(`Failed to add target: ${A.message}`),process.exit(1)}}),N.command("events:buses","List event buses").option("--region <region>","AWS region").action(async(t)=>{i("Event Buses");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new me(A),j=new o("Fetching event buses...");j.start();let Z=(await L.listEventBuses()).EventBuses||[];if(j.succeed(`Found ${Z.length} event bus(es)`),Z.length===0){e("No event buses found");return}c(["Name","ARN","Policy"],Z.map((ee)=>[ee.Name||"N/A",ee.Arn||"N/A",ee.Policy?"Custom":"Default"]))}catch(p){r(`Failed to list event buses: ${p.message}`),process.exit(1)}}),N.command("events:enable <name>","Enable an EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).action(async(t,p)=>{i("Enable EventBridge Rule");try{let A=new me(p.region),L=new o("Enabling rule...");L.start(),await A.enableRule({Name:t,EventBusName:p.bus}),L.succeed("Rule enabled")}catch(A){r(`Failed to enable rule: ${A.message}`),process.exit(1)}}),N.command("events:disable <name>","Disable an EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).action(async(t,p)=>{i("Disable EventBridge Rule");try{let A=new me(p.region),L=new o("Disabling rule...");L.start(),await A.disableRule({Name:t,EventBusName:p.bus}),L.succeed("Rule disabled")}catch(A){r(`Failed to disable rule: ${A.message}`),process.exit(1)}})}function Uc(N){N.command("email:identities","List verified email identities").option("--region <region>","AWS region").action(async(t)=>{i("Email Identities");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new ye(A),j=new o("Fetching identities...");j.start();let Z=(await L.listEmailIdentities()).EmailIdentities||[];if(j.succeed(`Found ${Z.length} identity(s)`),Z.length===0){e("No email identities found"),e("Use `cloud email:verify` to verify an email or domain");return}let ee=[];for(let re of Z){let oe=re.IdentityName||"Unknown",be="Unknown";try{be=(await L.getEmailIdentity(oe)).VerificationStatus||"Unknown"}catch{}let we=re.IdentityType==="EMAIL_ADDRESS"?"Email":re.IdentityType==="DOMAIN"?"Domain":re.IdentityType||"Unknown";ee.push([oe,we,be])}c(["Identity","Type","Verification Status"],ee)}catch(p){let A=p instanceof Error?p.message:String(p);r(`Failed to list identities: ${A}`),process.exit(1)}}),N.command("email:verify <identity>","Verify an email address or domain").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Verify Email Identity");try{let A=new ye(p.region),L=t.includes("@");if(e(`Identity: ${t}`),e(`Type: ${L?"Email Address":"Domain"}`),!await d(`
565
+ No targets configured.`)}catch(A){r(`Failed to get rule: ${A.message}`),process.exit(1)}}),N.command("events:target <ruleName>","Add a target to an EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).option("--id <id>","Target ID").option("--arn <arn>","Target ARN (Lambda, SQS, SNS, etc.)").option("--role <arn>","IAM role ARN (for some targets)").option("--input <json>","Constant JSON input").option("--input-path <path>","JSONPath expression for input").action(async(t,m)=>{i("Add EventBridge Target");try{if(!m.arn){r("--arn is required");return}let A=new me(m.region),L=m.id||`target-${Date.now()}`;if(e(`Rule: ${t}`),e(`Target ID: ${L}`),e(`Target ARN: ${m.arn}`),!await d(`
566
+ Add this target?`,!0)){e("Operation cancelled");return}let G=new o("Adding target...");G.start();let Z={Id:L,Arn:m.arn};if(m.role)Z.RoleArn=m.role;if(m.input)Z.Input=m.input;if(m.inputPath)Z.InputPath=m.inputPath;await A.putTargets({Rule:t,EventBusName:m.bus,Targets:[Z]}),G.succeed("Target added"),n(`
567
+ Target ${L} added to rule ${t}`)}catch(A){r(`Failed to add target: ${A.message}`),process.exit(1)}}),N.command("events:buses","List event buses").option("--region <region>","AWS region").action(async(t)=>{i("Event Buses");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new me(A),j=new o("Fetching event buses...");j.start();let Z=(await L.listEventBuses()).EventBuses||[];if(j.succeed(`Found ${Z.length} event bus(es)`),Z.length===0){e("No event buses found");return}c(["Name","ARN","Policy"],Z.map((ee)=>[ee.Name||"N/A",ee.Arn||"N/A",ee.Policy?"Custom":"Default"]))}catch(m){r(`Failed to list event buses: ${m.message}`),process.exit(1)}}),N.command("events:enable <name>","Enable an EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).action(async(t,m)=>{i("Enable EventBridge Rule");try{let A=new me(m.region),L=new o("Enabling rule...");L.start(),await A.enableRule({Name:t,EventBusName:m.bus}),L.succeed("Rule enabled")}catch(A){r(`Failed to enable rule: ${A.message}`),process.exit(1)}}),N.command("events:disable <name>","Disable an EventBridge rule").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bus <name>","Event bus name",{default:"default"}).action(async(t,m)=>{i("Disable EventBridge Rule");try{let A=new me(m.region),L=new o("Disabling rule...");L.start(),await A.disableRule({Name:t,EventBusName:m.bus}),L.succeed("Rule disabled")}catch(A){r(`Failed to disable rule: ${A.message}`),process.exit(1)}})}function Qc(N){N.command("email:identities","List verified email identities").option("--region <region>","AWS region").action(async(t)=>{i("Email Identities");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new ye(A),j=new o("Fetching identities...");j.start();let Z=(await L.listEmailIdentities()).EmailIdentities||[];if(j.succeed(`Found ${Z.length} identity(s)`),Z.length===0){e("No email identities found"),e("Use `cloud email:verify` to verify an email or domain");return}let ee=[];for(let ne of Z){let oe=ne.IdentityName||"Unknown",be="Unknown";try{be=(await L.getEmailIdentity(oe)).VerificationStatus||"Unknown"}catch{}let we=ne.IdentityType==="EMAIL_ADDRESS"?"Email":ne.IdentityType==="DOMAIN"?"Domain":ne.IdentityType||"Unknown";ee.push([oe,we,be])}c(["Identity","Type","Verification Status"],ee)}catch(m){let A=m instanceof Error?m.message:String(m);r(`Failed to list identities: ${A}`),process.exit(1)}}),N.command("email:verify <identity>","Verify an email address or domain").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Verify Email Identity");try{let A=new ye(m.region),L=t.includes("@");if(e(`Identity: ${t}`),e(`Type: ${L?"Email Address":"Domain"}`),!await d(`
568
568
  Send verification?`,!0)){e("Operation cancelled");return}let G=new o("Initiating verification...");if(G.start(),L)await A.createEmailIdentity({EmailIdentity:t}),G.succeed("Verification email sent"),e(`
569
569
  A verification email has been sent to ${t}`),e("Click the link in the email to complete verification.");else{let Z=await A.verifyDomain(t);G.succeed("Domain verification initiated");let ee=Z.dkimTokens||[];if(ee.length>0){e(`
570
- DKIM Records (for email authentication):`);for(let re of ee)e(`
571
- Name: ${re}._domainkey.${t}`),e(" Type: CNAME"),e(` Value: ${re}.dkim.amazonses.com`)}e(`
572
- Verification Status: ${Z.verificationStatus||"PENDING"}`)}}catch(A){let L=A instanceof Error?A.message:String(A);r(`Failed to verify identity: ${L}`),process.exit(1)}}),N.command("email:delete <identity>","Delete a verified email identity").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Delete Email Identity");try{let A=new ye(p.region);if(a(`This will remove identity: ${t}`),!await d(`
573
- Delete this identity?`,!1)){e("Operation cancelled");return}let j=new o("Deleting identity...");j.start(),await A.deleteEmailIdentity(t),j.succeed("Identity deleted")}catch(A){let L=A instanceof Error?A.message:String(A);r(`Failed to delete identity: ${L}`),process.exit(1)}}),N.command("email:send","Send a test email").option("--region <region>","AWS region",{default:"us-east-1"}).option("--from <email>","From email address (must be verified)").option("--to <email>","To email address").option("--subject <text>","Email subject").option("--body <text>","Email body (text)").option("--html <html>","Email body (HTML)").action(async(t)=>{i("Send Email");try{let p=new ye(t.region),A=t.from||await I("From (verified email)"),L=t.to||await I("To"),j=t.subject||await I("Subject","Test Email from ts-cloud"),G=t.body||await I("Body","This is a test email sent from ts-cloud CLI.");if(e(`
570
+ DKIM Records (for email authentication):`);for(let ne of ee)e(`
571
+ Name: ${ne}._domainkey.${t}`),e(" Type: CNAME"),e(` Value: ${ne}.dkim.amazonses.com`)}e(`
572
+ Verification Status: ${Z.verificationStatus||"PENDING"}`)}}catch(A){let L=A instanceof Error?A.message:String(A);r(`Failed to verify identity: ${L}`),process.exit(1)}}),N.command("email:delete <identity>","Delete a verified email identity").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Delete Email Identity");try{let A=new ye(m.region);if(a(`This will remove identity: ${t}`),!await d(`
573
+ Delete this identity?`,!1)){e("Operation cancelled");return}let j=new o("Deleting identity...");j.start(),await A.deleteEmailIdentity(t),j.succeed("Identity deleted")}catch(A){let L=A instanceof Error?A.message:String(A);r(`Failed to delete identity: ${L}`),process.exit(1)}}),N.command("email:send","Send a test email").option("--region <region>","AWS region",{default:"us-east-1"}).option("--from <email>","From email address (must be verified)").option("--to <email>","To email address").option("--subject <text>","Email subject").option("--body <text>","Email body (text)").option("--html <html>","Email body (HTML)").action(async(t)=>{i("Send Email");try{let m=new ye(t.region),A=t.from||await I("From (verified email)"),L=t.to||await I("To"),j=t.subject||await I("Subject","Test Email from ts-cloud"),G=t.body||await I("Body","This is a test email sent from ts-cloud CLI.");if(e(`
574
574
  From: ${A}`),e(`To: ${L}`),e(`Subject: ${j}`),!await d(`
575
- Send this email?`,!0)){e("Operation cancelled");return}let ee=new o("Sending email...");ee.start();let re=await p.sendEmail({FromEmailAddress:A,Destination:{ToAddresses:[L]},Content:{Simple:{Subject:{Data:j},Body:{Text:{Data:G},...t.html&&{Html:{Data:t.html}}}}}});ee.succeed("Email sent"),n(`
576
- Message ID: ${re.MessageId}`)}catch(p){let A=p instanceof Error?p.message:String(p);r(`Failed to send email: ${A}`),process.exit(1)}}),N.command("email:templates","List email templates").option("--region <region>","AWS region").action(async(t)=>{i("Email Templates");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new ye(A),j=new o("Fetching templates...");j.start();let Z=(await L.listEmailTemplates()).TemplatesMetadata||[];if(j.succeed(`Found ${Z.length} template(s)`),Z.length===0){e("No email templates found"),e("Use `cloud email:template:create` to create a template");return}c(["Name","Created"],Z.map((ee)=>[ee.TemplateName||"N/A",ee.CreatedTimestamp?new Date(ee.CreatedTimestamp).toLocaleString():"N/A"]))}catch(p){let A=p instanceof Error?p.message:String(p);r(`Failed to list templates: ${A}`),process.exit(1)}}),N.command("email:template:create <name>","Create an email template").option("--region <region>","AWS region",{default:"us-east-1"}).option("--subject <text>","Email subject (supports {{variable}} placeholders)").option("--text <text>","Text body").option("--html <html>","HTML body").option("--html-file <path>","HTML body from file").action(async(t,p)=>{i("Create Email Template");try{let A=new ye(p.region),L=p.subject||await I("Subject template","Hello {{name}}"),j=p.text||await I("Text body","Hello {{name}}, this is a test."),G=p.html;if(p.htmlFile)G=await Bun.file(p.htmlFile).text();if(e(`
575
+ Send this email?`,!0)){e("Operation cancelled");return}let ee=new o("Sending email...");ee.start();let ne=await m.sendEmail({FromEmailAddress:A,Destination:{ToAddresses:[L]},Content:{Simple:{Subject:{Data:j},Body:{Text:{Data:G},...t.html&&{Html:{Data:t.html}}}}}});ee.succeed("Email sent"),n(`
576
+ Message ID: ${ne.MessageId}`)}catch(m){let A=m instanceof Error?m.message:String(m);r(`Failed to send email: ${A}`),process.exit(1)}}),N.command("email:templates","List email templates").option("--region <region>","AWS region").action(async(t)=>{i("Email Templates");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new ye(A),j=new o("Fetching templates...");j.start();let Z=(await L.listEmailTemplates()).TemplatesMetadata||[];if(j.succeed(`Found ${Z.length} template(s)`),Z.length===0){e("No email templates found"),e("Use `cloud email:template:create` to create a template");return}c(["Name","Created"],Z.map((ee)=>[ee.TemplateName||"N/A",ee.CreatedTimestamp?new Date(ee.CreatedTimestamp).toLocaleString():"N/A"]))}catch(m){let A=m instanceof Error?m.message:String(m);r(`Failed to list templates: ${A}`),process.exit(1)}}),N.command("email:template:create <name>","Create an email template").option("--region <region>","AWS region",{default:"us-east-1"}).option("--subject <text>","Email subject (supports {{variable}} placeholders)").option("--text <text>","Text body").option("--html <html>","HTML body").option("--html-file <path>","HTML body from file").action(async(t,m)=>{i("Create Email Template");try{let A=new ye(m.region),L=m.subject||await I("Subject template","Hello {{name}}"),j=m.text||await I("Text body","Hello {{name}}, this is a test."),G=m.html;if(m.htmlFile)G=await Bun.file(m.htmlFile).text();if(e(`
577
577
  Template Name: ${t}`),e(`Subject: ${L}`),!await d(`
578
578
  Create this template?`,!0)){e("Operation cancelled");return}let ee=new o("Creating template...");ee.start(),await A.createEmailTemplate({TemplateName:t,TemplateContent:{Subject:L,Text:j,Html:G}}),ee.succeed("Template created"),e(`
579
- To send using this template:`),e(` cloud email:send:template --template ${t} --to user@example.com --data '{"name":"John"}'`)}catch(A){let L=A instanceof Error?A.message:String(A);r(`Failed to create template: ${L}`),process.exit(1)}}),N.command("email:template:delete <name>","Delete an email template").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Delete Email Template");try{let A=new ye(p.region);if(a(`This will delete template: ${t}`),!await d(`
580
- Delete this template?`,!1)){e("Operation cancelled");return}let j=new o("Deleting template...");j.start(),await A.deleteEmailTemplate(t),j.succeed("Template deleted")}catch(A){let L=A instanceof Error?A.message:String(A);r(`Failed to delete template: ${L}`),process.exit(1)}}),N.command("email:stats","Show SES sending statistics").option("--region <region>","AWS region").action(async(t)=>{i("Email Statistics");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new ye(A),j=new o("Fetching statistics...");j.start();let[G,Z]=await Promise.all([L.getSendQuota(),L.getSendStatistics()]);j.succeed("Statistics loaded"),e(`
579
+ To send using this template:`),e(` cloud email:send:template --template ${t} --to user@example.com --data '{"name":"John"}'`)}catch(A){let L=A instanceof Error?A.message:String(A);r(`Failed to create template: ${L}`),process.exit(1)}}),N.command("email:template:delete <name>","Delete an email template").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Delete Email Template");try{let A=new ye(m.region);if(a(`This will delete template: ${t}`),!await d(`
580
+ Delete this template?`,!1)){e("Operation cancelled");return}let j=new o("Deleting template...");j.start(),await A.deleteEmailTemplate(t),j.succeed("Template deleted")}catch(A){let L=A instanceof Error?A.message:String(A);r(`Failed to delete template: ${L}`),process.exit(1)}}),N.command("email:stats","Show SES sending statistics").option("--region <region>","AWS region").action(async(t)=>{i("Email Statistics");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new ye(A),j=new o("Fetching statistics...");j.start();let[G,Z]=await Promise.all([L.getSendQuota(),L.getSendStatistics()]);j.succeed("Statistics loaded"),e(`
581
581
  Sending Quota:`),e(` Max 24-hour Send: ${G.Max24HourSend||0}`),e(` Max Send Rate: ${G.MaxSendRate||0} emails/second`),e(` Sent Last 24h: ${G.SentLast24Hours||0}`);let ee=(G.Max24HourSend||0)-(G.SentLast24Hours||0);if(e(` Remaining: ${ee}`),Z.SendDataPoints&&Z.SendDataPoints.length>0){e(`
582
- Recent Statistics:`);let re=0,oe=0,be=0,we=0;for(let Ce of Z.SendDataPoints)re+=Ce.DeliveryAttempts||0,oe+=Ce.Bounces||0,be+=Ce.Complaints||0,we+=Ce.Rejects||0;if(e(` Total Attempts: ${re}`),e(` Bounces: ${oe}`),e(` Complaints: ${be}`),e(` Rejects: ${we}`),re>0){let Ce=(oe/re*100).toFixed(2),Ee=(be/re*100).toFixed(2);if(e(`
583
- Bounce Rate: ${Ce}%`),e(` Complaint Rate: ${Ee}%`),Number.parseFloat(Ce)>5)a(`
584
- Warning: Bounce rate is high (>5%). This may affect your sender reputation.`);if(Number.parseFloat(Ee)>0.1)a(`
585
- Warning: Complaint rate is high (>0.1%). This may affect your sender reputation.`)}}}catch(p){let A=p instanceof Error?p.message:String(p);r(`Failed to get statistics: ${A}`),process.exit(1)}})}function Vc(N){N.command("notify:topics","List all SNS topics").option("--region <region>","AWS region").action(async(t)=>{i("SNS Topics");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=new ce(A),j=new o("Fetching topics...");j.start();let Z=(await L.listTopics()).Topics||[];if(j.succeed(`Found ${Z.length} topic(s)`),Z.length===0){e("No SNS topics found"),e("Use `cloud notify:create` to create a new topic");return}c(["Topic ARN","Name"],Z.map((ee)=>{let re=ee.TopicArn?.split(":").pop()||"N/A";return[ee.TopicArn||"N/A",re]}))}catch(p){r(`Failed to list topics: ${p.message}`),process.exit(1)}}),N.command("notify:create <name>","Create a new SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).option("--fifo","Create a FIFO topic").option("--display-name <name>","Display name for SMS subscriptions").action(async(t,p)=>{i("Create SNS Topic");try{let A=new ce(p.region),L=p.fifo&&!t.endsWith(".fifo")?`${t}.fifo`:t;if(e(`Topic name: ${L}`),e(`Type: ${p.fifo?"FIFO":"Standard"}`),!await d(`
586
- Create this topic?`,!0)){e("Operation cancelled");return}let G=new o("Creating topic...");G.start();let Z={};if(p.fifo)Z.FifoTopic="true",Z.ContentBasedDeduplication="true";if(p.displayName)Z.DisplayName=p.displayName;let ee=await A.createTopic({Name:L,Attributes:Object.keys(Z).length>0?Z:void 0});G.succeed("Topic created"),n(`
582
+ Recent Statistics:`);let ne=0,oe=0,be=0,we=0;for(let Ce of Z.SendDataPoints)ne+=Ce.DeliveryAttempts||0,oe+=Ce.Bounces||0,be+=Ce.Complaints||0,we+=Ce.Rejects||0;if(e(` Total Attempts: ${ne}`),e(` Bounces: ${oe}`),e(` Complaints: ${be}`),e(` Rejects: ${we}`),ne>0){let Ce=(oe/ne*100).toFixed(2),De=(be/ne*100).toFixed(2);if(e(`
583
+ Bounce Rate: ${Ce}%`),e(` Complaint Rate: ${De}%`),Number.parseFloat(Ce)>5)a(`
584
+ Warning: Bounce rate is high (>5%). This may affect your sender reputation.`);if(Number.parseFloat(De)>0.1)a(`
585
+ Warning: Complaint rate is high (>0.1%). This may affect your sender reputation.`)}}}catch(m){let A=m instanceof Error?m.message:String(m);r(`Failed to get statistics: ${A}`),process.exit(1)}})}function Xc(N){N.command("notify:topics","List all SNS topics").option("--region <region>","AWS region").action(async(t)=>{i("SNS Topics");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=new ce(A),j=new o("Fetching topics...");j.start();let Z=(await L.listTopics()).Topics||[];if(j.succeed(`Found ${Z.length} topic(s)`),Z.length===0){e("No SNS topics found"),e("Use `cloud notify:create` to create a new topic");return}c(["Topic ARN","Name"],Z.map((ee)=>{let ne=ee.TopicArn?.split(":").pop()||"N/A";return[ee.TopicArn||"N/A",ne]}))}catch(m){r(`Failed to list topics: ${m.message}`),process.exit(1)}}),N.command("notify:create <name>","Create a new SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).option("--fifo","Create a FIFO topic").option("--display-name <name>","Display name for SMS subscriptions").action(async(t,m)=>{i("Create SNS Topic");try{let A=new ce(m.region),L=m.fifo&&!t.endsWith(".fifo")?`${t}.fifo`:t;if(e(`Topic name: ${L}`),e(`Type: ${m.fifo?"FIFO":"Standard"}`),!await d(`
586
+ Create this topic?`,!0)){e("Operation cancelled");return}let G=new o("Creating topic...");G.start();let Z={};if(m.fifo)Z.FifoTopic="true",Z.ContentBasedDeduplication="true";if(m.displayName)Z.DisplayName=m.displayName;let ee=await A.createTopic({Name:L,Attributes:Object.keys(Z).length>0?Z:void 0});G.succeed("Topic created"),n(`
587
587
  Topic ARN: ${ee.TopicArn}`),e(`
588
- To subscribe:`),e(` cloud notify:subscribe ${ee.TopicArn} --email user@example.com`)}catch(A){r(`Failed to create topic: ${A.message}`),process.exit(1)}}),N.command("notify:delete <topicArn>","Delete an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Delete SNS Topic");try{let A=new ce(p.region);if(a(`This will delete topic: ${t}`),a("All subscriptions will be removed."),!await d(`
589
- Delete this topic?`,!1)){e("Operation cancelled");return}let j=new o("Deleting topic...");j.start(),await A.deleteTopic(t),j.succeed("Topic deleted")}catch(A){r(`Failed to delete topic: ${A.message}`),process.exit(1)}}),N.command("notify:subscribe <topicArn>","Subscribe to an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).option("--email <address>","Email address").option("--sms <number>","Phone number (E.164 format)").option("--sqs <queueArn>","SQS queue ARN").option("--lambda <functionArn>","Lambda function ARN").option("--https <url>","HTTPS endpoint URL").option("--filter <json>","Filter policy JSON").action(async(t,p)=>{i("Subscribe to SNS Topic");try{let A=new ce(p.region),L,j;if(p.email)L="email",j=p.email;else if(p.sms)L="sms",j=p.sms;else if(p.sqs)L="sqs",j=p.sqs;else if(p.lambda)L="lambda",j=p.lambda;else if(p.https)L="https",j=p.https;else{r("Specify a subscription type: --email, --sms, --sqs, --lambda, or --https");return}if(e(`Topic: ${t}`),e(`Protocol: ${L}`),e(`Endpoint: ${j}`),!await d(`
590
- Create this subscription?`,!0)){e("Operation cancelled");return}let Z=new o("Creating subscription...");Z.start();let ee={TopicArn:t,Protocol:L,Endpoint:j};if(p.filter)ee.Attributes={FilterPolicy:p.filter};let re=await A.subscribe(ee);if(Z.succeed("Subscription created"),re.SubscriptionArn==="pending confirmation")e(`
588
+ To subscribe:`),e(` cloud notify:subscribe ${ee.TopicArn} --email user@example.com`)}catch(A){r(`Failed to create topic: ${A.message}`),process.exit(1)}}),N.command("notify:delete <topicArn>","Delete an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Delete SNS Topic");try{let A=new ce(m.region);if(a(`This will delete topic: ${t}`),a("All subscriptions will be removed."),!await d(`
589
+ Delete this topic?`,!1)){e("Operation cancelled");return}let j=new o("Deleting topic...");j.start(),await A.deleteTopic(t),j.succeed("Topic deleted")}catch(A){r(`Failed to delete topic: ${A.message}`),process.exit(1)}}),N.command("notify:subscribe <topicArn>","Subscribe to an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).option("--email <address>","Email address").option("--sms <number>","Phone number (E.164 format)").option("--sqs <queueArn>","SQS queue ARN").option("--lambda <functionArn>","Lambda function ARN").option("--https <url>","HTTPS endpoint URL").option("--filter <json>","Filter policy JSON").action(async(t,m)=>{i("Subscribe to SNS Topic");try{let A=new ce(m.region),L,j;if(m.email)L="email",j=m.email;else if(m.sms)L="sms",j=m.sms;else if(m.sqs)L="sqs",j=m.sqs;else if(m.lambda)L="lambda",j=m.lambda;else if(m.https)L="https",j=m.https;else{r("Specify a subscription type: --email, --sms, --sqs, --lambda, or --https");return}if(e(`Topic: ${t}`),e(`Protocol: ${L}`),e(`Endpoint: ${j}`),!await d(`
590
+ Create this subscription?`,!0)){e("Operation cancelled");return}let Z=new o("Creating subscription...");Z.start();let ee={TopicArn:t,Protocol:L,Endpoint:j};if(m.filter)ee.Attributes={FilterPolicy:m.filter};let ne=await A.subscribe(ee);if(Z.succeed("Subscription created"),ne.SubscriptionArn==="pending confirmation")e(`
591
591
  Subscription is pending confirmation.`),e("The subscriber will receive a confirmation message.");else n(`
592
- Subscription ARN: ${re.SubscriptionArn}`)}catch(A){r(`Failed to subscribe: ${A.message}`),process.exit(1)}}),N.command("notify:unsubscribe <subscriptionArn>","Unsubscribe from an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Unsubscribe from SNS Topic");try{let A=new ce(p.region);if(a(`This will remove subscription: ${t}`),!await d(`
593
- Unsubscribe?`,!1)){e("Operation cancelled");return}let j=new o("Unsubscribing...");j.start(),await A.unsubscribe(t),j.succeed("Unsubscribed")}catch(A){r(`Failed to unsubscribe: ${A.message}`),process.exit(1)}}),N.command("notify:subscriptions <topicArn>","List subscriptions for a topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Topic Subscriptions");try{let A=new ce(p.region),L=new o("Fetching subscriptions...");L.start();let G=(await A.listSubscriptionsByTopic(t)).Subscriptions||[];if(L.succeed(`Found ${G.length} subscription(s)`),G.length===0){e("No subscriptions found");return}c(["Protocol","Endpoint","Status","Subscription ARN"],G.map((Z)=>[Z.Protocol||"N/A",Z.Endpoint||"N/A",Z.SubscriptionArn==="PendingConfirmation"?"Pending":"Confirmed",(Z.SubscriptionArn||"N/A").substring(0,50)]))}catch(A){r(`Failed to list subscriptions: ${A.message}`),process.exit(1)}}),N.command("notify:publish <topicArn>","Publish a message to an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).option("--message <text>","Message body").option("--subject <text>","Message subject (for email)").option("--file <path>","Read message from file").option("--json","Send as JSON message structure").option("--group <id>","Message group ID (for FIFO topics)").option("--dedup <id>","Deduplication ID (for FIFO topics)").action(async(t,p)=>{i("Publish to SNS Topic");try{let A=new ce(p.region),L;if(p.file)L=await Bun.file(p.file).text();else if(p.message)L=p.message;else L=await I("Message");if(!L){r("Message is required");return}if(e(`Topic: ${t}`),e(`Message length: ${L.length} characters`),!await d(`
594
- Publish this message?`,!0)){e("Operation cancelled");return}let G=new o("Publishing message...");G.start();let Z={TopicArn:t,Message:L};if(p.subject)Z.Subject=p.subject;if(p.json)Z.MessageStructure="json";if(p.group)Z.MessageGroupId=p.group;if(p.dedup)Z.MessageDeduplicationId=p.dedup;let ee=await A.publish(Z);G.succeed("Message published"),n(`
595
- Message ID: ${ee.MessageId}`)}catch(A){r(`Failed to publish message: ${A.message}`),process.exit(1)}}),N.command("notify:sms <phoneNumber>","Send an SMS message directly").option("--region <region>","AWS region",{default:"us-east-1"}).option("--message <text>","Message body").option("--sender <id>","Sender ID or short code").option("--type <type>","Message type (Transactional or Promotional)",{default:"Transactional"}).action(async(t,p)=>{i("Send SMS");try{let A=new ce(p.region),L=p.message||await I("Message");if(!L){r("Message is required");return}if(e(`To: ${t}`),e(`Message: ${L}`),e(`Type: ${p.type}`),!await d(`
596
- Send this SMS?`,!0)){e("Operation cancelled");return}let G=new o("Sending SMS...");G.start();let Z={PhoneNumber:t,Message:L,MessageAttributes:{"AWS.SNS.SMS.SMSType":{DataType:"String",StringValue:p.type}}};if(p.sender)Z.MessageAttributes["AWS.SNS.SMS.SenderID"]={DataType:"String",StringValue:p.sender};let ee=await A.publish(Z);G.succeed("SMS sent"),n(`
597
- Message ID: ${ee.MessageId}`)}catch(A){r(`Failed to send SMS: ${A.message}`),process.exit(1)}}),N.command("notify:topic:attributes <topicArn>","Show topic attributes").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Topic Attributes");try{let A=new ce(p.region),L=new o("Fetching attributes...");L.start();let j=await A.getTopicAttributes(t);if(L.succeed("Attributes loaded"),e(`
592
+ Subscription ARN: ${ne.SubscriptionArn}`)}catch(A){r(`Failed to subscribe: ${A.message}`),process.exit(1)}}),N.command("notify:unsubscribe <subscriptionArn>","Unsubscribe from an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Unsubscribe from SNS Topic");try{let A=new ce(m.region);if(a(`This will remove subscription: ${t}`),!await d(`
593
+ Unsubscribe?`,!1)){e("Operation cancelled");return}let j=new o("Unsubscribing...");j.start(),await A.unsubscribe(t),j.succeed("Unsubscribed")}catch(A){r(`Failed to unsubscribe: ${A.message}`),process.exit(1)}}),N.command("notify:subscriptions <topicArn>","List subscriptions for a topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Topic Subscriptions");try{let A=new ce(m.region),L=new o("Fetching subscriptions...");L.start();let G=(await A.listSubscriptionsByTopic(t)).Subscriptions||[];if(L.succeed(`Found ${G.length} subscription(s)`),G.length===0){e("No subscriptions found");return}c(["Protocol","Endpoint","Status","Subscription ARN"],G.map((Z)=>[Z.Protocol||"N/A",Z.Endpoint||"N/A",Z.SubscriptionArn==="PendingConfirmation"?"Pending":"Confirmed",(Z.SubscriptionArn||"N/A").substring(0,50)]))}catch(A){r(`Failed to list subscriptions: ${A.message}`),process.exit(1)}}),N.command("notify:publish <topicArn>","Publish a message to an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).option("--message <text>","Message body").option("--subject <text>","Message subject (for email)").option("--file <path>","Read message from file").option("--json","Send as JSON message structure").option("--group <id>","Message group ID (for FIFO topics)").option("--dedup <id>","Deduplication ID (for FIFO topics)").action(async(t,m)=>{i("Publish to SNS Topic");try{let A=new ce(m.region),L;if(m.file)L=await Bun.file(m.file).text();else if(m.message)L=m.message;else L=await I("Message");if(!L){r("Message is required");return}if(e(`Topic: ${t}`),e(`Message length: ${L.length} characters`),!await d(`
594
+ Publish this message?`,!0)){e("Operation cancelled");return}let G=new o("Publishing message...");G.start();let Z={TopicArn:t,Message:L};if(m.subject)Z.Subject=m.subject;if(m.json)Z.MessageStructure="json";if(m.group)Z.MessageGroupId=m.group;if(m.dedup)Z.MessageDeduplicationId=m.dedup;let ee=await A.publish(Z);G.succeed("Message published"),n(`
595
+ Message ID: ${ee.MessageId}`)}catch(A){r(`Failed to publish message: ${A.message}`),process.exit(1)}}),N.command("notify:sms <phoneNumber>","Send an SMS message directly").option("--region <region>","AWS region",{default:"us-east-1"}).option("--message <text>","Message body").option("--sender <id>","Sender ID or short code").option("--type <type>","Message type (Transactional or Promotional)",{default:"Transactional"}).action(async(t,m)=>{i("Send SMS");try{let A=new ce(m.region),L=m.message||await I("Message");if(!L){r("Message is required");return}if(e(`To: ${t}`),e(`Message: ${L}`),e(`Type: ${m.type}`),!await d(`
596
+ Send this SMS?`,!0)){e("Operation cancelled");return}let G=new o("Sending SMS...");G.start();let Z={PhoneNumber:t,Message:L,MessageAttributes:{"AWS.SNS.SMS.SMSType":{DataType:"String",StringValue:m.type}}};if(m.sender)Z.MessageAttributes["AWS.SNS.SMS.SenderID"]={DataType:"String",StringValue:m.sender};let ee=await A.publish(Z);G.succeed("SMS sent"),n(`
597
+ Message ID: ${ee.MessageId}`)}catch(A){r(`Failed to send SMS: ${A.message}`),process.exit(1)}}),N.command("notify:topic:attributes <topicArn>","Show topic attributes").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,m)=>{i("Topic Attributes");try{let A=new ce(m.region),L=new o("Fetching attributes...");L.start();let j=await A.getTopicAttributes(t);if(L.succeed("Attributes loaded"),e(`
598
598
  Topic Information:`),e(` ARN: ${t}`),e(` Display Name: ${j.DisplayName||"Not set"}`),e(` Owner: ${j.Owner||"N/A"}`),e(`
599
599
  Subscriptions:`),e(` Confirmed: ${j.SubscriptionsConfirmed||0}`),e(` Pending: ${j.SubscriptionsPending||0}`),e(` Deleted: ${j.SubscriptionsDeleted||0}`),e(`
600
600
  Settings:`),e(` FIFO: ${j.FifoTopic==="true"?"Yes":"No"}`),e(` Content Deduplication: ${j.ContentBasedDeduplication==="true"?"Yes":"No"}`),j.EffectiveDeliveryPolicy)e(`
601
601
  Delivery Policy: Configured`);if(j.Policy)e(`
602
- Access Policy: Configured`)}catch(A){r(`Failed to get attributes: ${A.message}`),process.exit(1)}})}function Wc(N){N.command("iam:roles","List IAM roles").option("--prefix <prefix>","Filter by role name prefix").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(t)=>{i("IAM Roles");try{let p=new Y,A=new o("Fetching roles...");A.start();let j=(await p.listRoles({PathPrefix:t.path})).Roles||[];if(t.prefix)j=j.filter((G)=>G.RoleName?.startsWith(t.prefix));if(A.succeed(`Found ${j.length} role(s)`),j.length===0){e("No IAM roles found");return}c(["Role Name","Path","Created","Description"],j.map((G)=>[G.RoleName||"N/A",G.Path||"/",G.CreateDate?new Date(G.CreateDate).toLocaleDateString():"N/A",(G.Description||"").substring(0,40)]))}catch(p){r(`Failed to list roles: ${p.message}`),process.exit(1)}}),N.command("iam:role <roleName>","Show IAM role details").action(async(t)=>{i(`IAM Role: ${t}`);try{let p=new Y,A=new o("Fetching role details...");A.start();let L=await p.getRole({RoleName:t}),j=await p.listAttachedRolePolicies({RoleName:t}),G=await p.listRolePolicies({RoleName:t});if(A.succeed("Role details loaded"),e(`
602
+ Access Policy: Configured`)}catch(A){r(`Failed to get attributes: ${A.message}`),process.exit(1)}})}function el(N){N.command("iam:roles","List IAM roles").option("--prefix <prefix>","Filter by role name prefix").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(t)=>{i("IAM Roles");try{let m=new Y,A=new o("Fetching roles...");A.start();let j=(await m.listRoles({PathPrefix:t.path})).Roles||[];if(t.prefix)j=j.filter((G)=>G.RoleName?.startsWith(t.prefix));if(A.succeed(`Found ${j.length} role(s)`),j.length===0){e("No IAM roles found");return}c(["Role Name","Path","Created","Description"],j.map((G)=>[G.RoleName||"N/A",G.Path||"/",G.CreateDate?new Date(G.CreateDate).toLocaleDateString():"N/A",(G.Description||"").substring(0,40)]))}catch(m){r(`Failed to list roles: ${m.message}`),process.exit(1)}}),N.command("iam:role <roleName>","Show IAM role details").action(async(t)=>{i(`IAM Role: ${t}`);try{let m=new Y,A=new o("Fetching role details...");A.start();let L=await m.getRole({RoleName:t}),j=await m.listAttachedRolePolicies({RoleName:t}),G=await m.listRolePolicies({RoleName:t});if(A.succeed("Role details loaded"),e(`
603
603
  Role Information:`),e(` Name: ${L.RoleName}`),e(` ARN: ${L.Arn}`),e(` Path: ${L.Path}`),e(` Created: ${L.CreateDate?new Date(L.CreateDate).toLocaleString():"N/A"}`),L.Description)e(` Description: ${L.Description}`);if(L.MaxSessionDuration)e(` Max Session: ${L.MaxSessionDuration/3600} hours`);if(L.AssumeRolePolicyDocument){e(`
604
604
  Trust Policy:`);let Z=JSON.parse(decodeURIComponent(L.AssumeRolePolicyDocument));console.log(JSON.stringify(Z,null,2))}if(j.AttachedPolicies&&j.AttachedPolicies.length>0){e(`
605
605
  Attached Managed Policies:`);for(let Z of j.AttachedPolicies)e(` - ${Z.PolicyName}`)}if(G.PolicyNames&&G.PolicyNames.length>0){e(`
606
606
  Inline Policies:`);for(let Z of G.PolicyNames)e(` - ${Z}`)}if(L.Tags&&L.Tags.length>0){e(`
607
- Tags:`);for(let Z of L.Tags)e(` ${Z.Key}: ${Z.Value}`)}}catch(p){r(`Failed to get role: ${p.message}`),process.exit(1)}}),N.command("iam:policies","List IAM policies").option("--scope <scope>","Filter by scope (All, AWS, Local)",{default:"Local"}).option("--prefix <prefix>","Filter by policy name prefix").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(t)=>{i("IAM Policies");try{let p=new Y,A=new o("Fetching policies...");A.start();let j=(await p.listPolicies({Scope:t.scope,PathPrefix:t.path,OnlyAttached:!1})).Policies||[];if(t.prefix)j=j.filter((G)=>G.PolicyName?.startsWith(t.prefix));if(A.succeed(`Found ${j.length} policy(s)`),j.length===0){e("No IAM policies found");return}if(c(["Policy Name","ARN","Attachments","Created"],j.slice(0,50).map((G)=>[G.PolicyName||"N/A",(G.Arn||"N/A").substring(0,50),(G.AttachmentCount||0).toString(),G.CreateDate?new Date(G.CreateDate).toLocaleDateString():"N/A"])),j.length>50)e(`
608
- ... and ${j.length-50} more policies`)}catch(p){r(`Failed to list policies: ${p.message}`),process.exit(1)}}),N.command("iam:policy <policyArn>","Show IAM policy details").option("--version <version>","Policy version ID").action(async(t,p)=>{i("IAM Policy Details");try{let A=new Y,L=new o("Fetching policy...");L.start();let j=await A.getPolicy({PolicyArn:t}),G=p.version||j.DefaultVersionId||"v1",Z=await A.getPolicyVersion({PolicyArn:t,VersionId:G});if(L.succeed("Policy loaded"),e(`
607
+ Tags:`);for(let Z of L.Tags)e(` ${Z.Key}: ${Z.Value}`)}}catch(m){r(`Failed to get role: ${m.message}`),process.exit(1)}}),N.command("iam:policies","List IAM policies").option("--scope <scope>","Filter by scope (All, AWS, Local)",{default:"Local"}).option("--prefix <prefix>","Filter by policy name prefix").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(t)=>{i("IAM Policies");try{let m=new Y,A=new o("Fetching policies...");A.start();let j=(await m.listPolicies({Scope:t.scope,PathPrefix:t.path,OnlyAttached:!1})).Policies||[];if(t.prefix)j=j.filter((G)=>G.PolicyName?.startsWith(t.prefix));if(A.succeed(`Found ${j.length} policy(s)`),j.length===0){e("No IAM policies found");return}if(c(["Policy Name","ARN","Attachments","Created"],j.slice(0,50).map((G)=>[G.PolicyName||"N/A",(G.Arn||"N/A").substring(0,50),(G.AttachmentCount||0).toString(),G.CreateDate?new Date(G.CreateDate).toLocaleDateString():"N/A"])),j.length>50)e(`
608
+ ... and ${j.length-50} more policies`)}catch(m){r(`Failed to list policies: ${m.message}`),process.exit(1)}}),N.command("iam:policy <policyArn>","Show IAM policy details").option("--version <version>","Policy version ID").action(async(t,m)=>{i("IAM Policy Details");try{let A=new Y,L=new o("Fetching policy...");L.start();let j=await A.getPolicy({PolicyArn:t}),G=m.version||j.DefaultVersionId||"v1",Z=await A.getPolicyVersion({PolicyArn:t,VersionId:G});if(L.succeed("Policy loaded"),e(`
609
609
  Policy Information:`),e(` Name: ${j.PolicyName}`),e(` ARN: ${j.Arn}`),e(` Path: ${j.Path}`),e(` Created: ${j.CreateDate?new Date(j.CreateDate).toLocaleString():"N/A"}`),e(` Updated: ${j.UpdateDate?new Date(j.UpdateDate).toLocaleString():"N/A"}`),e(` Attachment Count: ${j.AttachmentCount||0}`),e(` Version: ${G}`),j.Description)e(` Description: ${j.Description}`);if(Z.Document){e(`
610
- Policy Document:`);let ee=JSON.parse(decodeURIComponent(Z.Document));console.log(JSON.stringify(ee,null,2))}}catch(A){r(`Failed to get policy: ${A.message}`),process.exit(1)}}),N.command("iam:users","List IAM users").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(t)=>{i("IAM Users");try{let p=new Y,A=new o("Fetching users...");A.start();let j=(await p.listUsers({PathPrefix:t.path})).Users||[];if(A.succeed(`Found ${j.length} user(s)`),j.length===0){e("No IAM users found");return}c(["User Name","User ID","Path","Created","Password Last Used"],j.map((G)=>[G.UserName||"N/A",G.UserId||"N/A",G.Path||"/",G.CreateDate?new Date(G.CreateDate).toLocaleDateString():"N/A",G.PasswordLastUsed?new Date(G.PasswordLastUsed).toLocaleDateString():"Never"]))}catch(p){r(`Failed to list users: ${p.message}`),process.exit(1)}}),N.command("iam:user <userName>","Show IAM user details").action(async(t)=>{i(`IAM User: ${t}`);try{let p=new Y,A=new o("Fetching user details...");A.start();let L=await p.getUser({UserName:t}),[j,G,Z]=await Promise.all([p.listGroupsForUser({UserName:t}),p.listAttachedUserPolicies({UserName:t}),p.listAccessKeys({UserName:t})]);if(A.succeed("User details loaded"),e(`
610
+ Policy Document:`);let ee=JSON.parse(decodeURIComponent(Z.Document));console.log(JSON.stringify(ee,null,2))}}catch(A){r(`Failed to get policy: ${A.message}`),process.exit(1)}}),N.command("iam:users","List IAM users").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(t)=>{i("IAM Users");try{let m=new Y,A=new o("Fetching users...");A.start();let j=(await m.listUsers({PathPrefix:t.path})).Users||[];if(A.succeed(`Found ${j.length} user(s)`),j.length===0){e("No IAM users found");return}c(["User Name","User ID","Path","Created","Password Last Used"],j.map((G)=>[G.UserName||"N/A",G.UserId||"N/A",G.Path||"/",G.CreateDate?new Date(G.CreateDate).toLocaleDateString():"N/A",G.PasswordLastUsed?new Date(G.PasswordLastUsed).toLocaleDateString():"Never"]))}catch(m){r(`Failed to list users: ${m.message}`),process.exit(1)}}),N.command("iam:user <userName>","Show IAM user details").action(async(t)=>{i(`IAM User: ${t}`);try{let m=new Y,A=new o("Fetching user details...");A.start();let L=await m.getUser({UserName:t}),[j,G,Z]=await Promise.all([m.listGroupsForUser({UserName:t}),m.listAttachedUserPolicies({UserName:t}),m.listAccessKeys({UserName:t})]);if(A.succeed("User details loaded"),e(`
611
611
  User Information:`),e(` Name: ${L.UserName}`),e(` ARN: ${L.Arn}`),e(` User ID: ${L.UserId}`),e(` Path: ${L.Path}`),e(` Created: ${L.CreateDate?new Date(L.CreateDate).toLocaleString():"N/A"}`),L.PasswordLastUsed)e(` Password Last Used: ${new Date(L.PasswordLastUsed).toLocaleString()}`);if(j.Groups&&j.Groups.length>0){e(`
612
612
  Groups:`);for(let ee of j.Groups)e(` - ${ee.GroupName}`)}if(G.AttachedPolicies&&G.AttachedPolicies.length>0){e(`
613
613
  Attached Policies:`);for(let ee of G.AttachedPolicies)e(` - ${ee.PolicyName}`)}if(Z.AccessKeyMetadata&&Z.AccessKeyMetadata.length>0){e(`
614
614
  Access Keys:`);for(let ee of Z.AccessKeyMetadata)if(e(` - ${ee.AccessKeyId}: ${ee.Status}`),ee.CreateDate)e(` Created: ${new Date(ee.CreateDate).toLocaleString()}`)}if(L.Tags&&L.Tags.length>0){e(`
615
- Tags:`);for(let ee of L.Tags)e(` ${ee.Key}: ${ee.Value}`)}}catch(p){r(`Failed to get user: ${p.message}`),process.exit(1)}}),N.command("iam:groups","List IAM groups").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(t)=>{i("IAM Groups");try{let p=new Y,A=new o("Fetching groups...");A.start();let j=(await p.listGroups({PathPrefix:t.path})).Groups||[];if(A.succeed(`Found ${j.length} group(s)`),j.length===0){e("No IAM groups found");return}c(["Group Name","Group ID","Path","Created"],j.map((G)=>[G.GroupName||"N/A",G.GroupId||"N/A",G.Path||"/",G.CreateDate?new Date(G.CreateDate).toLocaleDateString():"N/A"]))}catch(p){r(`Failed to list groups: ${p.message}`),process.exit(1)}}),N.command("iam:simulate <policyArn>","Simulate IAM policy").option("--action <action>","Action to simulate (e.g., s3:GetObject)").option("--resource <arn>","Resource ARN to test against").action(async(t,p)=>{i("Simulate IAM Policy");try{let A=new Y,L=p.action||await I("Action to simulate","s3:GetObject"),j=p.resource||await I("Resource ARN","*");e(`
616
- Policy: ${t}`),e(`Action: ${L}`),e(`Resource: ${j}`);let G=new o("Running simulation...");G.start();let Z=await A.simulatePrincipalPolicy({PolicySourceArn:t,ActionNames:[L],ResourceArns:[j]});if(G.succeed("Simulation complete"),Z.EvaluationResults&&Z.EvaluationResults.length>0)for(let ee of Z.EvaluationResults){let re=ee.EvalDecision;if(e(`
617
- Result for ${ee.EvalActionName}:`),re==="allowed")n(" Decision: ALLOWED");else if(re==="implicitDeny")a(" Decision: IMPLICIT DENY (no matching allow statement)");else r(" Decision: EXPLICIT DENY");if(ee.MatchedStatements&&ee.MatchedStatements.length>0){e(" Matched Statements:");for(let oe of ee.MatchedStatements)e(` - ${oe.SourcePolicyId}: ${oe.SourcePolicyType}`)}}}catch(A){r(`Failed to simulate policy: ${A.message}`),process.exit(1)}}),N.command("iam:whoami","Show current IAM identity").action(async()=>{i("Current IAM Identity");try{let t=new Y,p=new o("Fetching identity...");p.start();let{STSClient:A}=await import("./chunk-gbejgj71.js"),j=await new A("us-east-1").getCallerIdentity();p.succeed("Identity loaded"),e(`
615
+ Tags:`);for(let ee of L.Tags)e(` ${ee.Key}: ${ee.Value}`)}}catch(m){r(`Failed to get user: ${m.message}`),process.exit(1)}}),N.command("iam:groups","List IAM groups").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(t)=>{i("IAM Groups");try{let m=new Y,A=new o("Fetching groups...");A.start();let j=(await m.listGroups({PathPrefix:t.path})).Groups||[];if(A.succeed(`Found ${j.length} group(s)`),j.length===0){e("No IAM groups found");return}c(["Group Name","Group ID","Path","Created"],j.map((G)=>[G.GroupName||"N/A",G.GroupId||"N/A",G.Path||"/",G.CreateDate?new Date(G.CreateDate).toLocaleDateString():"N/A"]))}catch(m){r(`Failed to list groups: ${m.message}`),process.exit(1)}}),N.command("iam:simulate <policyArn>","Simulate IAM policy").option("--action <action>","Action to simulate (e.g., s3:GetObject)").option("--resource <arn>","Resource ARN to test against").action(async(t,m)=>{i("Simulate IAM Policy");try{let A=new Y,L=m.action||await I("Action to simulate","s3:GetObject"),j=m.resource||await I("Resource ARN","*");e(`
616
+ Policy: ${t}`),e(`Action: ${L}`),e(`Resource: ${j}`);let G=new o("Running simulation...");G.start();let Z=await A.simulatePrincipalPolicy({PolicySourceArn:t,ActionNames:[L],ResourceArns:[j]});if(G.succeed("Simulation complete"),Z.EvaluationResults&&Z.EvaluationResults.length>0)for(let ee of Z.EvaluationResults){let ne=ee.EvalDecision;if(e(`
617
+ Result for ${ee.EvalActionName}:`),ne==="allowed")n(" Decision: ALLOWED");else if(ne==="implicitDeny")a(" Decision: IMPLICIT DENY (no matching allow statement)");else r(" Decision: EXPLICIT DENY");if(ee.MatchedStatements&&ee.MatchedStatements.length>0){e(" Matched Statements:");for(let oe of ee.MatchedStatements)e(` - ${oe.SourcePolicyId}: ${oe.SourcePolicyType}`)}}}catch(A){r(`Failed to simulate policy: ${A.message}`),process.exit(1)}}),N.command("iam:whoami","Show current IAM identity").action(async()=>{i("Current IAM Identity");try{let t=new Y,m=new o("Fetching identity...");m.start();let{STSClient:A}=await import("./chunk-qxx1a51w.js"),j=await new A("us-east-1").getCallerIdentity();m.succeed("Identity loaded"),e(`
618
618
  Caller Identity:`),e(` Account: ${j.Account}`),e(` ARN: ${j.Arn}`),e(` User ID: ${j.UserId}`);let G=j.Arn||"";if(G.includes(":user/"))e(" Type: IAM User");else if(G.includes(":assumed-role/"))e(" Type: Assumed Role");else if(G.includes(":root"))a(" Type: Root Account"),a(`
619
- Warning: Using root credentials is not recommended!`)}catch(t){r(`Failed to get identity: ${t.message}`),process.exit(1)}})}async function to(N){let{AWSClient:t}=await import("./chunk-66egshmp.js");class p{client;region;constructor(A){this.region=A,this.client=new t}async jsonRpcRequest(A,L){return this.client.request({service:"cloudtrail",region:this.region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":`com.amazonaws.cloudtrail.v20131101.CloudTrail_20131101.${A}`},body:JSON.stringify(L)})}async describeTrails(){return this.jsonRpcRequest("DescribeTrails",{})}async getTrailStatus(A){return this.jsonRpcRequest("GetTrailStatus",{Name:A})}async lookupEvents(A){return this.jsonRpcRequest("LookupEvents",{...A,StartTime:A.StartTime?.toISOString(),EndTime:A.EndTime?.toISOString()})}async getEventSelectors(A){return this.jsonRpcRequest("GetEventSelectors",{TrailName:A})}async createTrail(A){return this.jsonRpcRequest("CreateTrail",A)}async startLogging(A){return this.jsonRpcRequest("StartLogging",{Name:A})}async stopLogging(A){return this.jsonRpcRequest("StopLogging",{Name:A})}async deleteTrail(A){return this.jsonRpcRequest("DeleteTrail",{Name:A})}}return new p(N)}function qc(N){N.command("audit:trails","List CloudTrail trails").option("--region <region>","AWS region").action(async(t)=>{i("CloudTrail Trails");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=await to(A),j=new o("Fetching trails...");j.start();let Z=(await L.describeTrails()).trailList||[];if(j.succeed(`Found ${Z.length} trail(s)`),Z.length===0){e("No CloudTrail trails found"),e("Use `cloud audit:create` to create a new trail");return}c(["Name","Multi-Region","S3 Bucket","Log Validation","Home Region"],Z.map((ee)=>[ee.Name||"N/A",ee.IsMultiRegionTrail?"Yes":"No",ee.S3BucketName||"N/A",ee.LogFileValidationEnabled?"Yes":"No",ee.HomeRegion||"N/A"]))}catch(p){r(`Failed to list trails: ${p.message}`),process.exit(1)}}),N.command("audit:trail <trailName>","Show CloudTrail trail details").option("--region <region>","AWS region").action(async(t,p)=>{i(`CloudTrail: ${t}`);try{let A=await Me(),L=p.region||A.project.region||"us-east-1",j=await to(L),G=new o("Fetching trail details...");G.start();let[Z,ee]=await Promise.all([j.describeTrails(),j.getTrailStatus(t)]),re=(Z.trailList||[]).find((oe)=>oe.Name===t);if(!re){G.fail("Trail not found");return}if(G.succeed("Trail details loaded"),e(`
620
- Trail Configuration:`),e(` Name: ${re.Name}`),e(` ARN: ${re.TrailARN}`),e(` Home Region: ${re.HomeRegion}`),e(` Multi-Region: ${re.IsMultiRegionTrail?"Yes":"No"}`),e(` Organization Trail: ${re.IsOrganizationTrail?"Yes":"No"}`),e(`
621
- Storage:`),e(` S3 Bucket: ${re.S3BucketName}`),re.S3KeyPrefix)e(` S3 Prefix: ${re.S3KeyPrefix}`);if(e(` Log Validation: ${re.LogFileValidationEnabled?"Enabled":"Disabled"}`),re.CloudWatchLogsLogGroupArn)e(`
622
- CloudWatch Logs:`),e(` Log Group: ${re.CloudWatchLogsLogGroupArn}`);if(re.KMSKeyId)e(`
623
- Encryption:`),e(` KMS Key: ${re.KMSKeyId}`);if(e(`
619
+ Warning: Using root credentials is not recommended!`)}catch(t){r(`Failed to get identity: ${t.message}`),process.exit(1)}})}async function so(N){let{AWSClient:t}=await import("./chunk-nqb1faqj.js");class m{client;region;constructor(A){this.region=A,this.client=new t}async jsonRpcRequest(A,L){return this.client.request({service:"cloudtrail",region:this.region,method:"POST",path:"/",headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":`com.amazonaws.cloudtrail.v20131101.CloudTrail_20131101.${A}`},body:JSON.stringify(L)})}async describeTrails(){return this.jsonRpcRequest("DescribeTrails",{})}async getTrailStatus(A){return this.jsonRpcRequest("GetTrailStatus",{Name:A})}async lookupEvents(A){return this.jsonRpcRequest("LookupEvents",{...A,StartTime:A.StartTime?.toISOString(),EndTime:A.EndTime?.toISOString()})}async getEventSelectors(A){return this.jsonRpcRequest("GetEventSelectors",{TrailName:A})}async createTrail(A){return this.jsonRpcRequest("CreateTrail",A)}async startLogging(A){return this.jsonRpcRequest("StartLogging",{Name:A})}async stopLogging(A){return this.jsonRpcRequest("StopLogging",{Name:A})}async deleteTrail(A){return this.jsonRpcRequest("DeleteTrail",{Name:A})}}return new m(N)}function tl(N){N.command("audit:trails","List CloudTrail trails").option("--region <region>","AWS region").action(async(t)=>{i("CloudTrail Trails");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=await so(A),j=new o("Fetching trails...");j.start();let Z=(await L.describeTrails()).trailList||[];if(j.succeed(`Found ${Z.length} trail(s)`),Z.length===0){e("No CloudTrail trails found"),e("Use `cloud audit:create` to create a new trail");return}c(["Name","Multi-Region","S3 Bucket","Log Validation","Home Region"],Z.map((ee)=>[ee.Name||"N/A",ee.IsMultiRegionTrail?"Yes":"No",ee.S3BucketName||"N/A",ee.LogFileValidationEnabled?"Yes":"No",ee.HomeRegion||"N/A"]))}catch(m){r(`Failed to list trails: ${m.message}`),process.exit(1)}}),N.command("audit:trail <trailName>","Show CloudTrail trail details").option("--region <region>","AWS region").action(async(t,m)=>{i(`CloudTrail: ${t}`);try{let A=await Ne(),L=m.region||A.project.region||"us-east-1",j=await so(L),G=new o("Fetching trail details...");G.start();let[Z,ee]=await Promise.all([j.describeTrails(),j.getTrailStatus(t)]),ne=(Z.trailList||[]).find((oe)=>oe.Name===t);if(!ne){G.fail("Trail not found");return}if(G.succeed("Trail details loaded"),e(`
620
+ Trail Configuration:`),e(` Name: ${ne.Name}`),e(` ARN: ${ne.TrailARN}`),e(` Home Region: ${ne.HomeRegion}`),e(` Multi-Region: ${ne.IsMultiRegionTrail?"Yes":"No"}`),e(` Organization Trail: ${ne.IsOrganizationTrail?"Yes":"No"}`),e(`
621
+ Storage:`),e(` S3 Bucket: ${ne.S3BucketName}`),ne.S3KeyPrefix)e(` S3 Prefix: ${ne.S3KeyPrefix}`);if(e(` Log Validation: ${ne.LogFileValidationEnabled?"Enabled":"Disabled"}`),ne.CloudWatchLogsLogGroupArn)e(`
622
+ CloudWatch Logs:`),e(` Log Group: ${ne.CloudWatchLogsLogGroupArn}`);if(ne.KMSKeyId)e(`
623
+ Encryption:`),e(` KMS Key: ${ne.KMSKeyId}`);if(e(`
624
624
  Status:`),e(` Logging: ${ee.IsLogging?"Active":"Stopped"}`),ee.LatestDeliveryTime)e(` Latest Delivery: ${new Date(ee.LatestDeliveryTime).toLocaleString()}`);if(ee.LatestDeliveryError)a(` Latest Error: ${ee.LatestDeliveryError}`);try{let oe=await j.getEventSelectors(t);if(oe.EventSelectors&&oe.EventSelectors.length>0){e(`
625
- Event Selectors:`);for(let be of oe.EventSelectors)if(e(` - Read/Write: ${be.ReadWriteType}`),e(` Management Events: ${be.IncludeManagementEvents?"Yes":"No"}`),be.DataResources&&be.DataResources.length>0)e(` Data Resources: ${be.DataResources.length} configured`)}}catch{}}catch(A){r(`Failed to get trail: ${A.message}`),process.exit(1)}}),N.command("audit:events","Look up recent CloudTrail events").option("--region <region>","AWS region").option("--user <username>","Filter by IAM user").option("--event <eventName>","Filter by event name").option("--resource <resourceName>","Filter by resource name").option("--hours <number>","Hours to look back",{default:"24"}).option("--limit <number>","Maximum events to return",{default:"50"}).action(async(t)=>{i("CloudTrail Events");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=await to(A),j=new o("Looking up events...");j.start();let G=new Date,ee={StartTime:new Date(G.getTime()-Number.parseInt(t.hours)*60*60*1000),EndTime:G,MaxResults:Number.parseInt(t.limit)};if(t.user)ee.LookupAttributes=[{AttributeKey:"Username",AttributeValue:t.user}];else if(t.event)ee.LookupAttributes=[{AttributeKey:"EventName",AttributeValue:t.event}];else if(t.resource)ee.LookupAttributes=[{AttributeKey:"ResourceName",AttributeValue:t.resource}];let oe=(await L.lookupEvents(ee)).Events||[];if(j.succeed(`Found ${oe.length} event(s)`),oe.length===0){e("No events found matching criteria");return}c(["Time","Event","User","Source IP","Resources"],oe.map((be)=>[be.EventTime?new Date(be.EventTime).toLocaleString():"N/A",be.EventName||"N/A",be.Username||"N/A",be.SourceIPAddress||"N/A",(be.Resources||[]).map((we)=>we.ResourceName).join(", ").substring(0,30)||"-"]))}catch(p){r(`Failed to lookup events: ${p.message}`),process.exit(1)}}),N.command("audit:event <eventId>","Show CloudTrail event details").option("--region <region>","AWS region").action(async(t,p)=>{i(`Event: ${t}`);try{let A=await Me(),L=p.region||A.project.region||"us-east-1",j=await to(L),G=new o("Fetching event...");G.start();let ee=(await j.lookupEvents({LookupAttributes:[{AttributeKey:"EventId",AttributeValue:t}],MaxResults:1})).Events?.[0];if(!ee){G.fail("Event not found");return}if(G.succeed("Event loaded"),e(`
625
+ Event Selectors:`);for(let be of oe.EventSelectors)if(e(` - Read/Write: ${be.ReadWriteType}`),e(` Management Events: ${be.IncludeManagementEvents?"Yes":"No"}`),be.DataResources&&be.DataResources.length>0)e(` Data Resources: ${be.DataResources.length} configured`)}}catch{}}catch(A){r(`Failed to get trail: ${A.message}`),process.exit(1)}}),N.command("audit:events","Look up recent CloudTrail events").option("--region <region>","AWS region").option("--user <username>","Filter by IAM user").option("--event <eventName>","Filter by event name").option("--resource <resourceName>","Filter by resource name").option("--hours <number>","Hours to look back",{default:"24"}).option("--limit <number>","Maximum events to return",{default:"50"}).action(async(t)=>{i("CloudTrail Events");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=await so(A),j=new o("Looking up events...");j.start();let G=new Date,ee={StartTime:new Date(G.getTime()-Number.parseInt(t.hours)*60*60*1000),EndTime:G,MaxResults:Number.parseInt(t.limit)};if(t.user)ee.LookupAttributes=[{AttributeKey:"Username",AttributeValue:t.user}];else if(t.event)ee.LookupAttributes=[{AttributeKey:"EventName",AttributeValue:t.event}];else if(t.resource)ee.LookupAttributes=[{AttributeKey:"ResourceName",AttributeValue:t.resource}];let oe=(await L.lookupEvents(ee)).Events||[];if(j.succeed(`Found ${oe.length} event(s)`),oe.length===0){e("No events found matching criteria");return}c(["Time","Event","User","Source IP","Resources"],oe.map((be)=>[be.EventTime?new Date(be.EventTime).toLocaleString():"N/A",be.EventName||"N/A",be.Username||"N/A",be.SourceIPAddress||"N/A",(be.Resources||[]).map((we)=>we.ResourceName).join(", ").substring(0,30)||"-"]))}catch(m){r(`Failed to lookup events: ${m.message}`),process.exit(1)}}),N.command("audit:event <eventId>","Show CloudTrail event details").option("--region <region>","AWS region").action(async(t,m)=>{i(`Event: ${t}`);try{let A=await Ne(),L=m.region||A.project.region||"us-east-1",j=await so(L),G=new o("Fetching event...");G.start();let ee=(await j.lookupEvents({LookupAttributes:[{AttributeKey:"EventId",AttributeValue:t}],MaxResults:1})).Events?.[0];if(!ee){G.fail("Event not found");return}if(G.succeed("Event loaded"),e(`
626
626
  Event Information:`),e(` Event ID: ${ee.EventId}`),e(` Event Name: ${ee.EventName}`),e(` Event Time: ${ee.EventTime?new Date(ee.EventTime).toLocaleString():"N/A"}`),e(` Event Source: ${ee.EventSource}`),e(` Username: ${ee.Username}`),e(` Source IP: ${ee.SourceIPAddress}`),e(` Access Key: ${ee.AccessKeyId||"N/A"}`),ee.Resources&&ee.Resources.length>0){e(`
627
- Resources:`);for(let re of ee.Resources)e(` - ${re.ResourceType}: ${re.ResourceName}`)}if(ee.CloudTrailEvent){e(`
628
- Full Event Record:`);let re=JSON.parse(ee.CloudTrailEvent);console.log(JSON.stringify(re,null,2))}}catch(A){r(`Failed to get event: ${A.message}`),process.exit(1)}}),N.command("audit:create <trailName>","Create a new CloudTrail trail").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bucket <name>","S3 bucket for logs").option("--prefix <prefix>","S3 key prefix").option("--multi-region","Enable multi-region trail").option("--validation","Enable log file validation").option("--global-events","Include global service events").action(async(t,p)=>{i("Create CloudTrail Trail");try{if(!p.bucket){r("--bucket is required");return}let A=await to(p.region);if(e(`Trail Name: ${t}`),e(`S3 Bucket: ${p.bucket}`),e(`Multi-Region: ${p.multiRegion?"Yes":"No"}`),e(`Log Validation: ${p.validation?"Yes":"No"}`),e(`Global Events: ${p.globalEvents?"Yes":"No"}`),!await d(`
629
- Create this trail?`,!0)){e("Operation cancelled");return}let j=new o("Creating trail...");j.start();let G=await A.createTrail({Name:t,S3BucketName:p.bucket,S3KeyPrefix:p.prefix,IsMultiRegionTrail:p.multiRegion,EnableLogFileValidation:p.validation,IncludeGlobalServiceEvents:p.globalEvents??!0});j.text="Starting logging...",await A.startLogging(t),j.succeed("Trail created and logging started"),n(`
630
- Trail ARN: ${G.TrailARN}`),e(`S3 Bucket: ${G.S3BucketName}`)}catch(A){r(`Failed to create trail: ${A.message}`),process.exit(1)}}),N.command("audit:start <trailName>","Start CloudTrail logging").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Start CloudTrail Logging");try{let A=await to(p.region),L=new o("Starting logging...");L.start(),await A.startLogging(t),L.succeed("Logging started")}catch(A){r(`Failed to start logging: ${A.message}`),process.exit(1)}}),N.command("audit:stop <trailName>","Stop CloudTrail logging").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Stop CloudTrail Logging");try{let A=await to(p.region);if(a(`This will stop logging for trail: ${t}`),!await d(`
631
- Stop logging?`,!1)){e("Operation cancelled");return}let j=new o("Stopping logging...");j.start(),await A.stopLogging(t),j.succeed("Logging stopped")}catch(A){r(`Failed to stop logging: ${A.message}`),process.exit(1)}}),N.command("audit:delete <trailName>","Delete a CloudTrail trail").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(t,p)=>{i("Delete CloudTrail Trail");try{let A=await to(p.region);if(a(`This will permanently delete trail: ${t}`),!await d(`
632
- Delete this trail?`,!1)){e("Operation cancelled");return}let j=new o("Deleting trail...");j.start(),await A.deleteTrail(t),j.succeed("Trail deleted")}catch(A){r(`Failed to delete trail: ${A.message}`),process.exit(1)}})}function Gc(N){N.command("status","Show overall infrastructure health dashboard").option("--region <region>","AWS region").action(async(t)=>{i("Infrastructure Status Dashboard");try{let p=await Me(),A=t.region||p.project.region||"us-east-1";e(`Region: ${A}`),e(`Project: ${p.project.name||"Unknown"}`),e("");let L=[],j=new o("Checking EC2 instances...");j.start();try{let{EC2Client:Se}=await import("./chunk-xtzy90zh.js"),_e=await new Se(A).describeInstances({Filters:[{Name:"instance-state-name",Values:["running","pending","stopping","stopped"]}]}),Ae=0,Te=0,Ne=0;for(let Xe of _e.Reservations||[])for(let Ge of Xe.Instances||[])if(Ne++,Ge.State?.Name==="running")Ae++;else if(Ge.State?.Name==="stopped")Te++;j.succeed("EC2 instances checked"),L.push({name:"EC2 Instances",status:Ae>0?"OK":Ne>0?"WARN":"INFO",details:`${Ae} running, ${Te} stopped, ${Ne} total`})}catch(Se){j.fail("EC2 check failed"),L.push({name:"EC2 Instances",status:"ERROR",details:Se.message})}let G=new o("Checking RDS instances...");G.start();try{let{RDSClient:Se}=await import("./chunk-2tn43y0d.js"),Ae=(await new Se(A).describeDBInstances()).DBInstances||[],Te=Ae.filter((Ne)=>Ne.DBInstanceStatus==="available").length;G.succeed("RDS instances checked"),L.push({name:"RDS Databases",status:Ae.length>0&&Te===Ae.length?"OK":Ae.length>0?"WARN":"INFO",details:`${Te} available, ${Ae.length} total`})}catch(Se){G.fail("RDS check failed"),L.push({name:"RDS Databases",status:"ERROR",details:Se.message})}let Z=new o("Checking Lambda functions...");Z.start();try{let{LambdaClient:Se}=await import("./chunk-f2wn46nw.js"),Ae=(await new Se(A).listFunctions()).Functions||[];Z.succeed("Lambda functions checked"),L.push({name:"Lambda Functions",status:"OK",details:`${Ae.length} function(s)`})}catch(Se){Z.fail("Lambda check failed"),L.push({name:"Lambda Functions",status:"ERROR",details:Se.message})}let ee=new o("Checking S3 buckets...");ee.start();try{let{S3Client:Se}=await import("./chunk-4v6f496n.js"),Ae=(await new Se(A).listBuckets()).Buckets||[];ee.succeed("S3 buckets checked"),L.push({name:"S3 Buckets",status:"OK",details:`${Ae.length} bucket(s)`})}catch(Se){ee.fail("S3 check failed"),L.push({name:"S3 Buckets",status:"ERROR",details:Se.message})}let re=new o("Checking CloudFront distributions...");re.start();try{let{CloudFrontClient:Se}=await import("./chunk-knthwdhz.js"),_e=await new Se().listDistributions(),Ae=_e.filter((Te)=>Te.Status==="Deployed").length;re.succeed("CloudFront checked"),L.push({name:"CloudFront",status:_e.length>0&&Ae===_e.length?"OK":_e.length>0?"WARN":"INFO",details:`${Ae} deployed, ${_e.length} total`})}catch(Se){re.fail("CloudFront check failed"),L.push({name:"CloudFront",status:"ERROR",details:Se.message})}let oe=new o("Checking SQS queues...");oe.start();try{let{SQSClient:Se}=await import("./chunk-rn0pgnt4.js"),Ae=(await new Se(A).listQueues()).QueueUrls||[];oe.succeed("SQS queues checked"),L.push({name:"SQS Queues",status:"OK",details:`${Ae.length} queue(s)`})}catch(Se){oe.fail("SQS check failed"),L.push({name:"SQS Queues",status:"ERROR",details:Se.message})}let be=new o("Checking CloudFormation stacks...");be.start();try{let{CloudFormationClient:Se}=await import("./chunk-dsc7ef1w.js"),Ae=(await new Se(A).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE"])).StackSummaries||[],Te=Ae.filter((Ge)=>Ge.StackStatus==="CREATE_COMPLETE"||Ge.StackStatus==="UPDATE_COMPLETE").length,Ne=Ae.filter((Ge)=>Ge.StackStatus?.includes("IN_PROGRESS")).length,Xe=Ae.filter((Ge)=>Ge.StackStatus?.includes("ROLLBACK")).length;be.succeed("CloudFormation checked"),L.push({name:"CloudFormation",status:Xe>0?"WARN":Ne>0?"INFO":"OK",details:`${Te} healthy, ${Ne} in progress, ${Xe} rolled back`})}catch(Se){be.fail("CloudFormation check failed"),L.push({name:"CloudFormation",status:"ERROR",details:Se.message})}let we=new o("Checking SSL certificates...");we.start();try{let{ACMClient:Se}=await import("./chunk-ffcyj2jj.js"),De=new Se("us-east-1"),Ae=(await De.listCertificates()).CertificateSummaryList||[],Te=await Promise.all(Ae.map((Ye)=>De.describeCertificate({CertificateArn:Ye.CertificateArn}))),Ne=Te.filter((Ye)=>Ye.Status==="ISSUED").length,Xe=Te.filter((Ye)=>Ye.Status==="PENDING_VALIDATION").length,Ge=Te.filter((Ye)=>{if(Ye.NotAfter)return(new Date(Ye.NotAfter).getTime()-Date.now())/86400000<30;return!1}).length;we.succeed("SSL certificates checked"),L.push({name:"SSL Certificates",status:Ge>0?"WARN":Xe>0?"INFO":"OK",details:`${Ne} issued, ${Xe} pending${Ge>0?`, ${Ge} expiring soon`:""}`})}catch(Se){we.fail("ACM check failed"),L.push({name:"SSL Certificates",status:"ERROR",details:Se.message})}e(`
627
+ Resources:`);for(let ne of ee.Resources)e(` - ${ne.ResourceType}: ${ne.ResourceName}`)}if(ee.CloudTrailEvent){e(`
628
+ Full Event Record:`);let ne=JSON.parse(ee.CloudTrailEvent);console.log(JSON.stringify(ne,null,2))}}catch(A){r(`Failed to get event: ${A.message}`),process.exit(1)}}),N.command("audit:create <trailName>","Create a new CloudTrail trail").option("--region <region>","AWS region",{default:"us-east-1"}).option("--bucket <name>","S3 bucket for logs").option("--prefix <prefix>","S3 key prefix").option("--multi-region","Enable multi-region trail").option("--validation","Enable log file validation").option("--global-events","Include global service events").action(async(t,m)=>{i("Create CloudTrail Trail");try{if(!m.bucket){r("--bucket is required");return}let A=await so(m.region);if(e(`Trail Name: ${t}`),e(`S3 Bucket: ${m.bucket}`),e(`Multi-Region: ${m.multiRegion?"Yes":"No"}`),e(`Log Validation: ${m.validation?"Yes":"No"}`),e(`Global Events: ${m.globalEvents?"Yes":"No"}`),!await d(`
629
+ Create this trail?`,!0)){e("Operation cancelled");return}let j=new o("Creating trail...");j.start();let G=await A.createTrail({Name:t,S3BucketName:m.bucket,S3KeyPrefix:m.prefix,IsMultiRegionTrail:m.multiRegion,EnableLogFileValidation:m.validation,IncludeGlobalServiceEvents:m.globalEvents??!0});j.text="Starting logging...",await A.startLogging(t),j.succeed("Trail created and logging started"),n(`
630
+ Trail ARN: ${G.TrailARN}`),e(`S3 Bucket: ${G.S3BucketName}`)}catch(A){r(`Failed to create trail: ${A.message}`),process.exit(1)}}),N.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 so(m.region),L=new o("Starting logging...");L.start(),await A.startLogging(t),L.succeed("Logging started")}catch(A){r(`Failed to start logging: ${A.message}`),process.exit(1)}}),N.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 so(m.region);if(a(`This will stop logging for trail: ${t}`),!await d(`
631
+ Stop logging?`,!1)){e("Operation cancelled");return}let j=new o("Stopping logging...");j.start(),await A.stopLogging(t),j.succeed("Logging stopped")}catch(A){r(`Failed to stop logging: ${A.message}`),process.exit(1)}}),N.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 so(m.region);if(a(`This will permanently delete trail: ${t}`),!await d(`
632
+ Delete this trail?`,!1)){e("Operation cancelled");return}let j=new o("Deleting trail...");j.start(),await A.deleteTrail(t),j.succeed("Trail deleted")}catch(A){r(`Failed to delete trail: ${A.message}`),process.exit(1)}})}function nl(N){N.command("status","Show overall infrastructure health dashboard").option("--region <region>","AWS region").action(async(t)=>{i("Infrastructure Status Dashboard");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1";e(`Region: ${A}`),e(`Project: ${m.project.name||"Unknown"}`),e("");let L=[],j=new o("Checking EC2 instances...");j.start();try{let{EC2Client:Se}=await import("./chunk-e0c3agf2.js"),Me=await new Se(A).describeInstances({Filters:[{Name:"instance-state-name",Values:["running","pending","stopping","stopped"]}]}),Ae=0,Ee=0,Te=0;for(let Qe of Me.Reservations||[])for(let Ke of Qe.Instances||[])if(Te++,Ke.State?.Name==="running")Ae++;else if(Ke.State?.Name==="stopped")Ee++;j.succeed("EC2 instances checked"),L.push({name:"EC2 Instances",status:Ae>0?"OK":Te>0?"WARN":"INFO",details:`${Ae} running, ${Ee} stopped, ${Te} total`})}catch(Se){j.fail("EC2 check failed"),L.push({name:"EC2 Instances",status:"ERROR",details:Se.message})}let G=new o("Checking RDS instances...");G.start();try{let{RDSClient:Se}=await import("./chunk-4cs098d1.js"),Ae=(await new Se(A).describeDBInstances()).DBInstances||[],Ee=Ae.filter((Te)=>Te.DBInstanceStatus==="available").length;G.succeed("RDS instances checked"),L.push({name:"RDS Databases",status:Ae.length>0&&Ee===Ae.length?"OK":Ae.length>0?"WARN":"INFO",details:`${Ee} available, ${Ae.length} total`})}catch(Se){G.fail("RDS check failed"),L.push({name:"RDS Databases",status:"ERROR",details:Se.message})}let Z=new o("Checking Lambda functions...");Z.start();try{let{LambdaClient:Se}=await import("./chunk-wagzxm7z.js"),Ae=(await new Se(A).listFunctions()).Functions||[];Z.succeed("Lambda functions checked"),L.push({name:"Lambda Functions",status:"OK",details:`${Ae.length} function(s)`})}catch(Se){Z.fail("Lambda check failed"),L.push({name:"Lambda Functions",status:"ERROR",details:Se.message})}let ee=new o("Checking S3 buckets...");ee.start();try{let{S3Client:Se}=await import("./chunk-a7kmjq4y.js"),Ae=(await new Se(A).listBuckets()).Buckets||[];ee.succeed("S3 buckets checked"),L.push({name:"S3 Buckets",status:"OK",details:`${Ae.length} bucket(s)`})}catch(Se){ee.fail("S3 check failed"),L.push({name:"S3 Buckets",status:"ERROR",details:Se.message})}let ne=new o("Checking CloudFront distributions...");ne.start();try{let{CloudFrontClient:Se}=await import("./chunk-72g1hr4z.js"),Me=await new Se().listDistributions(),Ae=Me.filter((Ee)=>Ee.Status==="Deployed").length;ne.succeed("CloudFront checked"),L.push({name:"CloudFront",status:Me.length>0&&Ae===Me.length?"OK":Me.length>0?"WARN":"INFO",details:`${Ae} deployed, ${Me.length} total`})}catch(Se){ne.fail("CloudFront check failed"),L.push({name:"CloudFront",status:"ERROR",details:Se.message})}let oe=new o("Checking SQS queues...");oe.start();try{let{SQSClient:Se}=await import("./chunk-hr3vbg6y.js"),Ae=(await new Se(A).listQueues()).QueueUrls||[];oe.succeed("SQS queues checked"),L.push({name:"SQS Queues",status:"OK",details:`${Ae.length} queue(s)`})}catch(Se){oe.fail("SQS check failed"),L.push({name:"SQS Queues",status:"ERROR",details:Se.message})}let be=new o("Checking CloudFormation stacks...");be.start();try{let{CloudFormationClient:Se}=await import("./chunk-p831sptd.js"),Ae=(await new Se(A).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE"])).StackSummaries||[],Ee=Ae.filter((Ke)=>Ke.StackStatus==="CREATE_COMPLETE"||Ke.StackStatus==="UPDATE_COMPLETE").length,Te=Ae.filter((Ke)=>Ke.StackStatus?.includes("IN_PROGRESS")).length,Qe=Ae.filter((Ke)=>Ke.StackStatus?.includes("ROLLBACK")).length;be.succeed("CloudFormation checked"),L.push({name:"CloudFormation",status:Qe>0?"WARN":Te>0?"INFO":"OK",details:`${Ee} healthy, ${Te} in progress, ${Qe} rolled back`})}catch(Se){be.fail("CloudFormation check failed"),L.push({name:"CloudFormation",status:"ERROR",details:Se.message})}let we=new o("Checking SSL certificates...");we.start();try{let{ACMClient:Se}=await import("./chunk-0h6cggyt.js"),xe=new Se("us-east-1"),Ae=(await xe.listCertificates()).CertificateSummaryList||[],Ee=await Promise.all(Ae.map((Ye)=>xe.describeCertificate({CertificateArn:Ye.CertificateArn}))),Te=Ee.filter((Ye)=>Ye.Status==="ISSUED").length,Qe=Ee.filter((Ye)=>Ye.Status==="PENDING_VALIDATION").length,Ke=Ee.filter((Ye)=>{if(Ye.NotAfter)return(new Date(Ye.NotAfter).getTime()-Date.now())/86400000<30;return!1}).length;we.succeed("SSL certificates checked"),L.push({name:"SSL Certificates",status:Ke>0?"WARN":Qe>0?"INFO":"OK",details:`${Te} issued, ${Qe} pending${Ke>0?`, ${Ke} expiring soon`:""}`})}catch(Se){we.fail("ACM check failed"),L.push({name:"SSL Certificates",status:"ERROR",details:Se.message})}e(`
633
633
  `+"=".repeat(60)),e("HEALTH SUMMARY"),e("=".repeat(60)+`
634
- `);for(let Se of L){let De="";if(Se.status==="OK")De=`${T.green}[OK]${T.reset}`;else if(Se.status==="WARN")De=`${T.yellow}[WARN]${T.reset}`;else if(Se.status==="ERROR")De=`${T.red}[ERROR]${T.reset}`;else De=`${T.blue}[INFO]${T.reset}`;console.log(`${De} ${Se.name.padEnd(20)} ${Se.details}`)}let Ce=L.some((Se)=>Se.status==="ERROR"),Ee=L.some((Se)=>Se.status==="WARN");if(e(""),Ce)r("Some services have errors. Check the details above.");else if(Ee)a("Some services need attention. Check the warnings above.");else n("All services are healthy!")}catch(p){r(`Failed to get status: ${p.message}`),process.exit(1)}}),N.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 p=new Date,A=new Date(p.getFullYear(),p.getMonth(),1),L=new Date(p.getFullYear(),p.getMonth()+1,0);e(`Period: ${A.toISOString().split("T")[0]} to ${L.toISOString().split("T")[0]}`),e(""),e("Note: Cost data requires AWS Cost Explorer API access."),e("Run `cloud cost` for detailed cost analysis.")}catch(p){r(`Failed to get cost summary: ${p.message}`),process.exit(1)}}),N.command("status:alarms","Show CloudWatch alarm status").option("--region <region>","AWS region").action(async(t)=>{i("CloudWatch Alarms");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",{AWSClient:L}=await import("./chunk-66egshmp.js");class j{client;region;constructor(Ce){this.region=Ce,this.client=new L}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 G=new j(A),Z=new o("Fetching alarms...");Z.start();let re=(await G.describeAlarms()).MetricAlarms||[];if(Z.succeed(`Found ${re.length} alarm(s)`),re.length===0){e("No CloudWatch alarms configured");return}let oe=re.filter((Ce)=>Ce.StateValue==="ALARM"),be=re.filter((Ce)=>Ce.StateValue==="OK"),we=re.filter((Ce)=>Ce.StateValue==="INSUFFICIENT_DATA");if(e(""),e(`Alarming: ${oe.length}`),e(`OK: ${be.length}`),e(`Insufficient Data: ${we.length}`),oe.length>0){e(`
635
- Alarms in ALARM state:`);for(let Ce of oe)r(` - ${Ce.AlarmName}: ${Ce.AlarmDescription||"No description"}`)}}catch(p){r(`Failed to get alarms: ${p.message}`),process.exit(1)}})}async function ci(N){let{AWSClient:t}=await import("./chunk-66egshmp.js");class p{client;region;constructor(A){this.region=A,this.client=new t}async jsonRpcRequest(A,L){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(L)})}async listBackupVaults(){return this.jsonRpcRequest("ListBackupVaults",{})}async listBackupPlans(){return this.jsonRpcRequest("ListBackupPlans",{})}async listRecoveryPointsByBackupVault(A,L){return this.jsonRpcRequest("ListRecoveryPointsByBackupVault",{BackupVaultName:A,...L})}async describeBackupVault(A){return this.jsonRpcRequest("DescribeBackupVault",{BackupVaultName:A})}async getBackupPlan(A){return this.jsonRpcRequest("GetBackupPlan",{BackupPlanId:A})}async createBackupVault(A,L){return this.jsonRpcRequest("CreateBackupVault",{BackupVaultName:A,...L})}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,L){return this.jsonRpcRequest("CreateBackupSelection",{BackupPlanId:A,BackupSelection:L})}async listBackupSelections(A){return this.jsonRpcRequest("ListBackupSelections",{BackupPlanId:A})}}return new p(N)}function Hc(N){N.command("backup:vaults","List backup vaults").option("--region <region>","AWS region").action(async(t)=>{i("Backup Vaults");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=await ci(A),j=new o("Fetching vaults...");j.start();let Z=(await L.listBackupVaults()).BackupVaultList||[];if(j.succeed(`Found ${Z.length} vault(s)`),Z.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"],Z.map((ee)=>[ee.BackupVaultName||"N/A",(ee.NumberOfRecoveryPoints||0).toString(),ee.CreationDate?new Date(ee.CreationDate).toLocaleDateString():"N/A",ee.EncryptionKeyArn?"Yes":"Default"]))}catch(p){r(`Failed to list vaults: ${p.message}`),process.exit(1)}}),N.command("backup:list","List backup plans").option("--region <region>","AWS region").action(async(t)=>{i("Backup Plans");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=await ci(A),j=new o("Fetching backup plans...");j.start();let Z=(await L.listBackupPlans()).BackupPlansList||[];if(j.succeed(`Found ${Z.length} backup plan(s)`),Z.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"],Z.map((ee)=>[ee.BackupPlanName||"N/A",ee.BackupPlanId||"N/A",ee.VersionId?.substring(0,8)||"N/A",ee.CreationDate?new Date(ee.CreationDate).toLocaleDateString():"N/A"]))}catch(p){r(`Failed to list backup plans: ${p.message}`),process.exit(1)}}),N.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,p)=>{i(`Recovery Points: ${t}`);try{let A=await Me(),L=p.region||A.project.region||"us-east-1",j=await ci(L),G=new o("Fetching recovery points...");G.start();let ee=(await j.listRecoveryPointsByBackupVault(t,{MaxResults:Number.parseInt(p.limit)})).RecoveryPoints||[];if(G.succeed(`Found ${ee.length} recovery point(s)`),ee.length===0){e("No recovery points found");return}c(["Resource","Status","Created","Size","Lifecycle"],ee.map((re)=>[re.ResourceArn?.split(":").pop()||"N/A",re.Status||"N/A",re.CreationDate?new Date(re.CreationDate).toLocaleString():"N/A",re.BackupSizeInBytes?Ed(re.BackupSizeInBytes):"N/A",re.Lifecycle?.DeleteAfterDays?`${re.Lifecycle.DeleteAfterDays} days`:"Indefinite"]))}catch(A){r(`Failed to list recovery points: ${A.message}`),process.exit(1)}}),N.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,p)=>{i("Create Backup Vault");try{let A=await ci(p.region);if(e(`Vault Name: ${t}`),e(`Region: ${p.region}`),e(`Encryption: ${p.kmsKey?"Custom KMS":"AWS Managed"}`),!await d(`
636
- Create this vault?`,!0)){e("Operation cancelled");return}let j=new o("Creating vault...");j.start(),await A.createBackupVault(t,{EncryptionKeyArn:p.kmsKey}),j.succeed("Vault created"),n(`
637
- Vault: ${t}`)}catch(A){r(`Failed to create vault: ${A.message}`),process.exit(1)}}),N.command("backup:create <planName>","Create a backup plan").option("--region <region>","AWS region",{default:"us-east-1"}).option("--vault <name>","Target backup vault",{default:"Default"}).option("--schedule <cron>","Backup schedule (cron expression)",{default:"cron(0 5 ? * * *)"}).option("--retention <days>","Retention period in days",{default:"30"}).option("--lifecycle-cold <days>","Move to cold storage after days").action(async(t,p)=>{i("Create Backup Plan");try{let A=await ci(p.region);if(e(`Plan Name: ${t}`),e(`Vault: ${p.vault}`),e(`Schedule: ${p.schedule}`),e(`Retention: ${p.retention} days`),!await d(`
638
- Create this backup plan?`,!0)){e("Operation cancelled");return}let j=new o("Creating backup plan...");j.start();let G={DeleteAfterDays:Number.parseInt(p.retention)};if(p.lifecycleCold)G.MoveToColdStorageAfterDays=Number.parseInt(p.lifecycleCold);let Z=await A.createBackupPlan({BackupPlanName:t,Rules:[{RuleName:`${t}-daily`,TargetBackupVaultName:p.vault,ScheduleExpression:p.schedule,StartWindowMinutes:60,CompletionWindowMinutes:180,Lifecycle:G}]});j.succeed("Backup plan created"),n(`
639
- Plan ID: ${Z.BackupPlanId}`),e("\nNote: Add resource selections with `cloud backup:add-selection`")}catch(A){r(`Failed to create backup plan: ${A.message}`),process.exit(1)}}),N.command("backup:add-selection <planId>","Add resources to a backup plan").option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","Selection name").option("--role <arn>","IAM role ARN for backup").option("--resource <arn>","Resource ARN to backup").option("--tag-key <key>","Tag key for resource selection").option("--tag-value <value>","Tag value for resource selection").action(async(t,p)=>{i("Add Backup Selection");try{if(!p.role){r("--role is required (IAM role ARN for AWS Backup)");return}if(!p.resource&&!p.tagKey){r("Specify --resource or --tag-key/--tag-value");return}let A=await ci(p.region),j={SelectionName:p.name||`selection-${Date.now()}`,IamRoleArn:p.role};if(p.resource)j.Resources=[p.resource],e(`Resource: ${p.resource}`);if(p.tagKey&&p.tagValue)j.ListOfTags=[{ConditionType:"STRINGEQUALS",ConditionKey:p.tagKey,ConditionValue:p.tagValue}],e(`Tag: ${p.tagKey}=${p.tagValue}`);if(!await d(`
634
+ `);for(let Se of L){let xe="";if(Se.status==="OK")xe=`${T.green}[OK]${T.reset}`;else if(Se.status==="WARN")xe=`${T.yellow}[WARN]${T.reset}`;else if(Se.status==="ERROR")xe=`${T.red}[ERROR]${T.reset}`;else xe=`${T.blue}[INFO]${T.reset}`;console.log(`${xe} ${Se.name.padEnd(20)} ${Se.details}`)}let Ce=L.some((Se)=>Se.status==="ERROR"),De=L.some((Se)=>Se.status==="WARN");if(e(""),Ce)r("Some services have errors. Check the details above.");else if(De)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)}}),N.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),L=new Date(m.getFullYear(),m.getMonth()+1,0);e(`Period: ${A.toISOString().split("T")[0]} to ${L.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)}}),N.command("status:alarms","Show CloudWatch alarm status").option("--region <region>","AWS region").action(async(t)=>{i("CloudWatch Alarms");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",{AWSClient:L}=await import("./chunk-nqb1faqj.js");class j{client;region;constructor(Ce){this.region=Ce,this.client=new L}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 G=new j(A),Z=new o("Fetching alarms...");Z.start();let ne=(await G.describeAlarms()).MetricAlarms||[];if(Z.succeed(`Found ${ne.length} alarm(s)`),ne.length===0){e("No CloudWatch alarms configured");return}let oe=ne.filter((Ce)=>Ce.StateValue==="ALARM"),be=ne.filter((Ce)=>Ce.StateValue==="OK"),we=ne.filter((Ce)=>Ce.StateValue==="INSUFFICIENT_DATA");if(e(""),e(`Alarming: ${oe.length}`),e(`OK: ${be.length}`),e(`Insufficient Data: ${we.length}`),oe.length>0){e(`
635
+ Alarms in ALARM state:`);for(let Ce of oe)r(` - ${Ce.AlarmName}: ${Ce.AlarmDescription||"No description"}`)}}catch(m){r(`Failed to get alarms: ${m.message}`),process.exit(1)}})}async function pi(N){let{AWSClient:t}=await import("./chunk-nqb1faqj.js");class m{client;region;constructor(A){this.region=A,this.client=new t}async jsonRpcRequest(A,L){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(L)})}async listBackupVaults(){return this.jsonRpcRequest("ListBackupVaults",{})}async listBackupPlans(){return this.jsonRpcRequest("ListBackupPlans",{})}async listRecoveryPointsByBackupVault(A,L){return this.jsonRpcRequest("ListRecoveryPointsByBackupVault",{BackupVaultName:A,...L})}async describeBackupVault(A){return this.jsonRpcRequest("DescribeBackupVault",{BackupVaultName:A})}async getBackupPlan(A){return this.jsonRpcRequest("GetBackupPlan",{BackupPlanId:A})}async createBackupVault(A,L){return this.jsonRpcRequest("CreateBackupVault",{BackupVaultName:A,...L})}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,L){return this.jsonRpcRequest("CreateBackupSelection",{BackupPlanId:A,BackupSelection:L})}async listBackupSelections(A){return this.jsonRpcRequest("ListBackupSelections",{BackupPlanId:A})}}return new m(N)}function rl(N){N.command("backup:vaults","List backup vaults").option("--region <region>","AWS region").action(async(t)=>{i("Backup Vaults");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=await pi(A),j=new o("Fetching vaults...");j.start();let Z=(await L.listBackupVaults()).BackupVaultList||[];if(j.succeed(`Found ${Z.length} vault(s)`),Z.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"],Z.map((ee)=>[ee.BackupVaultName||"N/A",(ee.NumberOfRecoveryPoints||0).toString(),ee.CreationDate?new Date(ee.CreationDate).toLocaleDateString():"N/A",ee.EncryptionKeyArn?"Yes":"Default"]))}catch(m){r(`Failed to list vaults: ${m.message}`),process.exit(1)}}),N.command("backup:list","List backup plans").option("--region <region>","AWS region").action(async(t)=>{i("Backup Plans");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=await pi(A),j=new o("Fetching backup plans...");j.start();let Z=(await L.listBackupPlans()).BackupPlansList||[];if(j.succeed(`Found ${Z.length} backup plan(s)`),Z.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"],Z.map((ee)=>[ee.BackupPlanName||"N/A",ee.BackupPlanId||"N/A",ee.VersionId?.substring(0,8)||"N/A",ee.CreationDate?new Date(ee.CreationDate).toLocaleDateString():"N/A"]))}catch(m){r(`Failed to list backup plans: ${m.message}`),process.exit(1)}}),N.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 Ne(),L=m.region||A.project.region||"us-east-1",j=await pi(L),G=new o("Fetching recovery points...");G.start();let ee=(await j.listRecoveryPointsByBackupVault(t,{MaxResults:Number.parseInt(m.limit)})).RecoveryPoints||[];if(G.succeed(`Found ${ee.length} recovery point(s)`),ee.length===0){e("No recovery points found");return}c(["Resource","Status","Created","Size","Lifecycle"],ee.map((ne)=>[ne.ResourceArn?.split(":").pop()||"N/A",ne.Status||"N/A",ne.CreationDate?new Date(ne.CreationDate).toLocaleString():"N/A",ne.BackupSizeInBytes?zd(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)}}),N.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 pi(m.region);if(e(`Vault Name: ${t}`),e(`Region: ${m.region}`),e(`Encryption: ${m.kmsKey?"Custom KMS":"AWS Managed"}`),!await d(`
636
+ Create this vault?`,!0)){e("Operation cancelled");return}let j=new o("Creating vault...");j.start(),await A.createBackupVault(t,{EncryptionKeyArn:m.kmsKey}),j.succeed("Vault created"),n(`
637
+ Vault: ${t}`)}catch(A){r(`Failed to create vault: ${A.message}`),process.exit(1)}}),N.command("backup:create <planName>","Create a backup plan").option("--region <region>","AWS region",{default:"us-east-1"}).option("--vault <name>","Target backup vault",{default:"Default"}).option("--schedule <cron>","Backup schedule (cron expression)",{default:"cron(0 5 ? * * *)"}).option("--retention <days>","Retention period in days",{default:"30"}).option("--lifecycle-cold <days>","Move to cold storage after days").action(async(t,m)=>{i("Create Backup Plan");try{let A=await pi(m.region);if(e(`Plan Name: ${t}`),e(`Vault: ${m.vault}`),e(`Schedule: ${m.schedule}`),e(`Retention: ${m.retention} days`),!await d(`
638
+ Create this backup plan?`,!0)){e("Operation cancelled");return}let j=new o("Creating backup plan...");j.start();let G={DeleteAfterDays:Number.parseInt(m.retention)};if(m.lifecycleCold)G.MoveToColdStorageAfterDays=Number.parseInt(m.lifecycleCold);let Z=await A.createBackupPlan({BackupPlanName:t,Rules:[{RuleName:`${t}-daily`,TargetBackupVaultName:m.vault,ScheduleExpression:m.schedule,StartWindowMinutes:60,CompletionWindowMinutes:180,Lifecycle:G}]});j.succeed("Backup plan created"),n(`
639
+ Plan ID: ${Z.BackupPlanId}`),e("\nNote: Add resource selections with `cloud backup:add-selection`")}catch(A){r(`Failed to create backup plan: ${A.message}`),process.exit(1)}}),N.command("backup:add-selection <planId>","Add resources to a backup plan").option("--region <region>","AWS region",{default:"us-east-1"}).option("--name <name>","Selection name").option("--role <arn>","IAM role ARN for backup").option("--resource <arn>","Resource ARN to backup").option("--tag-key <key>","Tag key for resource selection").option("--tag-value <value>","Tag value for resource selection").action(async(t,m)=>{i("Add Backup Selection");try{if(!m.role){r("--role is required (IAM role ARN for AWS Backup)");return}if(!m.resource&&!m.tagKey){r("Specify --resource or --tag-key/--tag-value");return}let A=await pi(m.region),j={SelectionName:m.name||`selection-${Date.now()}`,IamRoleArn:m.role};if(m.resource)j.Resources=[m.resource],e(`Resource: ${m.resource}`);if(m.tagKey&&m.tagValue)j.ListOfTags=[{ConditionType:"STRINGEQUALS",ConditionKey:m.tagKey,ConditionValue:m.tagValue}],e(`Tag: ${m.tagKey}=${m.tagValue}`);if(!await d(`
640
640
  Add this selection?`,!0)){e("Operation cancelled");return}let Z=new o("Adding selection...");Z.start();let ee=await A.createBackupSelection(t,j);Z.succeed("Selection added"),n(`
641
- Selection ID: ${ee.SelectionId}`)}catch(A){r(`Failed to add selection: ${A.message}`),process.exit(1)}}),N.command("backup:start <resourceArn>","Start an on-demand backup").option("--region <region>","AWS region",{default:"us-east-1"}).option("--vault <name>","Backup vault name",{default:"Default"}).option("--role <arn>","IAM role ARN for backup").action(async(t,p)=>{i("Start Backup Job");try{if(!p.role){r("--role is required (IAM role ARN for AWS Backup)");return}let A=await ci(p.region);if(e(`Resource: ${t}`),e(`Vault: ${p.vault}`),!await d(`
642
- Start backup?`,!0)){e("Operation cancelled");return}let j=new o("Starting backup job...");j.start();let G=await A.startBackupJob({BackupVaultName:p.vault,ResourceArn:t,IamRoleArn:p.role,IdempotencyToken:`cli-${Date.now()}`});j.succeed("Backup job started"),n(`
643
- Job ID: ${G.BackupJobId}`),e("Use `cloud backup:jobs` to check status")}catch(A){r(`Failed to start backup: ${A.message}`),process.exit(1)}}),N.command("backup:restore <recoveryPointArn>","Start a restore job").option("--region <region>","AWS region",{default:"us-east-1"}).option("--role <arn>","IAM role ARN for restore").option("--metadata <json>","Restore metadata JSON").action(async(t,p)=>{i("Start Restore Job");try{if(!p.role){r("--role is required (IAM role ARN for AWS Backup)");return}let A=await ci(p.region);e(`Recovery Point: ${t}`);let L={};if(p.metadata)L=JSON.parse(p.metadata);if(a(`
641
+ Selection ID: ${ee.SelectionId}`)}catch(A){r(`Failed to add selection: ${A.message}`),process.exit(1)}}),N.command("backup:start <resourceArn>","Start an on-demand backup").option("--region <region>","AWS region",{default:"us-east-1"}).option("--vault <name>","Backup vault name",{default:"Default"}).option("--role <arn>","IAM role ARN for backup").action(async(t,m)=>{i("Start Backup Job");try{if(!m.role){r("--role is required (IAM role ARN for AWS Backup)");return}let A=await pi(m.region);if(e(`Resource: ${t}`),e(`Vault: ${m.vault}`),!await d(`
642
+ Start backup?`,!0)){e("Operation cancelled");return}let j=new o("Starting backup job...");j.start();let G=await A.startBackupJob({BackupVaultName:m.vault,ResourceArn:t,IamRoleArn:m.role,IdempotencyToken:`cli-${Date.now()}`});j.succeed("Backup job started"),n(`
643
+ Job ID: ${G.BackupJobId}`),e("Use `cloud backup:jobs` to check status")}catch(A){r(`Failed to start backup: ${A.message}`),process.exit(1)}}),N.command("backup:restore <recoveryPointArn>","Start a restore job").option("--region <region>","AWS region",{default:"us-east-1"}).option("--role <arn>","IAM role ARN for restore").option("--metadata <json>","Restore metadata JSON").action(async(t,m)=>{i("Start Restore Job");try{if(!m.role){r("--role is required (IAM role ARN for AWS Backup)");return}let A=await pi(m.region);e(`Recovery Point: ${t}`);let L={};if(m.metadata)L=JSON.parse(m.metadata);if(a(`
644
644
  Restore will create new resources. Review carefully.`),!await d(`
645
- Start restore?`,!1)){e("Operation cancelled");return}let G=new o("Starting restore job...");G.start();let Z=await A.startRestoreJob({RecoveryPointArn:t,IamRoleArn:p.role,Metadata:L,IdempotencyToken:`cli-${Date.now()}`});G.succeed("Restore job started"),n(`
646
- Job ID: ${Z.RestoreJobId}`),e("Use `cloud backup:restore-jobs` to check status")}catch(A){r(`Failed to start restore: ${A.message}`),process.exit(1)}}),N.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 p=await Me(),A=t.region||p.project.region||"us-east-1",L=await ci(A),j=new o("Fetching backup jobs...");j.start();let Z=(await L.listBackupJobs({ByState:t.state,MaxResults:50})).BackupJobs||[];if(j.succeed(`Found ${Z.length} job(s)`),Z.length===0){e("No backup jobs found");return}c(["Job ID","Resource","State","Started","Size"],Z.map((ee)=>[ee.BackupJobId?.substring(0,16)||"N/A",ee.ResourceArn?.split(":").pop()||"N/A",ee.State||"N/A",ee.CreationDate?new Date(ee.CreationDate).toLocaleString():"N/A",ee.BackupSizeInBytes?Ed(ee.BackupSizeInBytes):"N/A"]))}catch(p){r(`Failed to list backup jobs: ${p.message}`),process.exit(1)}}),N.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 p=await Me(),A=t.region||p.project.region||"us-east-1",L=await ci(A),j=new o("Fetching restore jobs...");j.start();let Z=(await L.listRestoreJobs({ByStatus:t.status,MaxResults:50})).RestoreJobs||[];if(j.succeed(`Found ${Z.length} job(s)`),Z.length===0){e("No restore jobs found");return}c(["Job ID","Resource","Status","Started","Completed"],Z.map((ee)=>[ee.RestoreJobId?.substring(0,16)||"N/A",ee.CreatedResourceArn?.split(":").pop()||"Pending",ee.Status||"N/A",ee.CreationDate?new Date(ee.CreationDate).toLocaleString():"N/A",ee.CompletionDate?new Date(ee.CompletionDate).toLocaleString():"-"]))}catch(p){r(`Failed to list restore jobs: ${p.message}`),process.exit(1)}}),N.command("backup:schedule","Show backup schedule overview").option("--region <region>","AWS region").action(async(t)=>{i("Backup Schedule Overview");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",L=await ci(A),j=new o("Fetching backup plans...");j.start();let Z=(await L.listBackupPlans()).BackupPlansList||[];if(j.succeed(`Found ${Z.length} backup plan(s)`),Z.length===0){e("No backup plans configured");return}for(let ee of Z){e(`
647
- ${ee.BackupPlanName}`),e("=".repeat(40));try{let oe=(await L.getBackupPlan(ee.BackupPlanId)).BackupPlan?.Rules||[];for(let we of oe)if(e(` Rule: ${we.RuleName}`),e(` Schedule: ${we.ScheduleExpression}`),e(` Vault: ${we.TargetBackupVaultName}`),we.Lifecycle?.DeleteAfterDays)e(` Retention: ${we.Lifecycle.DeleteAfterDays} days`);let be=await L.listBackupSelections(ee.BackupPlanId);if(be.BackupSelectionsList&&be.BackupSelectionsList.length>0)e(` Selections: ${be.BackupSelectionsList.length}`)}catch{e(" (Unable to load plan details)")}}}catch(p){r(`Failed to get schedule: ${p.message}`),process.exit(1)}})}function Ed(N){if(N===0)return"0 B";let t=1024,p=["B","KB","MB","GB","TB"],A=Math.floor(Math.log(N)/Math.log(t));return`${Number.parseFloat((N/t**A).toFixed(2))} ${p[A]}`}async function Vf(N){let t=await Me(),p=y(process.cwd(),t),A=N??Object.keys(t.environments??{})[0]??"production",L=p.environments.get(A);if(!L)throw p.store.close(),Error(`Environment ${A} was not found.`);let j=new b(p.store,{workerId:`cli:${process.pid}`}),G=new Mn(p.store),Z=new Or(G,j),ee=new Wt(p.store),re=new Gt(p.store,k(process.cwd())),oe=p.store.getActorByExternalId("system","cli")??p.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:t,controlPlane:p,env:A,environmentRecord:L,queue:j,store:G,coordinator:Z,dataServices:ee,secrets:re,actor:oe}}async function Si(N,t){let p=await Vf(N);try{return await t(p)}finally{p.controlPlane.store.close()}}async function Di(N){try{await N()}catch(t){r(t instanceof Error?t.message:String(t))}}function zi(N,t,p,A){N.controlPlane.store.appendEvent({organizationId:N.controlPlane.organization.id,projectId:N.controlPlane.project.id,resourceId:A,actorId:N.actor.id,type:`backup.${t}`,payload:p})}function Wf(N,t){let p=N.store.getPolicy(t)??N.store.listPolicies(N.controlPlane.project.id,N.environmentRecord.id).find((A)=>A.name===t);if(!p||p.environmentId!==N.environmentRecord.id)throw Error(`Backup policy ${t} was not found in ${N.env}.`);return p}function Qs(N,t){let p=N.store.getRecoveryPoint(t),A=N.store.listRecoveryPoints(N.controlPlane.project.id).filter((j)=>j.id.startsWith(t)),L=p??(A.length===1?A[0]:void 0);if(!L)throw Error(A.length>1?`Recovery point prefix ${t} is ambiguous.`:`Recovery point ${t} was not found.`);return L}function Td(N,t){let p=N.store.getDestination(t)??N.store.listDestinations(N.controlPlane.project.id).find((A)=>A.name===t);if(!p)throw Error(`Backup destination ${t} was not found.`);return p}function qf(N,t){return N.kind==="volume"?{volumeName:t}:N.kind==="logical_database"?{targetId:t,dataServiceId:t}:{targetId:t}}function Gf(N,t=[]){let p=new Map(t.map((A)=>[A.id,A.name]));return N.map((A)=>[A.id,A.status,A.verificationState,A.kind,p.get(A.policyId??"")??"manual",A.pointInTime,A.sizeBytes?`${A.sizeBytes} B`:"provider",A.held?"hold":A.pinned?"pinned":"retention"])}function Kc(N){N.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)=>Di(async()=>Si(t.env,async(p)=>{let A=p.store.listPolicies(p.controlPlane.project.id,p.environmentRecord.id),L=new Set(A.map((Z)=>Z.id)),j=p.store.listRecoveryPoints(p.controlPlane.project.id).filter((Z)=>!Z.policyId||L.has(Z.policyId)),G={coverage:p.store.coverage(p.controlPlane.project.id).filter((Z)=>L.has(Z.policy.id)),destinations:p.store.listDestinations(p.controlPlane.project.id).map((Z)=>({...Z,credentialRef:void 0,encryptionKeyRef:void 0,credentialsConfigured:!!Z.credentialRef,clientEncryptionConfigured:!!Z.encryptionKeyRef})),policies:A,recoveryPoints:j,jobs:p.store.listJobs(p.controlPlane.project.id).filter((Z)=>!Z.policyId||L.has(Z.policyId))};if(t.json)e(JSON.stringify(G,null,2));else c(["Policy","RPO","Destination","Latest point","Verification"],G.coverage.map((Z)=>[Z.policy.name,Z.missedRpo?"MISSED":"protected",Z.destinationHealthy?"healthy":"attention",Z.lastRecoveryPoint?.pointInTime??"none",Z.lastRecoveryPoint?.verificationState??"none"])),c(["ID","State","Verification","Kind","Policy","Point in time","Size","Protection"],Gf(j,A))}))),N.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)=>Di(async()=>Si(t.env,async(p)=>{let A=new Set(p.store.listPolicies(p.controlPlane.project.id,p.environmentRecord.id).map((j)=>j.id)),L=p.store.listJobs(p.controlPlane.project.id).filter((j)=>!j.policyId||A.has(j.policyId));if(t.json)e(JSON.stringify(L,null,2));else c(["ID","Kind","State","Phase","Operation","Started","Finished","Error"],L.map((j)=>[j.id,j.kind,j.status,String(j.progress.phase??"queued"),j.operationId??"—",j.startedAt??"—",j.finishedAt??"—",j.error??"—"]))}))),N.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,p)=>Di(async()=>Si(p.env,async(A)=>{if(!["aws_s3","s3_compatible","aws_backup"].includes(p.provider??""))throw Error("--provider must be aws_s3, s3_compatible, or aws_backup.");if(!["provider","client_side","both"].includes(p.encryption??""))throw Error("--encryption must be provider, client_side, or both.");let L=`secret://data-services/backups/${A.controlPlane.project.id}/${t}`,j=[];try{let G,Z;if(p.credentialsEnv){let re=process.env[p.credentialsEnv];if(!re)throw Error(`${p.credentialsEnv} is empty.`);G=`${L}/credentials`,await A.secrets.put(G,re),j.push(G)}if(p.encryption!=="provider"){if(!p.encryptionKeyEnv)throw Error("Client encryption requires --encryption-key-env.");let re=process.env[p.encryptionKeyEnv];if(!re||re.length<32)throw Error(`${p.encryptionKeyEnv} must contain at least 32 characters.`);Z=`${L}/encryption-key`,await A.secrets.put(Z,re),j.push(Z)}let ee=A.store.createDestination({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,name:t,provider:p.provider,endpoint:p.endpoint,endpointPolicy:p.allowPrivate?"allow_private":"public_https",bucket:p.bucket,prefix:p.prefix??"",region:p.region??A.config.project.region,forcePathStyle:!!p.pathStyle,credentialRef:G,encryption:p.encryption,encryptionKeyRef:Z,immutability:{objectLock:!!p.lockDays,defaultRetentionDays:p.lockDays?Number(p.lockDays):void 0},status:"untested"});zi(A,"destination_created",{destinationId:ee.id,provider:ee.provider}),n(`Created destination ${ee.name} (${ee.id}); test it with cloud recovery:destination:test ${ee.name}`)}catch(G){for(let Z of j)await A.secrets.remove(Z).catch(()=>{});throw G}}))),N.command("recovery:destination:test <destination>","Write, read, verify, and clean a destination health object").option("--env <environment>","Target environment").action(async(t,p)=>Di(async()=>Si(p.env,async(A)=>{let L=Td(A,t);if(L.provider==="aws_backup")throw Error("AWS Backup is verified by creating a provider recovery point.");try{await new Un(A.secrets).test(L),A.store.recordDestinationTest(L.id,{ok:!0}),zi(A,"destination_tested",{destinationId:L.id,ok:!0}),n(`${L.name} passed write, read, checksum, and cleanup checks.`)}catch(j){let G=j instanceof Error?j.message:String(j);throw A.store.recordDestinationTest(L.id,{ok:!1,error:G}),zi(A,"destination_tested",{destinationId:L.id,ok:!1}),j}}))),N.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,p)=>Di(async()=>Si(p.env,async(A)=>{if(!p.destination)throw Error("--destination is required.");if(!["managed_database","logical_database","volume","files","control_plane","infrastructure"].includes(p.kind??""))throw Error("--kind must be managed_database, logical_database, volume, files, control_plane, or infrastructure.");let L=Td(A,p.destination),j=A.dataServices.list(A.controlPlane.project.id,A.environmentRecord.id),G=p.dataService?A.dataServices.get(p.dataService)??j.find((we)=>we.name===p.dataService):void 0,Z=A.controlPlane.store.listResources(A.controlPlane.project.id,A.environmentRecord.id),ee=p.resource?Z.find((we)=>we.id===p.resource||we.slug===p.resource):void 0,re=p.include?Array.isArray(p.include)?p.include:[p.include]:[],oe=p.exclude?Array.isArray(p.exclude)?p.exclude:[p.exclude]:[];if(["managed_database","logical_database"].includes(p.kind??"")&&!G)throw Error("--data-service must identify a service in this environment.");if(p.kind==="volume"&&(!ee||!p.volume))throw Error("Volume policies require --resource and --volume.");if(p.kind==="files"&&(!ee||!re.length))throw Error("File policies require --resource and at least one --include path.");if(p.kind==="infrastructure"&&(!re.some((we)=>we.startsWith("resource:arn:"))||!re.some((we)=>we.startsWith("role:arn:"))))throw Error("Infrastructure policies require --include resource:<arn> and --include role:<arn>.");let be=A.store.createPolicy({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,environmentId:A.environmentRecord.id,resourceId:ee?.id,dataServiceId:G?.id,destinationId:L.id,name:t,resourceKind:p.kind,schedule:p.schedule??"daily",timezone:p.timezone??"UTC",retention:{keepLast:Number(p.keep)||7,expireAfterDays:Number(p.expire)||30},compression:p.compression,encryption:"destination",includePatterns:p.volume?[p.volume,...re]:re,excludePatterns:oe,expectedRpoMinutes:Number(p.rpo)||1440,expectedRtoMinutes:Number(p.rto)||120,enabled:!p.disabled});zi(A,"policy_created",{policyId:be.id,resourceKind:be.resourceKind},be.resourceId),n(`Created policy ${be.name} (${be.id}); next run ${be.nextRunAt}`)}))),N.command("recovery:run <policy>","Queue an on-demand run of a recovery policy").option("--env <environment>","Target environment").action(async(t,p)=>Di(async()=>Si(p.env,async(A)=>{let L=Wf(A,t),j=A.coordinator.enqueueBackup(L,new Date().toISOString(),A.actor.id);zi(A,"run_queued",{policyId:L.id,backupJobId:j.id},L.resourceId),n(`Queued backup job ${j.id}; inspect it with cloud recovery:jobs`)}))),N.command("recovery:verify <point>","Queue independent verification of a recovery point").option("--env <environment>","Target environment").action(async(t,p)=>Di(async()=>Si(p.env,async(A)=>{let L=Qs(A,t),j=A.coordinator.enqueueVerification(L);zi(A,"verification_queued",{recoveryPointId:L.id,backupJobId:j.id},L.resourceId),n(`Queued verification job ${j.id}`)}))),N.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,p,A)=>Di(async()=>Si(A.env,async(L)=>{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 j=Qs(L,t),G={mode:A.mode,target:{...qf(j,p),...A.metadata?{metadata:JSON.parse(A.metadata)}:{},inPlace:A.mode==="in_place"},targetName:p,confirm:A.confirm,recentAuth:!0,downtimeAcknowledged:!!A.ackDowntime,safetyBackupId:A.safety?Qs(L,A.safety).id:void 0},Z=L.coordinator.planRestore(j,G);if(!A.execute){e(JSON.stringify({...Z,productionExecutionCreated:!1},null,2));return}let ee=L.coordinator.enqueueRestore(j,{...G,drill:!!A.drill,actorId:L.actor.id});zi(L,A.drill?"drill_queued":"restore_queued",{recoveryPointId:j.id,backupJobId:ee.id,mode:G.mode,target:p},j.resourceId),n(`Queued ${A.drill?"drill":"restore"} job ${ee.id}`)}))),N.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,p)=>Di(async()=>Si(p.env,async(A)=>{let L=Qs(A,t);if(![p.pin,p.unpin,p.hold,p.releaseHold].filter(Boolean).length)throw Error("Choose --pin, --unpin, --hold, or --release-hold.");let j=A.store.updateRecoveryPoint(L.id,{pinned:p.pin?!0:p.unpin?!1:L.pinned,held:p.hold?!0:p.releaseHold?!1:L.held});zi(A,"protection_updated",{recoveryPointId:L.id,pinned:j.pinned,held:j.held},L.resourceId),n(`${j.id}: pinned=${j.pinned} held=${j.held}`)}))),N.command("recovery:retention","Queue deletion of expired, unlocked, unheld recovery points").option("--env <environment>","Target environment").action(async(t)=>Di(async()=>Si(t.env,async(p)=>{let A=p.coordinator.enqueueRetention();zi(p,"retention_queued",{jobs:A.length}),n(`Queued ${A.length} retention cleanup job${A.length===1?"":"s"}.`)})))}function Jc(N){N.command("api:list","List all API Gateway APIs").option("--region <region>","AWS region").action(async(t)=>{i("API Gateway APIs");try{let p=await Me(),A=t.region||p.project.region||"us-east-1",{LambdaClient:L}=await import("./chunk-f2wn46nw.js"),j=new L(A),G=new o("Fetching APIs...");G.start();let{CloudFormationClient:Z}=await import("./chunk-dsc7ef1w.js"),oe=(await new Z(A).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE"])).StackSummaries.filter((be)=>be.StackName?.includes("api")||be.StackName?.includes("Api")||be.StackName?.includes("API"));if(G.succeed("APIs listed"),e(`
645
+ Start restore?`,!1)){e("Operation cancelled");return}let G=new o("Starting restore job...");G.start();let Z=await A.startRestoreJob({RecoveryPointArn:t,IamRoleArn:m.role,Metadata:L,IdempotencyToken:`cli-${Date.now()}`});G.succeed("Restore job started"),n(`
646
+ Job ID: ${Z.RestoreJobId}`),e("Use `cloud backup:restore-jobs` to check status")}catch(A){r(`Failed to start restore: ${A.message}`),process.exit(1)}}),N.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 Ne(),A=t.region||m.project.region||"us-east-1",L=await pi(A),j=new o("Fetching backup jobs...");j.start();let Z=(await L.listBackupJobs({ByState:t.state,MaxResults:50})).BackupJobs||[];if(j.succeed(`Found ${Z.length} job(s)`),Z.length===0){e("No backup jobs found");return}c(["Job ID","Resource","State","Started","Size"],Z.map((ee)=>[ee.BackupJobId?.substring(0,16)||"N/A",ee.ResourceArn?.split(":").pop()||"N/A",ee.State||"N/A",ee.CreationDate?new Date(ee.CreationDate).toLocaleString():"N/A",ee.BackupSizeInBytes?zd(ee.BackupSizeInBytes):"N/A"]))}catch(m){r(`Failed to list backup jobs: ${m.message}`),process.exit(1)}}),N.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 Ne(),A=t.region||m.project.region||"us-east-1",L=await pi(A),j=new o("Fetching restore jobs...");j.start();let Z=(await L.listRestoreJobs({ByStatus:t.status,MaxResults:50})).RestoreJobs||[];if(j.succeed(`Found ${Z.length} job(s)`),Z.length===0){e("No restore jobs found");return}c(["Job ID","Resource","Status","Started","Completed"],Z.map((ee)=>[ee.RestoreJobId?.substring(0,16)||"N/A",ee.CreatedResourceArn?.split(":").pop()||"Pending",ee.Status||"N/A",ee.CreationDate?new Date(ee.CreationDate).toLocaleString():"N/A",ee.CompletionDate?new Date(ee.CompletionDate).toLocaleString():"-"]))}catch(m){r(`Failed to list restore jobs: ${m.message}`),process.exit(1)}}),N.command("backup:schedule","Show backup schedule overview").option("--region <region>","AWS region").action(async(t)=>{i("Backup Schedule Overview");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",L=await pi(A),j=new o("Fetching backup plans...");j.start();let Z=(await L.listBackupPlans()).BackupPlansList||[];if(j.succeed(`Found ${Z.length} backup plan(s)`),Z.length===0){e("No backup plans configured");return}for(let ee of Z){e(`
647
+ ${ee.BackupPlanName}`),e("=".repeat(40));try{let oe=(await L.getBackupPlan(ee.BackupPlanId)).BackupPlan?.Rules||[];for(let we of oe)if(e(` Rule: ${we.RuleName}`),e(` Schedule: ${we.ScheduleExpression}`),e(` Vault: ${we.TargetBackupVaultName}`),we.Lifecycle?.DeleteAfterDays)e(` Retention: ${we.Lifecycle.DeleteAfterDays} days`);let be=await L.listBackupSelections(ee.BackupPlanId);if(be.BackupSelectionsList&&be.BackupSelectionsList.length>0)e(` Selections: ${be.BackupSelectionsList.length}`)}catch{e(" (Unable to load plan details)")}}}catch(m){r(`Failed to get schedule: ${m.message}`),process.exit(1)}})}function zd(N){if(N===0)return"0 B";let t=1024,m=["B","KB","MB","GB","TB"],A=Math.floor(Math.log(N)/Math.log(t));return`${Number.parseFloat((N/t**A).toFixed(2))} ${m[A]}`}async function eg(N){let t=await Ne(),m=y(process.cwd(),t),A=N??Object.keys(t.environments??{})[0]??"production",L=m.environments.get(A);if(!L)throw m.store.close(),Error(`Environment ${A} was not found.`);let j=new b(m.store,{workerId:`cli:${process.pid}`}),G=new Un(m.store),Z=new Lr(G,j),ee=new Gt(m.store),ne=new Kt(m.store,k(process.cwd())),oe=m.store.getActorByExternalId("system","cli")??m.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:t,controlPlane:m,env:A,environmentRecord:L,queue:j,store:G,coordinator:Z,dataServices:ee,secrets:ne,actor:oe}}async function Ti(N,t){let m=await eg(N);try{return await t(m)}finally{m.controlPlane.store.close()}}async function Fi(N){try{await N()}catch(t){r(t instanceof Error?t.message:String(t))}}function Hi(N,t,m,A){N.controlPlane.store.appendEvent({organizationId:N.controlPlane.organization.id,projectId:N.controlPlane.project.id,resourceId:A,actorId:N.actor.id,type:`backup.${t}`,payload:m})}function tg(N,t){let m=N.store.getPolicy(t)??N.store.listPolicies(N.controlPlane.project.id,N.environmentRecord.id).find((A)=>A.name===t);if(!m||m.environmentId!==N.environmentRecord.id)throw Error(`Backup policy ${t} was not found in ${N.env}.`);return m}function aa(N,t){let m=N.store.getRecoveryPoint(t),A=N.store.listRecoveryPoints(N.controlPlane.project.id).filter((j)=>j.id.startsWith(t)),L=m??(A.length===1?A[0]:void 0);if(!L)throw Error(A.length>1?`Recovery point prefix ${t} is ambiguous.`:`Recovery point ${t} was not found.`);return L}function Ud(N,t){let m=N.store.getDestination(t)??N.store.listDestinations(N.controlPlane.project.id).find((A)=>A.name===t);if(!m)throw Error(`Backup destination ${t} was not found.`);return m}function ng(N,t){return N.kind==="volume"?{volumeName:t}:N.kind==="logical_database"?{targetId:t,dataServiceId:t}:{targetId:t}}function rg(N,t=[]){let m=new Map(t.map((A)=>[A.id,A.name]));return N.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 il(N){N.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)=>Fi(async()=>Ti(t.env,async(m)=>{let A=m.store.listPolicies(m.controlPlane.project.id,m.environmentRecord.id),L=new Set(A.map((Z)=>Z.id)),j=m.store.listRecoveryPoints(m.controlPlane.project.id).filter((Z)=>!Z.policyId||L.has(Z.policyId)),G={coverage:m.store.coverage(m.controlPlane.project.id).filter((Z)=>L.has(Z.policy.id)),destinations:m.store.listDestinations(m.controlPlane.project.id).map((Z)=>({...Z,credentialRef:void 0,encryptionKeyRef:void 0,credentialsConfigured:!!Z.credentialRef,clientEncryptionConfigured:!!Z.encryptionKeyRef})),policies:A,recoveryPoints:j,jobs:m.store.listJobs(m.controlPlane.project.id).filter((Z)=>!Z.policyId||L.has(Z.policyId))};if(t.json)e(JSON.stringify(G,null,2));else c(["Policy","RPO","Destination","Latest point","Verification"],G.coverage.map((Z)=>[Z.policy.name,Z.missedRpo?"MISSED":"protected",Z.destinationHealthy?"healthy":"attention",Z.lastRecoveryPoint?.pointInTime??"none",Z.lastRecoveryPoint?.verificationState??"none"])),c(["ID","State","Verification","Kind","Policy","Point in time","Size","Protection"],rg(j,A))}))),N.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)=>Fi(async()=>Ti(t.env,async(m)=>{let A=new Set(m.store.listPolicies(m.controlPlane.project.id,m.environmentRecord.id).map((j)=>j.id)),L=m.store.listJobs(m.controlPlane.project.id).filter((j)=>!j.policyId||A.has(j.policyId));if(t.json)e(JSON.stringify(L,null,2));else c(["ID","Kind","State","Phase","Operation","Started","Finished","Error"],L.map((j)=>[j.id,j.kind,j.status,String(j.progress.phase??"queued"),j.operationId??"—",j.startedAt??"—",j.finishedAt??"—",j.error??"—"]))}))),N.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)=>Fi(async()=>Ti(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 L=`secret://data-services/backups/${A.controlPlane.project.id}/${t}`,j=[];try{let G,Z;if(m.credentialsEnv){let ne=process.env[m.credentialsEnv];if(!ne)throw Error(`${m.credentialsEnv} is empty.`);G=`${L}/credentials`,await A.secrets.put(G,ne),j.push(G)}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.`);Z=`${L}/encryption-key`,await A.secrets.put(Z,ne),j.push(Z)}let ee=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:G,encryption:m.encryption,encryptionKeyRef:Z,immutability:{objectLock:!!m.lockDays,defaultRetentionDays:m.lockDays?Number(m.lockDays):void 0},status:"untested"});Hi(A,"destination_created",{destinationId:ee.id,provider:ee.provider}),n(`Created destination ${ee.name} (${ee.id}); test it with cloud recovery:destination:test ${ee.name}`)}catch(G){for(let Z of j)await A.secrets.remove(Z).catch(()=>{});throw G}}))),N.command("recovery:destination:test <destination>","Write, read, verify, and clean a destination health object").option("--env <environment>","Target environment").action(async(t,m)=>Fi(async()=>Ti(m.env,async(A)=>{let L=Ud(A,t);if(L.provider==="aws_backup")throw Error("AWS Backup is verified by creating a provider recovery point.");try{await new Bn(A.secrets).test(L),A.store.recordDestinationTest(L.id,{ok:!0}),Hi(A,"destination_tested",{destinationId:L.id,ok:!0}),n(`${L.name} passed write, read, checksum, and cleanup checks.`)}catch(j){let G=j instanceof Error?j.message:String(j);throw A.store.recordDestinationTest(L.id,{ok:!1,error:G}),Hi(A,"destination_tested",{destinationId:L.id,ok:!1}),j}}))),N.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)=>Fi(async()=>Ti(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 L=Ud(A,m.destination),j=A.dataServices.list(A.controlPlane.project.id,A.environmentRecord.id),G=m.dataService?A.dataServices.get(m.dataService)??j.find((we)=>we.name===m.dataService):void 0,Z=A.controlPlane.store.listResources(A.controlPlane.project.id,A.environmentRecord.id),ee=m.resource?Z.find((we)=>we.id===m.resource||we.slug===m.resource):void 0,ne=m.include?Array.isArray(m.include)?m.include:[m.include]:[],oe=m.exclude?Array.isArray(m.exclude)?m.exclude:[m.exclude]:[];if(["managed_database","logical_database"].includes(m.kind??"")&&!G)throw Error("--data-service must identify a service in this environment.");if(m.kind==="volume"&&(!ee||!m.volume))throw Error("Volume policies require --resource and --volume.");if(m.kind==="files"&&(!ee||!ne.length))throw Error("File policies require --resource and at least one --include path.");if(m.kind==="infrastructure"&&(!ne.some((we)=>we.startsWith("resource:arn:"))||!ne.some((we)=>we.startsWith("role:arn:"))))throw Error("Infrastructure policies require --include resource:<arn> and --include role:<arn>.");let be=A.store.createPolicy({organizationId:A.controlPlane.organization.id,projectId:A.controlPlane.project.id,environmentId:A.environmentRecord.id,resourceId:ee?.id,dataServiceId:G?.id,destinationId:L.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:oe,expectedRpoMinutes:Number(m.rpo)||1440,expectedRtoMinutes:Number(m.rto)||120,enabled:!m.disabled});Hi(A,"policy_created",{policyId:be.id,resourceKind:be.resourceKind},be.resourceId),n(`Created policy ${be.name} (${be.id}); next run ${be.nextRunAt}`)}))),N.command("recovery:run <policy>","Queue an on-demand run of a recovery policy").option("--env <environment>","Target environment").action(async(t,m)=>Fi(async()=>Ti(m.env,async(A)=>{let L=tg(A,t),j=A.coordinator.enqueueBackup(L,new Date().toISOString(),A.actor.id);Hi(A,"run_queued",{policyId:L.id,backupJobId:j.id},L.resourceId),n(`Queued backup job ${j.id}; inspect it with cloud recovery:jobs`)}))),N.command("recovery:verify <point>","Queue independent verification of a recovery point").option("--env <environment>","Target environment").action(async(t,m)=>Fi(async()=>Ti(m.env,async(A)=>{let L=aa(A,t),j=A.coordinator.enqueueVerification(L);Hi(A,"verification_queued",{recoveryPointId:L.id,backupJobId:j.id},L.resourceId),n(`Queued verification job ${j.id}`)}))),N.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)=>Fi(async()=>Ti(A.env,async(L)=>{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 j=aa(L,t),G={mode:A.mode,target:{...ng(j,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?aa(L,A.safety).id:void 0},Z=L.coordinator.planRestore(j,G);if(!A.execute){e(JSON.stringify({...Z,productionExecutionCreated:!1},null,2));return}let ee=L.coordinator.enqueueRestore(j,{...G,drill:!!A.drill,actorId:L.actor.id});Hi(L,A.drill?"drill_queued":"restore_queued",{recoveryPointId:j.id,backupJobId:ee.id,mode:G.mode,target:m},j.resourceId),n(`Queued ${A.drill?"drill":"restore"} job ${ee.id}`)}))),N.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)=>Fi(async()=>Ti(m.env,async(A)=>{let L=aa(A,t);if(![m.pin,m.unpin,m.hold,m.releaseHold].filter(Boolean).length)throw Error("Choose --pin, --unpin, --hold, or --release-hold.");let j=A.store.updateRecoveryPoint(L.id,{pinned:m.pin?!0:m.unpin?!1:L.pinned,held:m.hold?!0:m.releaseHold?!1:L.held});Hi(A,"protection_updated",{recoveryPointId:L.id,pinned:j.pinned,held:j.held},L.resourceId),n(`${j.id}: pinned=${j.pinned} held=${j.held}`)}))),N.command("recovery:retention","Queue deletion of expired, unlocked, unheld recovery points").option("--env <environment>","Target environment").action(async(t)=>Fi(async()=>Ti(t.env,async(m)=>{let A=m.coordinator.enqueueRetention();Hi(m,"retention_queued",{jobs:A.length}),n(`Queued ${A.length} retention cleanup job${A.length===1?"":"s"}.`)})))}function ol(N){N.command("api:list","List all API Gateway APIs").option("--region <region>","AWS region").action(async(t)=>{i("API Gateway APIs");try{let m=await Ne(),A=t.region||m.project.region||"us-east-1",{LambdaClient:L}=await import("./chunk-wagzxm7z.js"),j=new L(A),G=new o("Fetching APIs...");G.start();let{CloudFormationClient:Z}=await import("./chunk-p831sptd.js"),oe=(await new Z(A).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE"])).StackSummaries.filter((be)=>be.StackName?.includes("api")||be.StackName?.includes("Api")||be.StackName?.includes("API"));if(G.succeed("APIs listed"),e(`
648
648
  API-related CloudFormation stacks:`),oe.length===0)e("No API Gateway stacks found"),e(`
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"],oe.map((be)=>[be.StackName||"N/A",be.StackStatus||"N/A",be.CreationTime?new Date(be.CreationTime).toLocaleDateString():"N/A"]));e("\nTip: Use AWS Console or `aws apigateway get-rest-apis` for detailed API listing")}catch(p){r(`Failed to list APIs: ${p.message}`),process.exit(1)}}),N.command("api:describe <apiId>","Show API Gateway API details").option("--region <region>","AWS region").action(async(t,p)=>{i(`API: ${t}`);try{let A=await Me(),L=p.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Region: ${L}`),e(""),e("For detailed API information, use AWS CLI:"),e(` aws apigateway get-rest-api --rest-api-id ${t} --region ${L}`),e(` aws apigateway get-resources --rest-api-id ${t} --region ${L}`),e(` aws apigateway get-stages --rest-api-id ${t} --region ${L}`)}catch(A){r(`Failed to describe API: ${A.message}`),process.exit(1)}}),N.command("api:stages <apiId>","List API stages").option("--region <region>","AWS region").action(async(t,p)=>{i(`API Stages: ${t}`);try{let A=await Me(),L=p.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Region: ${L}`),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 ${L}`)}catch(A){r(`Failed to list stages: ${A.message}`),process.exit(1)}}),N.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,p)=>{i("Deploy API");try{if(e(`API ID: ${t}`),e(`Stage: ${p.stage}`),!await d(`
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 ${p.stage} \\`),e(` --region ${p.region}`),p.description)e(` --description "${p.description}"`)}catch(A){r(`Failed to deploy API: ${A.message}`),process.exit(1)}}),N.command("api:domains","List custom domain names").option("--region <region>","AWS region").action(async(t)=>{i("API Gateway Custom Domains");try{let p=await Me(),A=t.region||p.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(p){r(`Failed to list domains: ${p.message}`),process.exit(1)}}),N.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,p)=>{i("API Usage Statistics");try{let A=await Me(),L=p.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Stage: ${p.stage}`),e(`Period: Last ${p.days} days`),e("");let j=new Date,G=new Date;G.setDate(G.getDate()-Number.parseInt(p.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=${p.stage} \\`),e(` --start-time ${G.toISOString()} \\`),e(` --end-time ${j.toISOString()} \\`),e(" --period 86400 \\"),e(" --statistics Sum \\"),e(` --region ${L}`),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)}}),N.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,p)=>{i("Export API Specification");try{e(`API ID: ${t}`),e(`Stage: ${p.stage}`),e(`Format: ${p.format==="oas30"?"OpenAPI 3.0":"Swagger 2.0"}`),e("");let A=p.format==="oas30"?"oas30":"swagger";e("To export the API specification:"),e(" aws apigateway get-export \\"),e(` --rest-api-id ${t} \\`),e(` --stage-name ${p.stage} \\`),e(` --export-type ${A} \\`),e(" --accepts application/json \\"),e(` --region ${p.region} \\`),e(` ${p.output||"api-spec.json"}`)}catch(A){r(`Failed to export API: ${A.message}`),process.exit(1)}}),N.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,p)=>{i("API Gateway Logs");try{let A=await Me(),L=p.region||A.project.region||"us-east-1",j=`API-Gateway-Execution-Logs_${t}/${p.stage}`;if(e(`Log Group: ${j}`),e(""),e("To view logs:"),e(" aws logs filter-log-events \\"),e(` --log-group-name "${j}" \\`),e(` --region ${L}`),p.tail)e(""),e("For real-time log tailing, use:"),e(` aws logs tail "${j}" --follow --region ${L}`);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)}}),N.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,p,A)=>{i("Test API Endpoint");try{let L=await Me(),j=A.region||L.project.region||"us-east-1",G=`https://${t}.execute-api.${j}.amazonaws.com/${A.stage}${p.startsWith("/")?p:`/${p}`}`;e(`URL: ${G}`),e(`Method: ${A.method}`);let Z={"Content-Type":"application/json"};if(A.header){let Ee=Array.isArray(A.header)?A.header:[A.header];for(let Se of Ee){let[De,..._e]=Se.split(":");Z[De.trim()]=_e.join(":").trim()}}e("Headers:");for(let[Ee,Se]of Object.entries(Z))e(` ${Ee}: ${Se}`);if(A.body)e(`Body: ${A.body}`);if(!await d(`
651
- Send request?`,!0)){e("Operation cancelled");return}let re=new o("Sending request...");re.start();let oe=Date.now(),be=await fetch(G,{method:A.method,headers:Z,body:A.body}),we=Date.now()-oe,Ce=await be.text();re.succeed(`Response received (${we}ms)`),e(`
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"],oe.map((be)=>[be.StackName||"N/A",be.StackStatus||"N/A",be.CreationTime?new Date(be.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)}}),N.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 Ne(),L=m.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Region: ${L}`),e(""),e("For detailed API information, use AWS CLI:"),e(` aws apigateway get-rest-api --rest-api-id ${t} --region ${L}`),e(` aws apigateway get-resources --rest-api-id ${t} --region ${L}`),e(` aws apigateway get-stages --rest-api-id ${t} --region ${L}`)}catch(A){r(`Failed to describe API: ${A.message}`),process.exit(1)}}),N.command("api:stages <apiId>","List API stages").option("--region <region>","AWS region").action(async(t,m)=>{i(`API Stages: ${t}`);try{let A=await Ne(),L=m.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Region: ${L}`),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 ${L}`)}catch(A){r(`Failed to list stages: ${A.message}`),process.exit(1)}}),N.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 d(`
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)}}),N.command("api:domains","List custom domain names").option("--region <region>","AWS region").action(async(t)=>{i("API Gateway Custom Domains");try{let m=await Ne(),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)}}),N.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 Ne(),L=m.region||A.project.region||"us-east-1";e(`API ID: ${t}`),e(`Stage: ${m.stage}`),e(`Period: Last ${m.days} days`),e("");let j=new Date,G=new Date;G.setDate(G.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 ${G.toISOString()} \\`),e(` --end-time ${j.toISOString()} \\`),e(" --period 86400 \\"),e(" --statistics Sum \\"),e(` --region ${L}`),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)}}),N.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)}}),N.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 Ne(),L=m.region||A.project.region||"us-east-1",j=`API-Gateway-Execution-Logs_${t}/${m.stage}`;if(e(`Log Group: ${j}`),e(""),e("To view logs:"),e(" aws logs filter-log-events \\"),e(` --log-group-name "${j}" \\`),e(` --region ${L}`),m.tail)e(""),e("For real-time log tailing, use:"),e(` aws logs tail "${j}" --follow --region ${L}`);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)}}),N.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 L=await Ne(),j=A.region||L.project.region||"us-east-1",G=`https://${t}.execute-api.${j}.amazonaws.com/${A.stage}${m.startsWith("/")?m:`/${m}`}`;e(`URL: ${G}`),e(`Method: ${A.method}`);let Z={"Content-Type":"application/json"};if(A.header){let De=Array.isArray(A.header)?A.header:[A.header];for(let Se of De){let[xe,...Me]=Se.split(":");Z[xe.trim()]=Me.join(":").trim()}}e("Headers:");for(let[De,Se]of Object.entries(Z))e(` ${De}: ${Se}`);if(A.body)e(`Body: ${A.body}`);if(!await d(`
651
+ Send request?`,!0)){e("Operation cancelled");return}let ne=new o("Sending request...");ne.start();let oe=Date.now(),be=await fetch(G,{method:A.method,headers:Z,body:A.body}),we=Date.now()-oe,Ce=await be.text();ne.succeed(`Response received (${we}ms)`),e(`
652
652
  Status: ${be.status} ${be.statusText}`),e(`
653
- Response Headers:`),be.headers.forEach((Ee,Se)=>{e(` ${Se}: ${Ee}`)}),e(`
654
- Response Body:`);try{let Ee=JSON.parse(Ce);console.log(JSON.stringify(Ee,null,2))}catch{console.log(Ce)}}catch(L){r(`Failed to test API: ${L.message}`),process.exit(1)}})}async function uo(N){let t=await Me(),p=y(process.cwd(),t),A=new b(p.store,{workerId:`cli:${process.pid}`});try{return await N(A,p.project.id,p.organization.id)}finally{p.store.close()}}function Hf(N){if(!N)return;if(!["queued","running","succeeded","failed","cancelled","timed_out"].includes(N))throw Error("State must be queued, running, succeeded, failed, cancelled, or timed_out");return N}function Kf(N,t){if(!N)return"—";let p=Math.max(0,new Date(t??Date.now()).getTime()-new Date(N).getTime());return p<1000?`${p}ms`:p<60000?`${Math.round(p/1000)}s`:`${Math.round(p/60000)}m`}function Yc(N){N.command("ops:list","List durable deployment and mutation jobs").option("--state <state>","Filter queue state").option("--all-projects","Show all locally authorized projects").action(async(t)=>{try{await uo(async(p,A)=>{let L=p.list({projectId:t.allProjects?void 0:A,state:Hf(t.state),limit:500});c(["ID","State","Operation","Target","Attempt","Step / blocked","Queued","Duration"],L.map(({operation:j,job:G,approximatePosition:Z})=>[j.id,j.state,j.kind,j.resourceId??j.environmentId??j.projectId??"global",`${j.attempt}/${G.maxAttempts}`,G.blockedReason??G.currentStep??(Z?`${Z.ahead} ahead`:"—"),j.createdAt,Kf(j.startedAt,j.finishedAt)]))})}catch(p){r(p instanceof Error?p.message:String(p))}}),N.command("ops:show <id>","Show a durable operation and its latest sanitized logs").option("--after <cursor>","Show logs after this sequence",{default:"0"}).action(async(t,p)=>{try{await uo(async(A)=>{let L=A.view(t);if(!L)throw Error(`Operation ${t} was not found`);e(JSON.stringify(L,null,2));for(let j of A.logs(t,{after:Number(p.after)||0,limit:500}))e(`${j.sequence} ${j.stream.toUpperCase()}${j.step?` [${j.step}]`:""} ${j.message}`)})}catch(A){r(A instanceof Error?A.message:String(A))}}),N.command("ops:cancel <id>","Cancel queued work or request running cancellation").option("--yes","Skip confirmation").action(async(t,p)=>{try{await uo(async(A)=>{let L=A.view(t);if(!L)throw Error(`Operation ${t} was not found`);if(!p.yes&&!await d(`Cancel ${L.operation.kind} (${t})?`,!1))return;let j=A.requestCancellation(t);n(`${j.state==="cancelled"?"Cancelled":"Cancellation requested for"} ${t}`)})}catch(A){r(A instanceof Error?A.message:String(A))}}),N.command("ops:retry <id>","Retry a terminal job using an allow-listed error class").option("--class <name>","Retryable error class").option("--delay <milliseconds>","Delay before the next attempt",{default:"0"}).action(async(t,p)=>{try{if(!p.class)throw Error("Pass --class with one of the retry classes shown by ops:show");await uo(async(A)=>{let L=A.retry(t,p.class,{delayMs:Number(p.delay)||0});n(`Retry queued: ${L.id} · attempt ${L.attempt+1}`)})}catch(A){r(A instanceof Error?A.message:String(A))}}),N.command("ops:concurrency","Show effective durable queue concurrency limits").action(async()=>{try{await uo(async(t)=>e(JSON.stringify(t.limits,null,2)))}catch(t){r(t instanceof Error?t.message:String(t))}}),N.command("ops:concurrency:set","Confirm and audit durable queue concurrency limits").option("--project <count>","Concurrent jobs per project").option("--environment <count>","Concurrent jobs per environment").option("--provider <count>","Concurrent jobs per provider").option("--builds <count>","Concurrent build slots").option("--confirm <text>",'Must equal "update queue limits"').action(async(t)=>{try{if(t.confirm!=="update queue limits")throw Error('Pass --confirm "update queue limits" to change production concurrency');let p={};for(let A of["project","environment","provider","builds"])if(t[A]!==void 0)p[A]=Number(t[A]);await uo(async(A,L,j)=>n(`Queue concurrency updated: ${JSON.stringify(A.configureConcurrency(p,{organizationId:j}))}`))}catch(p){r(p instanceof Error?p.message:String(p))}}),N.command("ops:history:clear","Clear terminal jobs after their retention policy expires").option("--before <date>","Also require completion before this timestamp").option("--yes","Skip confirmation").action(async(t)=>{try{await uo(async(p,A)=>{if(!t.yes&&!await d("Clear completed queue history whose retention has elapsed?",!1))return;let L=p.clearCompleted({projectId:A,before:t.before});n(`Cleared ${L} retained operation${L===1?"":"s"}.`)})}catch(p){r(p instanceof Error?p.message:String(p))}})}import{readFile as Jf,writeFile as Yf}from"node:fs/promises";function Zf(N){if(N==null)return"—";if(N<60)return`${N}s`;if(N<3600)return`${Math.floor(N/60)}m`;if(N<86400)return`${Math.floor(N/3600)}h`;return`${Math.floor(N/86400)}d`}function Qf(N){return N==null?"—":Yr(N)}function Xf(N){return N.map((t)=>[t.id,t.name,t.provider,t.kind,t.status,t.desiredReplicas==null?"—":`${t.runningReplicas??0}/${t.desiredReplicas}`,t.image??t.runtime??"—",Qf(t.resources?.memoryBytes??t.resources?.memoryLimitBytes),Zf(t.ageSeconds)])}function eg(N,t){let p=t.search?.toLowerCase();return{...N,workloads:N.workloads.filter((A)=>(!t.provider||A.provider===t.provider)&&(!t.status||A.status===t.status)&&(!p||`${A.name} ${A.id} ${A.links.service??""} ${Object.values(A.tags).join(" ")}`.toLowerCase().includes(p)))}}async function mo(N){let t=await Me(),p=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},p))throw Error(`Environment ${p} was not found`);let A=new ge(t,p);return{config:t,env:p,service:A}}async function Xs(N,t){if(t!==void 0)return t;return I(`Type ${N.name} to confirm`,"")}async function tg(N,t,p,A,L,j){return ea(N,`runtime.${t}`,{workloadId:N.id,provider:N.provider,action:t,replicas:p??null},L,()=>j.run({workloadId:N.id,action:t,replicas:p,confirm:A,recentAuth:!0}))}async function ea(N,t,p,A,L){let j=await Me(),G=y(process.cwd(),j),Z=new b(G.store,{workerId:`cli:${process.pid}`});try{let ee=G.environments.get(A),re=G.store.listResources(G.project.id,ee?.id).find((we)=>we.slug===N.links.service),oe=G.store.getActorByExternalId("system","cli")??G.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI",metadata:{source:"cli"}}),be=Z.enqueue({projectId:G.project.id,environmentId:ee?.id,resourceId:re?.id,actorId:oe.id,kind:t,input:h(p),lockKey:re?`resource:${re.id}`:`runtime:${N.id}`,providerKey:N.provider,maxAttempts:1,timeoutSeconds:3600,resumePolicy:"fail",cancellationMode:"provider_non_cancellable",retentionDays:90}).operation;if(!Z.claim(be.id))throw Error(`Workload is locked by another operation. Inspect ${be.id} with cloud ops:show.`);try{let we=await L();if(we.stdout)Z.appendLog(be.id,we.stdout,{stream:"stdout"});if(we.stderr)Z.appendLog(be.id,we.stderr,{stream:"stderr"});if(we.ok)Z.complete(be.id,{ok:!0,command:we.command??null});else Z.fail(be.id,we.error??we.stderr??"Runtime operation failed",{ok:!1});return{result:we,operationId:be.id}}catch(we){throw Z.fail(be.id,we instanceof Error?we.message:String(we),{ok:!1,threw:!0}),we}}finally{G.store.close()}}function Zc(N){N.command("runtime:list","List services, containers, ECS tasks, and Lambda functions").option("--env <environment>","Target environment").option("--provider <provider>","Filter systemd, docker, ecs, or lambda").option("--status <status>","Filter normalized runtime status").option("--search <query>","Search names, IDs, services, and tags").option("--json","Print normalized JSON").action(async(t)=>{try{let{service:p}=await mo(t.env),A=eg(await p.inventory(),t);if(t.json)e(JSON.stringify(A,null,2));else{c(["ID","Name","Provider","Kind","Status","Replicas","Image / runtime","Memory","Age"],Xf(A.workloads));for(let L of A.sources.filter((j)=>j.status!=="fresh"))a(`${L.id}: ${L.status}${L.message?` — ${L.message}`:""}`)}}catch(p){r(p instanceof Error?p.message:String(p)),process.exitCode=1}}),N.command("runtime:inspect <workload>","Inspect normalized config, replicas, networks, mounts, links, and capabilities").option("--env <environment>","Target environment").action(async(t,p)=>{try{let{service:A}=await mo(p.env),L=await A.workload(t);if(!L)throw Error("Workload was not found in this project and environment");e(JSON.stringify(L,null,2))}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("runtime:logs <workload>","Read bounded logs for one authorized runtime target").option("--env <environment>","Target environment").option("--lines <count>","Maximum log lines",{default:"200"}).option("--json","Print structured JSON").action(async(t,p)=>{try{let{service:A}=await mo(p.env),L=await A.logs(t,{limit:Number(p.lines)||200});if(p.json)e(JSON.stringify(L,null,2));else for(let j of L.lines)console.log(`${j.timestamp??"—"} ${j.message}`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("runtime:action <workload> <action>","Start, stop, restart, redeploy, or scale one workload").option("--env <environment>","Target environment").option("--replicas <count>","Desired replicas for scale").option("--confirm <name>","Exact workload name confirmation").action(async(t,p,A)=>{try{let L=p;if(!["start","stop","restart","redeploy","scale"].includes(L))throw Error("Action must be start, stop, restart, redeploy, or scale");let j=A.replicas==null?void 0:Number(A.replicas);if(L==="scale"&&(!Number.isInteger(j)||j<0))throw Error("Scale requires a non-negative integer --replicas value");let{env:G,service:Z}=await mo(A.env),ee=await Z.inventory(),re=ee.workloads.find((we)=>we.id===t);if(!re)throw Error("Workload was not found in this project and environment");let oe=new ge(await Me(),G,{inventory:async()=>ee}),be=await tg(re,L,j,await Xs(re,A.confirm),G,oe);if(!be.result.ok)throw Error(be.result.error??be.result.stderr??"Runtime operation failed");if(n(`${L} completed for ${re.name} · operation ${be.operationId}`),be.result.stdout)e(be.result.stdout)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.command("runtime:exec <workload>","Run a scoped diagnostic preset or container command").option("--env <environment>","Target environment").option("--preset <preset>","Read-only preset: process, sockets, or filesystem").option("--command <command>","Free-form command (Docker containers only)").option("--confirm <name>","Exact workload name confirmation").action(async(t,p)=>{try{let{env:A,service:L}=await mo(p.env),G=(await L.inventory()).workloads.find((oe)=>oe.id===t);if(!G)throw Error("Workload was not found in this project and environment");let Z=["process","sockets","filesystem"].includes(String(p.preset))?p.preset:void 0;if(p.preset&&!Z)throw Error("Preset must be process, sockets, or filesystem");if(!Z&&!p.command)throw Error("Pass --preset or --command");let ee=await Xs(G,p.confirm),re=await ea(G,"runtime.exec",{workloadId:t,provider:G.provider,preset:Z??null,freeForm:!!p.command},A,()=>L.exec({workloadId:t,preset:Z,command:p.command,confirm:ee,recentAuth:!0}));if(!re.result.ok)throw Error(re.result.error??re.result.stderr??"Exec failed");if(re.result.stdout)e(re.result.stdout);if(re.result.stderr)a(re.result.stderr);n(`Exec completed for ${G.name} · operation ${re.operationId}`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("runtime:download <workload> <remote> <local>","Download a bounded file from a service-owned path").option("--env <environment>","Target environment").option("--confirm <name>","Exact workload name confirmation").action(async(t,p,A,L)=>{try{let{env:j,service:G}=await mo(L.env),ee=(await G.inventory()).workloads.find((be)=>be.id===t);if(!ee)throw Error("Workload was not found in this project and environment");let re=await Xs(ee,L.confirm),oe=await ea(ee,"runtime.file.read",{workloadId:t,provider:ee.provider,path:p},j,()=>G.readFile({workloadId:t,path:p,confirm:re,recentAuth:!0}));if(!oe.result.ok||!oe.result.contentBase64)throw Error(oe.result.error??"File download failed");await Yf(A,Buffer.from(oe.result.contentBase64,"base64")),n(`Downloaded ${oe.result.size??0} bytes to ${A} · operation ${oe.operationId}${oe.result.truncated?" (truncated)":""}`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}}),N.command("runtime:upload <workload> <local> <remote>","Upload and atomically replace a file in a service-owned path").option("--env <environment>","Target environment").option("--confirm <name>","Exact workload name confirmation").action(async(t,p,A,L)=>{try{let{env:j,service:G}=await mo(L.env),ee=(await G.inventory()).workloads.find((we)=>we.id===t);if(!ee)throw Error("Workload was not found in this project and environment");let re=await Xs(ee,L.confirm),oe=(await Jf(p)).toString("base64"),be=await ea(ee,"runtime.file.write",{workloadId:t,provider:ee.provider,path:A,bytes:Buffer.from(oe,"base64").byteLength},j,()=>G.writeFile({workloadId:t,path:A,contentBase64:oe,confirm:re,recentAuth:!0}));if(!be.result.ok)throw Error(be.result.error??"File upload failed");n(`Uploaded ${be.result.size??0} bytes to ${A} · operation ${be.operationId}`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}})}function ng(N,t=new Date){let p=N.to?new Date(N.to):t;if(!Number.isFinite(p.getTime()))throw Error("--to must be a valid ISO-8601 instant");if(N.from){let j=new Date(N.from);if(!Number.isFinite(j.getTime()))throw Error("--from must be a valid ISO-8601 instant");return{from:j.toISOString(),to:p.toISOString()}}let A=String(N.range??"1h").match(/^(\d+)(m|h|d)$/);if(!A)throw Error("--range must use minutes, hours, or days (for example 30m, 6h, or 7d)");let L=A[2]==="m"?60000:A[2]==="h"?3600000:86400000;return{from:new Date(p.getTime()-Number(A[1])*L).toISOString(),to:p.toISOString()}}async function fs(N){let t=await Me(),p=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},p))throw Error(`Environment ${p} was not found`);let A=y(process.cwd(),t),L=A.environments.get(p),j=new We(A.store);return{config:t,env:p,controlPlane:A,environmentRecord:L,telemetry:j}}function Nd(N,t,p){let A=(j)=>j?.split(",").map((G)=>G.trim()).filter(Boolean)||void 0,L=A(N.kind)?.filter((j)=>["metric","log","trace","request","event"].includes(j));return{projectId:t,environmentId:p,...ng(N),kinds:L,names:A(N.name),sources:A(N.source),text:N.text,limit:Math.min(5000,Math.max(1,Number(N.limit)||200))}}function rg(N){let t=["timestamp","kind","source","name","level","value","unit","message","traceId","requestId","deploymentId","releaseId","workloadId"];return[t.join(","),...N.map((p)=>t.map((A)=>`"${String(p[A]??"").replaceAll('"','""')}"`).join(","))].join(`
655
- `)}function Qc(N){N.command("telemetry:collect","Collect normalized host/runtime or AWS telemetry into the local control plane").option("--env <environment>","Target environment").option("--force","Bypass the provider-query cache").option("--json","Print structured JSON").action(async(t)=>{let p=await fs(t.env);try{let A=await Bn({controlPlane:p.controlPlane.store,projectId:p.controlPlane.project.id,environmentId:p.environmentRecord?.id,config:p.config,environment:p.env,force:t.force});if(t.json)e(JSON.stringify(A,null,2));else n(`Collected ${A.collected} normalized records${A.cached?" (cached provider result)":""}.`),c(["Source","Freshness","Lag","Sampling","Retention"],A.statuses.map((L)=>[L.source,L.freshness,L.lagSeconds==null?"—":`${L.lagSeconds}s`,`${Math.round(L.samplingRate*100)}%`,`${L.retentionDays}d`]))}finally{p.controlPlane.store.close()}}),N.command("telemetry:query","Query bounded normalized metrics, logs, traces, requests, and events").option("--env <environment>","Target environment").option("--from <instant>","ISO-8601 start").option("--to <instant>","ISO-8601 end").option("--range <duration>","Relative window such as 1h or 7d",{default:"1h"}).option("--kind <kinds>","Comma-separated kinds").option("--name <names>","Comma-separated signal names").option("--source <sources>","Comma-separated sources").option("--text <query>","Search name/message").option("--limit <count>","Maximum records",{default:"200"}).option("--json","Print structured JSON").action(async(t)=>{let p=await fs(t.env);try{let A=p.telemetry.query(Nd(t,p.controlPlane.project.id,p.environmentRecord?.id));if(t.json)e(JSON.stringify(A,null,2));else c(["Time","Kind","Source","Signal","Value / message","Correlation"],A.records.map((L)=>[L.timestamp,L.kind,L.source,L.name,L.message??(L.value==null?"—":`${L.value} ${L.unit??""}`),L.requestId??L.traceId??L.releaseId??L.deploymentId??"—"]));if(A.truncated)a("More records are available; narrow the window or use the dashboard cursor explorer.")}finally{p.controlPlane.store.close()}}),N.command("telemetry:export <file>","Export a bounded telemetry query as JSON or CSV").option("--env <environment>","Target environment").option("--from <instant>","ISO-8601 start").option("--to <instant>","ISO-8601 end").option("--range <duration>","Relative window",{default:"1h"}).option("--kind <kinds>","Comma-separated kinds").option("--name <names>","Comma-separated signal names").option("--source <sources>","Comma-separated sources").option("--text <query>","Search name/message").option("--limit <count>","Maximum records",{default:"5000"}).action(async(t,p)=>{let A=await fs(p.env);try{let L=Nd(p,A.controlPlane.project.id,A.environmentRecord?.id),j=A.telemetry.query(L);await Bun.write(t,t.toLowerCase().endsWith(".csv")?rg(j.records):JSON.stringify({exportedAt:new Date().toISOString(),query:L,...j},null,2)),n(`Exported ${j.records.length} records to ${t}${j.truncated?" (bounded result)":""}.`)}finally{A.controlPlane.store.close()}}),N.command("telemetry:compact","Downsample old metrics and enforce configured retention").option("--env <environment>","Target environment").action(async(t)=>{let p=await fs(t.env);try{let A=He(p.controlPlane.store,p.controlPlane.project.id);e(JSON.stringify(p.telemetry.enforceRetention(A,p.controlPlane.project.id),null,2))}finally{p.controlPlane.store.close()}}),N.command("telemetry:policy","Show or update sampling, retention, and local storage cost assumptions").option("--env <environment>","Target environment").option("--raw-days <days>","Raw retention days").option("--downsample-days <days>","Downsample after days").option("--sampling <rate>","Sampling rate from 0.01 to 1").option("--storage-usd-gb-month <amount>","Local storage cost assumption").option("--json","Print structured JSON").action(async(t)=>{let p=await fs(t.env);try{let A={};if(t.rawDays!=null)A.rawDays=Number(t.rawDays);if(t.downsampleDays!=null)A.downsampleAfterDays=Number(t.downsampleDays);if(t.sampling!=null)A.samplingRate=Number(t.sampling);if(t.storageUsdGbMonth!=null)A.estimatedStorageUsdPerGbMonth=Number(t.storageUsdGbMonth);let L=Object.keys(A).length?Kn(p.controlPlane.store,p.controlPlane.project.id,A):He(p.controlPlane.store,p.controlPlane.project.id),G=p.telemetry.status(p.controlPlane.project.id,p.environmentRecord?.id,L.rawDays,L.samplingRate).reduce((ee,re)=>ee+re.estimatedDailyBytes*30,0),Z={policy:L,estimatedMonthlyBytes:G,estimatedMonthlyCostUsd:Jn(G,L),costEstimateConfigured:L.estimatedStorageUsdPerGbMonth>0};if(t.json)e(JSON.stringify(Z,null,2));else e(JSON.stringify(L,null,2)),e(Z.costEstimateConfigured?`Estimated local storage: $${Z.estimatedMonthlyCostUsd}/month`:"Storage cost remains unavailable until --storage-usd-gb-month is set.")}finally{p.controlPlane.store.close()}})}async function no(N){let t=await Me(),p=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},p))throw Error(`Environment ${p} was not found`);let A=y(process.cwd(),t),L=A.environments.get(p),j=new pt(A.store,{encryptionKey:k(process.cwd())});return{config:t,env:p,controlPlane:A,environmentRecord:L,alerts:j}}function Od(N){return new lr(N,{emailImpl:async(t)=>{let{email:p}=await import("./chunk-z6mnhd0j.js");return p.send(t)}})}function Xc(N){N.command("health:list","List configured health checks and latest results").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{let p=await no(t.env);try{let A=p.alerts.listHealthChecks(p.controlPlane.project.id,p.environmentRecord?.id).map((L)=>({...L,latest:p.alerts.listHealthResults(L.id,1)[0]}));if(t.json)e(JSON.stringify(A,null,2));else c(["ID","Name","Kind","Target","Status","Interval","Enabled"],A.map((L)=>[L.id,L.name,L.kind,L.target,L.latest?.status??"no data",`${L.intervalSeconds}s`,L.enabled?"yes":"no"]))}finally{p.controlPlane.store.close()}}),N.command("health:run <check>","Run one health check and evaluate linked alert rules").option("--env <environment>","Target environment").option("--test","Preview without persisting results or incidents").action(async(t,p)=>{let A=await no(p.env);try{let L=A.alerts.getHealthCheck(t)??A.alerts.listHealthChecks(A.controlPlane.project.id,A.environmentRecord?.id).find((Z)=>Z.name===t);if(!L)throw Error("Health check was not found");let j=new Oe(A.alerts);if(p.test){let Z=await j.probe(L);e(JSON.stringify({...Z,productionIncidentCreated:!1},null,2));return}let G=await j.runAndEvaluate(L);if(e(JSON.stringify(G,null,2)),G.result.status==="healthy")n(`${L.name} is healthy.`);else a(`${L.name} is ${G.result.status}.`)}finally{A.controlPlane.store.close()}}),N.command("alerts:list","List pending, firing, silenced, and resolved alerts").option("--env <environment>","Target environment").option("--state <states>","Comma-separated states").option("--json","Print structured JSON").action(async(t)=>{let p=await no(t.env);try{let A=p.alerts.listAlerts(p.controlPlane.project.id,{environmentId:p.environmentRecord?.id,states:t.state?.split(",").filter(Boolean)});if(t.json)e(JSON.stringify(A,null,2));else c(["ID","State","Severity","Title","Owner","First seen","Last seen"],A.map((L)=>[L.id,L.state,L.severity,L.title,L.ownerActorId??"unassigned",L.firstSeenAt,L.lastSeenAt]))}finally{p.controlPlane.store.close()}}),N.command("alerts:evaluate","Evaluate persisted telemetry rules now").option("--env <environment>","Target environment").action(async(t)=>{let p=await no(t.env);try{let A=qr(p.alerts,p.controlPlane.project.id,p.environmentRecord?.id);e(JSON.stringify(A,null,2))}finally{p.controlPlane.store.close()}}),N.command("alerts:ack <alert>","Acknowledge an alert as the CLI actor").option("--env <environment>","Target environment").action(async(t,p)=>{let A=await no(p.env);try{let L=A.controlPlane.store.getActorByExternalId("system","cli")??A.controlPlane.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});n(`Acknowledged ${A.alerts.acknowledge(t,L.id).title}.`)}finally{A.controlPlane.store.close()}}),N.command("notifications:list","List notification channels, routes, and recent delivery diagnostics").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{let p=await no(t.env);try{let A={channels:p.alerts.listChannels(p.controlPlane.organization.id),routes:p.alerts.listRoutes(p.controlPlane.organization.id),deliveries:p.alerts.listDeliveries({limit:100})};if(t.json)e(JSON.stringify(A,null,2));else c(["Channel","Type","Status","Tested","Error"],A.channels.map((L)=>[L.name,L.kind,L.status,L.lastTestedAt??"never",L.lastError??"—"])),c(["Event","State","Attempts","Status","Error"],A.deliveries.map((L)=>[L.eventType,L.state,`${L.attempt}/${L.maxAttempts}`,String(L.responseStatus??"—"),L.error??"—"]))}finally{p.controlPlane.store.close()}}),N.command("notifications:test <channel>","Send a channel test without creating a production incident").option("--env <environment>","Target environment").action(async(t,p)=>{let A=await no(p.env);try{let L=A.alerts.getChannel(t)??A.alerts.listChannels(A.controlPlane.organization.id).find((G)=>G.name===t);if(!L)throw Error("Notification channel was not found");let j=await Od(A.alerts).testChannel(L.id);if(j.ok)n("Test delivered; no production incident was created.");else throw Error(j.error)}finally{A.controlPlane.store.close()}}),N.command("notifications:retry <delivery>","Retry one failed notification delivery").option("--env <environment>","Target environment").action(async(t,p)=>{let A=await no(p.env);try{let L=A.alerts.getDelivery(t);if(!L)throw Error("Notification delivery was not found");A.alerts.updateDelivery(t,{state:"retrying",attempt:Math.max(0,L.attempt-1),nextAttemptAt:new Date().toISOString()}),e(JSON.stringify(await Od(A.alerts).deliver(t),null,2))}finally{A.controlPlane.store.close()}})}async function ig(N,t){let p=await Me(),A=y(process.cwd(),p),L=!N&&t?new _r(A.store).get(t):void 0,j=L?.environmentId?A.store.listEnvironments(A.project.id).find((Ce)=>Ce.id===L.environmentId)?.slug:void 0,G=N??j??Object.keys(p.environments??{})[0]??"production";if(!Object.hasOwn(p.environments??{},G))throw A.store.close(),Error(`Environment ${G} was not found`);let Z=A.environments.get(G);if(!Z)throw A.store.close(),Error(`Environment ${G} was not found in the control plane`);let ee=new _r(A.store),re=new b(A.store,{workerId:`cli:${process.pid}`}),oe=new Ln(ee,{queue:re}),be=A.store.listResources(A.project.id,Z.id);$r(ee,p,{organization:A.organization,project:A.project,environment:Z,resources:be});let we=A.store.getActorByExternalId("system","cli")??A.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:p,env:G,controlPlane:A,environmentRecord:Z,resources:be,store:ee,queue:re,service:oe,actor:we}}async function li(N,t,p){let A=await ig(N,p);try{return await t(A)}finally{A.controlPlane.store.close()}}async function Kr(N){try{await N()}catch(t){r(t instanceof Error?t.message:String(t))}}function Do(N,t){let p=N.store.list(N.controlPlane.project.id,{environmentId:N.environmentRecord.id}),A=N.store.get(t)??p.find((L)=>L.name===t);if(!A||A.environmentId!==N.environmentRecord.id)throw Error(`Scheduled job ${t} was not found in ${N.env}`);return A}function po(N,t,p,A){N.controlPlane.store.appendEvent({organizationId:N.controlPlane.organization.id,projectId:N.controlPlane.project.id,resourceId:A,actorId:N.actor.id,type:`job.${t}`,payload:p})}function Ld(N,t,p){if(N.environmentRecord.kind==="production"&&p!==t)throw Error(`Production ${t} requires --confirm ${t}`)}function og(N){if(!N)return{};if(!/^(?:secret|ssm|arn|env):\/\//.test(N))throw Error("Payloads must use a secret://, ssm://, arn://, or env:// reference");return{input:N}}function sg(N){return N.map((t)=>[t.id,t.enabled?"enabled":"disabled",t.provider,t.name,t.expression,t.timezone,t.nextRunAt??"—",t.reconciliationStatus])}function el(N){N.command("jobs:list","List server cron, EventBridge, and recurring control-plane jobs").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>Kr(async()=>li(t.env,async(p)=>{let A=p.store.list(p.controlPlane.project.id,{environmentId:p.environmentRecord.id});if(t.json)e(JSON.stringify(A,null,2));else c(["ID","State","Provider","Name","Expression","Timezone","Next run","Reconciliation"],sg(A))}))),N.command("jobs:preview <expression>","Preview the next runs without creating production work").option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--from <date>","Preview from an ISO timestamp").option("--count <count>","Number of runs",{default:"5"}).action(async(t,p)=>Kr(async()=>{let A=ot(t,p.timezone??"UTC",p.from?new Date(p.from):new Date,Number(p.count)||5);e(JSON.stringify({...A,productionExecutionCreated:!1},null,2))})),N.command("jobs:add <name> <expression>","Create a managed recurring job").option("--env <environment>","Target environment").option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--operation <id>","Safe server operation target (scheduler, backup, or worker)").option("--resource <id>","Resource ID or slug").option("--starts-at <date>","Schedule window start").option("--ends-at <date>","Schedule window end").option("--flexible-minutes <count>","Provider flexible window",{default:"0"}).option("--missed <policy>","skip or catch_up",{default:"skip"}).option("--overlap <policy>","allow, forbid, or replace",{default:"forbid"}).option("--attempts <count>","Maximum attempts",{default:"3"}).option("--backoff <seconds>","Retry backoff seconds",{default:"30"}).option("--timeout <seconds>","Execution timeout",{default:"900"}).option("--dead-letter <reference>","Dead-letter ARN or secret reference").option("--payload-ref <reference>","Referenced payload; inline values are forbidden").option("--disabled","Create disabled").action(async(t,p,A)=>Kr(async()=>li(A.env,async(L)=>{if(!["skip","catch_up"].includes(A.missed??"skip"))throw Error("--missed must be skip or catch_up");if(!["allow","forbid","replace"].includes(A.overlap??"forbid"))throw Error("--overlap must be allow, forbid, or replace");let j=V(L.config),Z={workers:L.store.listWorkers(L.controlPlane.project.id,L.environmentRecord.id).map((Ce)=>({name:Ce.name}))},ee;if(j==="server"){let Ce=Dr(L.config,Z).filter((Se)=>!Se.danger&&["scheduler","backup","worker"].includes(Se.group)),Ee=A.operation?Ce.find((Se)=>Se.id===A.operation):Ce.length===1?Ce[0]:void 0;if(!Ee)throw Error(`Choose a safe target with --operation. Available: ${Ce.map((Se)=>Se.id).join(", ")||"none"}`);ee={kind:"dashboard_operation",operationId:Ee.id}}else ee={kind:"serverless_scheduler",action:"run"};let re=j==="server"?"server":"eventbridge",oe=On({provider:re,expression:p,flexibleMinutes:Number(A.flexibleMinutes)||0,missedRunPolicy:A.missed??"skip",overlapPolicy:A.overlap??"forbid"});if(!oe.supported)throw Error(oe.notes.join(" "));let be=A.resource?L.resources.find((Ce)=>Ce.id===A.resource||Ce.slug===A.resource):void 0;if(A.resource&&!be)throw Error(`Resource ${A.resource} was not found in ${L.env}`);let we=L.store.create({organizationId:L.controlPlane.organization.id,projectId:L.controlPlane.project.id,environmentId:L.environmentRecord.id,resourceId:be?.id,name:t,provider:re,expression:p,timezone:A.timezone??"UTC",startsAt:A.startsAt,endsAt:A.endsAt,flexibleMinutes:Number(A.flexibleMinutes)||0,target:ee,payloadRefs:og(A.payloadRef),missedRunPolicy:A.missed??"skip",overlapPolicy:A.overlap??"forbid",retryPolicy:{maxAttempts:Number(A.attempts)||3,backoffSeconds:Number(A.backoff)||30,deadLetterRef:A.deadLetter},timeoutSeconds:Number(A.timeout)||900,enabled:!A.disabled,origin:"managed",ownerActorId:L.actor.id,observedState:{},reconciliationStatus:"pending"});po(L,"created",{jobId:we.id,provider:we.provider,expression:we.normalizedExpression},we.resourceId),n(`Created ${we.name} (${we.id}); next run ${we.nextRunAt??"outside the active window"}`)}))),N.command("jobs:update <job>","Update a managed job schedule and policies").option("--env <environment>","Target environment").option("--expression <expression>","Cron, preset, or rate expression").option("--timezone <timezone>","IANA timezone").option("--missed <policy>","skip or catch_up").option("--overlap <policy>","allow, forbid, or replace").option("--timeout <seconds>","Execution timeout").action(async(t,p)=>Kr(async()=>li(p.env,async(A)=>{let L=Do(A,t);if(L.origin==="config")throw Error("Config-defined schedules must be edited in cloud.config.ts and reconciled");if(p.missed&&!["skip","catch_up"].includes(p.missed))throw Error("--missed must be skip or catch_up");if(p.overlap&&!["allow","forbid","replace"].includes(p.overlap))throw Error("--overlap must be allow, forbid, or replace");let j=A.store.updateSchedule(L.id,{expression:p.expression,timezone:p.timezone,missedRunPolicy:p.missed,overlapPolicy:p.overlap,timeoutSeconds:p.timeout==null?void 0:Number(p.timeout)});po(A,"updated",{jobId:j.id,expression:j.normalizedExpression,version:j.version},j.resourceId),n(`Updated ${j.name}; next run ${j.nextRunAt??"outside the active window"}`)}))),N.command("jobs:run <job>","Queue a manual run through the durable operation worker").option("--env <environment>","Target environment").action(async(t,p)=>Kr(async()=>li(p.env,async(A)=>{let L=Do(A,t),j=A.service.enqueue(L,{trigger:"manual",actorId:A.actor.id});po(A,"run_queued",{jobId:L.id,executionId:j.id},L.resourceId),n(`Queued execution ${j.id}; inspect it with cloud jobs:history "${L.name}"`)}))),N.command("jobs:dispatch <job>","Dispatch an externally triggered schedule into the durable queue").option("--env <environment>","Target environment").option("--scheduled-for <date>","Provider scheduled timestamp").option("--scheduled","Mark this as a provider schedule trigger").action(async(t,p)=>Kr(async()=>li(p.env,async(A)=>{let L=Do(A,t);if(!L.enabled)throw Error(`${L.name} is disabled`);let j=p.scheduledFor?new Date(p.scheduledFor):new Date;if(!p.scheduledFor)j.setUTCSeconds(0,0);let G=j.toISOString(),Z=A.service.enqueue(L,{trigger:p.scheduled?"scheduled":"external",scheduledFor:G,actorId:A.actor.id}),ee=it(L.normalizedExpression,L.timezone,j,1)[0];A.store.markScheduled(L.id,G,ee),n(`Accepted ${L.name} for ${G} as ${Z.id}`)},t)));for(let t of["enable","disable"])N.command(`jobs:${t} <job>`,`${t==="enable"?"Enable":"Disable"} a scheduled job`).option("--env <environment>","Target environment").option("--confirm <text>",`Production ${t} confirmation`).action(async(p,A)=>Kr(async()=>li(A.env,async(L)=>{let j=Do(L,p);if(t==="disable")Ld(L,t,A.confirm);let G=L.store.setEnabled(j.id,t==="enable");po(L,t==="enable"?"enabled":"disabled",{jobId:G.id},G.resourceId),n(`${G.name} is ${G.enabled?"enabled":"disabled"}`)})));N.command("jobs:delete <job>","Delete a managed schedule").option("--env <environment>","Target environment").option("--confirm <text>","Must be delete in production").action(async(t,p)=>Kr(async()=>li(p.env,async(A)=>{let L=Do(A,t);Ld(A,"delete",p.confirm),A.store.remove(L.id),po(A,"deleted",{jobId:L.id,name:L.name},L.resourceId),n(`Deleted ${L.name}`)}))),N.command("jobs:history <job>","Show execution attempts, output, errors, and redacted logs").option("--env <environment>","Target environment").option("--limit <count>","Maximum executions",{default:"100"}).option("--logs","Print execution logs").option("--json","Print structured JSON").action(async(t,p)=>Kr(async()=>li(p.env,async(A)=>{let L=Do(A,t),j=A.store.listExecutions(L.id,Number(p.limit)||100).map((G)=>({...G,logs:p.logs&&G.operationId?A.queue.logs(G.operationId,{limit:500}):[]}));if(p.json)e(JSON.stringify(j,null,2));else if(c(["ID","Status","Trigger","Scheduled for","Attempt","Started","Finished","Error"],j.map((G)=>[G.id,G.status,G.trigger,G.scheduledFor,String(G.attempt),G.startedAt??"—",G.finishedAt??"—",G.error??"—"])),p.logs)for(let G of j)for(let Z of G.logs)e(`${G.id} ${Z.sequence} ${Z.stream.toUpperCase()} ${Z.message}`)}))),N.command("jobs:reconcile","Import declarative schedules and workers without destructive deletion").option("--env <environment>","Target environment").action(async(t)=>Kr(async()=>li(t.env,async(p)=>{let A=$r(p.store,p.config,{organization:p.controlPlane.organization,project:p.controlPlane.project,environment:p.environmentRecord,resources:p.resources});po(p,"reconciled",A),n(`Reconciled ${A.jobs} schedules and ${A.workers} workers; retained ${A.drifted} missing definitions as drifted`)}))),N.command("workers:list","List configured systemd, ECS, and Lambda workers").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>Kr(async()=>li(t.env,async(p)=>{let A=p.store.listWorkers(p.controlPlane.project.id,p.environmentRecord.id);if(t.json)e(JSON.stringify(A,null,2));else c(["ID","Provider","Name","Queue","Processes","Timeout","Restart","Reconciliation"],A.map((L)=>[L.id,L.provider,L.name,L.queue,String(L.processes),`${L.timeoutSeconds}s`,L.restartPolicy,L.reconciliationStatus]))}))),N.command("workers:restart <worker>","Gracefully restart an allowlisted systemd worker").option("--env <environment>","Target environment").option("--confirm <name>","Exact worker name confirmation").action(async(t,p)=>Kr(async()=>li(p.env,async(A)=>{let L=A.store.listWorkers(A.controlPlane.project.id,A.environmentRecord.id),j=A.store.getWorker(t)??L.find((ee)=>ee.name===t);if(!j||j.environmentId!==A.environmentRecord.id)throw Error(`Worker ${t} was not found in ${A.env}`);if(p.confirm!==j.name)throw Error(`Restart requires --confirm "${j.name}"`);if(j.provider!=="systemd")throw Error(`${j.provider} workers are provider/config managed and do not expose a safe restart adapter`);let G=_n(String(j.target.operationId??""),A.config,{workers:L.map((ee)=>({name:ee.name}))});if(!G||G.danger||G.group!=="worker")throw Error("The allowlisted worker restart operation is unavailable");let Z=await kr(A.config,A.env,G);if(A.store.reconcileWorker(j.id,Z.ok?"in_sync":"unavailable",{...j.observedState,lastRestartAt:new Date().toISOString(),command:Z.command??null,error:Z.error??Z.stderr??null}),po(A,"worker_restarted",{workerId:j.id,ok:Z.ok},j.resourceId),!Z.ok)throw Error(Z.error??Z.stderr??"Worker restart failed");n(`Restarted ${j.name}`)})))}function tl(N){N.command("tunnel","Start a local tunnel to expose your local server").option("--port <number>","Local port to expose",{default:"3000"}).option("--subdomain <name>","Custom subdomain (if available)").option("--host <hostname>","Local hostname",{default:"localhost"}).option("--server <url>","Tunnel server URL",{default:"localtunnel.dev"}).option("--verbose","Enable verbose logging").action(async(t)=>{i("Local Tunnel");let p=Number.parseInt(t.port);if(e(`Local server: ${t.host}:${p}`),e(`Tunnel server: ${t.server}`),t.subdomain)e(`Requested subdomain: ${t.subdomain}`);let A=new o("Connecting to tunnel server...");A.start();try{let L;try{let re=await import("localtunnels");L=re.TunnelClient}catch{A.fail("localtunnels package not found"),e(`
653
+ Response Headers:`),be.headers.forEach((De,Se)=>{e(` ${Se}: ${De}`)}),e(`
654
+ Response Body:`);try{let De=JSON.parse(Ce);console.log(JSON.stringify(De,null,2))}catch{console.log(Ce)}}catch(L){r(`Failed to test API: ${L.message}`),process.exit(1)}})}async function yo(N){let t=await Ne(),m=y(process.cwd(),t),A=new b(m.store,{workerId:`cli:${process.pid}`});try{return await N(A,m.project.id,m.organization.id)}finally{m.store.close()}}function ig(N){if(!N)return;if(!["queued","running","succeeded","failed","cancelled","timed_out"].includes(N))throw Error("State must be queued, running, succeeded, failed, cancelled, or timed_out");return N}function og(N,t){if(!N)return"—";let m=Math.max(0,new Date(t??Date.now()).getTime()-new Date(N).getTime());return m<1000?`${m}ms`:m<60000?`${Math.round(m/1000)}s`:`${Math.round(m/60000)}m`}function sl(N){N.command("ops:list","List durable deployment and mutation jobs").option("--state <state>","Filter queue state").option("--all-projects","Show all locally authorized projects").action(async(t)=>{try{await yo(async(m,A)=>{let L=m.list({projectId:t.allProjects?void 0:A,state:ig(t.state),limit:500});c(["ID","State","Operation","Target","Attempt","Step / blocked","Queued","Duration"],L.map(({operation:j,job:G,approximatePosition:Z})=>[j.id,j.state,j.kind,j.resourceId??j.environmentId??j.projectId??"global",`${j.attempt}/${G.maxAttempts}`,G.blockedReason??G.currentStep??(Z?`${Z.ahead} ahead`:"—"),j.createdAt,og(j.startedAt,j.finishedAt)]))})}catch(m){r(m instanceof Error?m.message:String(m))}}),N.command("ops:show <id>","Show a durable operation and its latest sanitized logs").option("--after <cursor>","Show logs after this sequence",{default:"0"}).action(async(t,m)=>{try{await yo(async(A)=>{let L=A.view(t);if(!L)throw Error(`Operation ${t} was not found`);e(JSON.stringify(L,null,2));for(let j of A.logs(t,{after:Number(m.after)||0,limit:500}))e(`${j.sequence} ${j.stream.toUpperCase()}${j.step?` [${j.step}]`:""} ${j.message}`)})}catch(A){r(A instanceof Error?A.message:String(A))}}),N.command("ops:cancel <id>","Cancel queued work or request running cancellation").option("--yes","Skip confirmation").action(async(t,m)=>{try{await yo(async(A)=>{let L=A.view(t);if(!L)throw Error(`Operation ${t} was not found`);if(!m.yes&&!await d(`Cancel ${L.operation.kind} (${t})?`,!1))return;let j=A.requestCancellation(t);n(`${j.state==="cancelled"?"Cancelled":"Cancellation requested for"} ${t}`)})}catch(A){r(A instanceof Error?A.message:String(A))}}),N.command("ops:retry <id>","Retry a terminal job using an allow-listed error class").option("--class <name>","Retryable error class").option("--delay <milliseconds>","Delay before the next attempt",{default:"0"}).action(async(t,m)=>{try{if(!m.class)throw Error("Pass --class with one of the retry classes shown by ops:show");await yo(async(A)=>{let L=A.retry(t,m.class,{delayMs:Number(m.delay)||0});n(`Retry queued: ${L.id} · attempt ${L.attempt+1}`)})}catch(A){r(A instanceof Error?A.message:String(A))}}),N.command("ops:concurrency","Show effective durable queue concurrency limits").action(async()=>{try{await yo(async(t)=>e(JSON.stringify(t.limits,null,2)))}catch(t){r(t instanceof Error?t.message:String(t))}}),N.command("ops:concurrency:set","Confirm and audit durable queue concurrency limits").option("--project <count>","Concurrent jobs per project").option("--environment <count>","Concurrent jobs per environment").option("--provider <count>","Concurrent jobs per provider").option("--builds <count>","Concurrent build slots").option("--confirm <text>",'Must equal "update queue limits"').action(async(t)=>{try{if(t.confirm!=="update queue limits")throw Error('Pass --confirm "update queue limits" to change production concurrency');let m={};for(let A of["project","environment","provider","builds"])if(t[A]!==void 0)m[A]=Number(t[A]);await yo(async(A,L,j)=>n(`Queue concurrency updated: ${JSON.stringify(A.configureConcurrency(m,{organizationId:j}))}`))}catch(m){r(m instanceof Error?m.message:String(m))}}),N.command("ops:history:clear","Clear terminal jobs after their retention policy expires").option("--before <date>","Also require completion before this timestamp").option("--yes","Skip confirmation").action(async(t)=>{try{await yo(async(m,A)=>{if(!t.yes&&!await d("Clear completed queue history whose retention has elapsed?",!1))return;let L=m.clearCompleted({projectId:A,before:t.before});n(`Cleared ${L} retained operation${L===1?"":"s"}.`)})}catch(m){r(m instanceof Error?m.message:String(m))}})}import{readFile as sg,writeFile as ag}from"node:fs/promises";function cg(N){if(N==null)return"—";if(N<60)return`${N}s`;if(N<3600)return`${Math.floor(N/60)}m`;if(N<86400)return`${Math.floor(N/3600)}h`;return`${Math.floor(N/86400)}d`}function lg(N){return N==null?"—":Xr(N)}function dg(N){return N.map((t)=>[t.id,t.name,t.provider,t.kind,t.status,t.desiredReplicas==null?"—":`${t.runningReplicas??0}/${t.desiredReplicas}`,t.image??t.runtime??"—",lg(t.resources?.memoryBytes??t.resources?.memoryLimitBytes),cg(t.ageSeconds)])}function ug(N,t){let m=t.search?.toLowerCase();return{...N,workloads:N.workloads.filter((A)=>(!t.provider||A.provider===t.provider)&&(!t.status||A.status===t.status)&&(!m||`${A.name} ${A.id} ${A.links.service??""} ${Object.values(A.tags).join(" ")}`.toLowerCase().includes(m)))}}async function vo(N){let t=await Ne(),m=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},m))throw Error(`Environment ${m} was not found`);let A=new ge(t,m);return{config:t,env:m,service:A}}async function ca(N,t){if(t!==void 0)return t;return I(`Type ${N.name} to confirm`,"")}async function pg(N,t,m,A,L,j){return la(N,`runtime.${t}`,{workloadId:N.id,provider:N.provider,action:t,replicas:m??null},L,()=>j.run({workloadId:N.id,action:t,replicas:m,confirm:A,recentAuth:!0}))}async function la(N,t,m,A,L){let j=await Ne(),G=y(process.cwd(),j),Z=new b(G.store,{workerId:`cli:${process.pid}`});try{let ee=G.environments.get(A),ne=G.store.listResources(G.project.id,ee?.id).find((we)=>we.slug===N.links.service),oe=G.store.getActorByExternalId("system","cli")??G.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI",metadata:{source:"cli"}}),be=Z.enqueue({projectId:G.project.id,environmentId:ee?.id,resourceId:ne?.id,actorId:oe.id,kind:t,input:h(m),lockKey:ne?`resource:${ne.id}`:`runtime:${N.id}`,providerKey:N.provider,maxAttempts:1,timeoutSeconds:3600,resumePolicy:"fail",cancellationMode:"provider_non_cancellable",retentionDays:90}).operation;if(!Z.claim(be.id))throw Error(`Workload is locked by another operation. Inspect ${be.id} with cloud ops:show.`);try{let we=await L();if(we.stdout)Z.appendLog(be.id,we.stdout,{stream:"stdout"});if(we.stderr)Z.appendLog(be.id,we.stderr,{stream:"stderr"});if(we.ok)Z.complete(be.id,{ok:!0,command:we.command??null});else Z.fail(be.id,we.error??we.stderr??"Runtime operation failed",{ok:!1});return{result:we,operationId:be.id}}catch(we){throw Z.fail(be.id,we instanceof Error?we.message:String(we),{ok:!1,threw:!0}),we}}finally{G.store.close()}}function al(N){N.command("runtime:list","List services, containers, ECS tasks, and Lambda functions").option("--env <environment>","Target environment").option("--provider <provider>","Filter systemd, docker, ecs, or lambda").option("--status <status>","Filter normalized runtime status").option("--search <query>","Search names, IDs, services, and tags").option("--json","Print normalized JSON").action(async(t)=>{try{let{service:m}=await vo(t.env),A=ug(await m.inventory(),t);if(t.json)e(JSON.stringify(A,null,2));else{c(["ID","Name","Provider","Kind","Status","Replicas","Image / runtime","Memory","Age"],dg(A.workloads));for(let L of A.sources.filter((j)=>j.status!=="fresh"))a(`${L.id}: ${L.status}${L.message?` — ${L.message}`:""}`)}}catch(m){r(m instanceof Error?m.message:String(m)),process.exitCode=1}}),N.command("runtime:inspect <workload>","Inspect normalized config, replicas, networks, mounts, links, and capabilities").option("--env <environment>","Target environment").action(async(t,m)=>{try{let{service:A}=await vo(m.env),L=await A.workload(t);if(!L)throw Error("Workload was not found in this project and environment");e(JSON.stringify(L,null,2))}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("runtime:logs <workload>","Read bounded logs for one authorized runtime target").option("--env <environment>","Target environment").option("--lines <count>","Maximum log lines",{default:"200"}).option("--json","Print structured JSON").action(async(t,m)=>{try{let{service:A}=await vo(m.env),L=await A.logs(t,{limit:Number(m.lines)||200});if(m.json)e(JSON.stringify(L,null,2));else for(let j of L.lines)console.log(`${j.timestamp??"—"} ${j.message}`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("runtime:action <workload> <action>","Start, stop, restart, redeploy, or scale one workload").option("--env <environment>","Target environment").option("--replicas <count>","Desired replicas for scale").option("--confirm <name>","Exact workload name confirmation").action(async(t,m,A)=>{try{let L=m;if(!["start","stop","restart","redeploy","scale"].includes(L))throw Error("Action must be start, stop, restart, redeploy, or scale");let j=A.replicas==null?void 0:Number(A.replicas);if(L==="scale"&&(!Number.isInteger(j)||j<0))throw Error("Scale requires a non-negative integer --replicas value");let{env:G,service:Z}=await vo(A.env),ee=await Z.inventory(),ne=ee.workloads.find((we)=>we.id===t);if(!ne)throw Error("Workload was not found in this project and environment");let oe=new ge(await Ne(),G,{inventory:async()=>ee}),be=await pg(ne,L,j,await ca(ne,A.confirm),G,oe);if(!be.result.ok)throw Error(be.result.error??be.result.stderr??"Runtime operation failed");if(n(`${L} completed for ${ne.name} · operation ${be.operationId}`),be.result.stdout)e(be.result.stdout)}catch(L){r(L instanceof Error?L.message:String(L)),process.exitCode=1}}),N.command("runtime:exec <workload>","Run a scoped diagnostic preset or container command").option("--env <environment>","Target environment").option("--preset <preset>","Read-only preset: process, sockets, or filesystem").option("--command <command>","Free-form command (Docker containers only)").option("--confirm <name>","Exact workload name confirmation").action(async(t,m)=>{try{let{env:A,service:L}=await vo(m.env),G=(await L.inventory()).workloads.find((oe)=>oe.id===t);if(!G)throw Error("Workload was not found in this project and environment");let Z=["process","sockets","filesystem"].includes(String(m.preset))?m.preset:void 0;if(m.preset&&!Z)throw Error("Preset must be process, sockets, or filesystem");if(!Z&&!m.command)throw Error("Pass --preset or --command");let ee=await ca(G,m.confirm),ne=await la(G,"runtime.exec",{workloadId:t,provider:G.provider,preset:Z??null,freeForm:!!m.command},A,()=>L.exec({workloadId:t,preset:Z,command:m.command,confirm:ee,recentAuth:!0}));if(!ne.result.ok)throw Error(ne.result.error??ne.result.stderr??"Exec failed");if(ne.result.stdout)e(ne.result.stdout);if(ne.result.stderr)a(ne.result.stderr);n(`Exec completed for ${G.name} · operation ${ne.operationId}`)}catch(A){r(A instanceof Error?A.message:String(A)),process.exitCode=1}}),N.command("runtime:download <workload> <remote> <local>","Download a bounded file from a service-owned path").option("--env <environment>","Target environment").option("--confirm <name>","Exact workload name confirmation").action(async(t,m,A,L)=>{try{let{env:j,service:G}=await vo(L.env),ee=(await G.inventory()).workloads.find((be)=>be.id===t);if(!ee)throw Error("Workload was not found in this project and environment");let ne=await ca(ee,L.confirm),oe=await la(ee,"runtime.file.read",{workloadId:t,provider:ee.provider,path:m},j,()=>G.readFile({workloadId:t,path:m,confirm:ne,recentAuth:!0}));if(!oe.result.ok||!oe.result.contentBase64)throw Error(oe.result.error??"File download failed");await ag(A,Buffer.from(oe.result.contentBase64,"base64")),n(`Downloaded ${oe.result.size??0} bytes to ${A} · operation ${oe.operationId}${oe.result.truncated?" (truncated)":""}`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}}),N.command("runtime:upload <workload> <local> <remote>","Upload and atomically replace a file in a service-owned path").option("--env <environment>","Target environment").option("--confirm <name>","Exact workload name confirmation").action(async(t,m,A,L)=>{try{let{env:j,service:G}=await vo(L.env),ee=(await G.inventory()).workloads.find((we)=>we.id===t);if(!ee)throw Error("Workload was not found in this project and environment");let ne=await ca(ee,L.confirm),oe=(await sg(m)).toString("base64"),be=await la(ee,"runtime.file.write",{workloadId:t,provider:ee.provider,path:A,bytes:Buffer.from(oe,"base64").byteLength},j,()=>G.writeFile({workloadId:t,path:A,contentBase64:oe,confirm:ne,recentAuth:!0}));if(!be.result.ok)throw Error(be.result.error??"File upload failed");n(`Uploaded ${be.result.size??0} bytes to ${A} · operation ${be.operationId}`)}catch(j){r(j instanceof Error?j.message:String(j)),process.exitCode=1}})}function mg(N,t=new Date){let m=N.to?new Date(N.to):t;if(!Number.isFinite(m.getTime()))throw Error("--to must be a valid ISO-8601 instant");if(N.from){let j=new Date(N.from);if(!Number.isFinite(j.getTime()))throw Error("--from must be a valid ISO-8601 instant");return{from:j.toISOString(),to:m.toISOString()}}let A=String(N.range??"1h").match(/^(\d+)(m|h|d)$/);if(!A)throw Error("--range must use minutes, hours, or days (for example 30m, 6h, or 7d)");let L=A[2]==="m"?60000:A[2]==="h"?3600000:86400000;return{from:new Date(m.getTime()-Number(A[1])*L).toISOString(),to:m.toISOString()}}async function vs(N){let t=await Ne(),m=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},m))throw Error(`Environment ${m} was not found`);let A=y(process.cwd(),t),L=A.environments.get(m),j=new Ge(A.store);return{config:t,env:m,controlPlane:A,environmentRecord:L,telemetry:j}}function Vd(N,t,m){let A=(j)=>j?.split(",").map((G)=>G.trim()).filter(Boolean)||void 0,L=A(N.kind)?.filter((j)=>["metric","log","trace","request","event"].includes(j));return{projectId:t,environmentId:m,...mg(N),kinds:L,names:A(N.name),sources:A(N.source),text:N.text,limit:Math.min(5000,Math.max(1,Number(N.limit)||200))}}function fg(N){let t=["timestamp","kind","source","name","level","value","unit","message","traceId","requestId","deploymentId","releaseId","workloadId"];return[t.join(","),...N.map((m)=>t.map((A)=>`"${String(m[A]??"").replaceAll('"','""')}"`).join(","))].join(`
655
+ `)}function cl(N){N.command("telemetry:collect","Collect normalized host/runtime or AWS telemetry into the local control plane").option("--env <environment>","Target environment").option("--force","Bypass the provider-query cache").option("--json","Print structured JSON").action(async(t)=>{let m=await vs(t.env);try{let A=await jn({controlPlane:m.controlPlane.store,projectId:m.controlPlane.project.id,environmentId:m.environmentRecord?.id,config:m.config,environment:m.env,force:t.force});if(t.json)e(JSON.stringify(A,null,2));else n(`Collected ${A.collected} normalized records${A.cached?" (cached provider result)":""}.`),c(["Source","Freshness","Lag","Sampling","Retention"],A.statuses.map((L)=>[L.source,L.freshness,L.lagSeconds==null?"—":`${L.lagSeconds}s`,`${Math.round(L.samplingRate*100)}%`,`${L.retentionDays}d`]))}finally{m.controlPlane.store.close()}}),N.command("telemetry:query","Query bounded normalized metrics, logs, traces, requests, and events").option("--env <environment>","Target environment").option("--from <instant>","ISO-8601 start").option("--to <instant>","ISO-8601 end").option("--range <duration>","Relative window such as 1h or 7d",{default:"1h"}).option("--kind <kinds>","Comma-separated kinds").option("--name <names>","Comma-separated signal names").option("--source <sources>","Comma-separated sources").option("--text <query>","Search name/message").option("--limit <count>","Maximum records",{default:"200"}).option("--json","Print structured JSON").action(async(t)=>{let m=await vs(t.env);try{let A=m.telemetry.query(Vd(t,m.controlPlane.project.id,m.environmentRecord?.id));if(t.json)e(JSON.stringify(A,null,2));else c(["Time","Kind","Source","Signal","Value / message","Correlation"],A.records.map((L)=>[L.timestamp,L.kind,L.source,L.name,L.message??(L.value==null?"—":`${L.value} ${L.unit??""}`),L.requestId??L.traceId??L.releaseId??L.deploymentId??"—"]));if(A.truncated)a("More records are available; narrow the window or use the dashboard cursor explorer.")}finally{m.controlPlane.store.close()}}),N.command("telemetry:export <file>","Export a bounded telemetry query as JSON or CSV").option("--env <environment>","Target environment").option("--from <instant>","ISO-8601 start").option("--to <instant>","ISO-8601 end").option("--range <duration>","Relative window",{default:"1h"}).option("--kind <kinds>","Comma-separated kinds").option("--name <names>","Comma-separated signal names").option("--source <sources>","Comma-separated sources").option("--text <query>","Search name/message").option("--limit <count>","Maximum records",{default:"5000"}).action(async(t,m)=>{let A=await vs(m.env);try{let L=Vd(m,A.controlPlane.project.id,A.environmentRecord?.id),j=A.telemetry.query(L);await Bun.write(t,t.toLowerCase().endsWith(".csv")?fg(j.records):JSON.stringify({exportedAt:new Date().toISOString(),query:L,...j},null,2)),n(`Exported ${j.records.length} records to ${t}${j.truncated?" (bounded result)":""}.`)}finally{A.controlPlane.store.close()}}),N.command("telemetry:compact","Downsample old metrics and enforce configured retention").option("--env <environment>","Target environment").action(async(t)=>{let m=await vs(t.env);try{let A=We(m.controlPlane.store,m.controlPlane.project.id);e(JSON.stringify(m.telemetry.enforceRetention(A,m.controlPlane.project.id),null,2))}finally{m.controlPlane.store.close()}}),N.command("telemetry:policy","Show or update sampling, retention, and local storage cost assumptions").option("--env <environment>","Target environment").option("--raw-days <days>","Raw retention days").option("--downsample-days <days>","Downsample after days").option("--sampling <rate>","Sampling rate from 0.01 to 1").option("--storage-usd-gb-month <amount>","Local storage cost assumption").option("--json","Print structured JSON").action(async(t)=>{let m=await vs(t.env);try{let A={};if(t.rawDays!=null)A.rawDays=Number(t.rawDays);if(t.downsampleDays!=null)A.downsampleAfterDays=Number(t.downsampleDays);if(t.sampling!=null)A.samplingRate=Number(t.sampling);if(t.storageUsdGbMonth!=null)A.estimatedStorageUsdPerGbMonth=Number(t.storageUsdGbMonth);let L=Object.keys(A).length?Xn(m.controlPlane.store,m.controlPlane.project.id,A):We(m.controlPlane.store,m.controlPlane.project.id),G=m.telemetry.status(m.controlPlane.project.id,m.environmentRecord?.id,L.rawDays,L.samplingRate).reduce((ee,ne)=>ee+ne.estimatedDailyBytes*30,0),Z={policy:L,estimatedMonthlyBytes:G,estimatedMonthlyCostUsd:Qn(G,L),costEstimateConfigured:L.estimatedStorageUsdPerGbMonth>0};if(t.json)e(JSON.stringify(Z,null,2));else e(JSON.stringify(L,null,2)),e(Z.costEstimateConfigured?`Estimated local storage: $${Z.estimatedMonthlyCostUsd}/month`:"Storage cost remains unavailable until --storage-usd-gb-month is set.")}finally{m.controlPlane.store.close()}})}async function co(N){let t=await Ne(),m=N??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},m))throw Error(`Environment ${m} was not found`);let A=y(process.cwd(),t),L=A.environments.get(m),j=new pt(A.store,{encryptionKey:k(process.cwd())});return{config:t,env:m,controlPlane:A,environmentRecord:L,alerts:j}}function Wd(N){return new dr(N,{emailImpl:async(t)=>{let{email:m}=await import("./chunk-zww0xfdp.js");return m.send(t)}})}function ll(N){N.command("health:list","List configured health checks and latest results").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{let m=await co(t.env);try{let A=m.alerts.listHealthChecks(m.controlPlane.project.id,m.environmentRecord?.id).map((L)=>({...L,latest:m.alerts.listHealthResults(L.id,1)[0]}));if(t.json)e(JSON.stringify(A,null,2));else c(["ID","Name","Kind","Target","Status","Interval","Enabled"],A.map((L)=>[L.id,L.name,L.kind,L.target,L.latest?.status??"no data",`${L.intervalSeconds}s`,L.enabled?"yes":"no"]))}finally{m.controlPlane.store.close()}}),N.command("health:run <check>","Run one health check and evaluate linked alert rules").option("--env <environment>","Target environment").option("--test","Preview without persisting results or incidents").action(async(t,m)=>{let A=await co(m.env);try{let L=A.alerts.getHealthCheck(t)??A.alerts.listHealthChecks(A.controlPlane.project.id,A.environmentRecord?.id).find((Z)=>Z.name===t);if(!L)throw Error("Health check was not found");let j=new Oe(A.alerts);if(m.test){let Z=await j.probe(L);e(JSON.stringify({...Z,productionIncidentCreated:!1},null,2));return}let G=await j.runAndEvaluate(L);if(e(JSON.stringify(G,null,2)),G.result.status==="healthy")n(`${L.name} is healthy.`);else a(`${L.name} is ${G.result.status}.`)}finally{A.controlPlane.store.close()}}),N.command("alerts:list","List pending, firing, silenced, and resolved alerts").option("--env <environment>","Target environment").option("--state <states>","Comma-separated states").option("--json","Print structured JSON").action(async(t)=>{let m=await co(t.env);try{let A=m.alerts.listAlerts(m.controlPlane.project.id,{environmentId:m.environmentRecord?.id,states:t.state?.split(",").filter(Boolean)});if(t.json)e(JSON.stringify(A,null,2));else c(["ID","State","Severity","Title","Owner","First seen","Last seen"],A.map((L)=>[L.id,L.state,L.severity,L.title,L.ownerActorId??"unassigned",L.firstSeenAt,L.lastSeenAt]))}finally{m.controlPlane.store.close()}}),N.command("alerts:evaluate","Evaluate persisted telemetry rules now").option("--env <environment>","Target environment").action(async(t)=>{let m=await co(t.env);try{let A=Wr(m.alerts,m.controlPlane.project.id,m.environmentRecord?.id);e(JSON.stringify(A,null,2))}finally{m.controlPlane.store.close()}}),N.command("alerts:ack <alert>","Acknowledge an alert as the CLI actor").option("--env <environment>","Target environment").action(async(t,m)=>{let A=await co(m.env);try{let L=A.controlPlane.store.getActorByExternalId("system","cli")??A.controlPlane.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});n(`Acknowledged ${A.alerts.acknowledge(t,L.id).title}.`)}finally{A.controlPlane.store.close()}}),N.command("notifications:list","List notification channels, routes, and recent delivery diagnostics").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>{let m=await co(t.env);try{let A={channels:m.alerts.listChannels(m.controlPlane.organization.id),routes:m.alerts.listRoutes(m.controlPlane.organization.id),deliveries:m.alerts.listDeliveries({limit:100})};if(t.json)e(JSON.stringify(A,null,2));else c(["Channel","Type","Status","Tested","Error"],A.channels.map((L)=>[L.name,L.kind,L.status,L.lastTestedAt??"never",L.lastError??"—"])),c(["Event","State","Attempts","Status","Error"],A.deliveries.map((L)=>[L.eventType,L.state,`${L.attempt}/${L.maxAttempts}`,String(L.responseStatus??"—"),L.error??"—"]))}finally{m.controlPlane.store.close()}}),N.command("notifications:test <channel>","Send a channel test without creating a production incident").option("--env <environment>","Target environment").action(async(t,m)=>{let A=await co(m.env);try{let L=A.alerts.getChannel(t)??A.alerts.listChannels(A.controlPlane.organization.id).find((G)=>G.name===t);if(!L)throw Error("Notification channel was not found");let j=await Wd(A.alerts).testChannel(L.id);if(j.ok)n("Test delivered; no production incident was created.");else throw Error(j.error)}finally{A.controlPlane.store.close()}}),N.command("notifications:retry <delivery>","Retry one failed notification delivery").option("--env <environment>","Target environment").action(async(t,m)=>{let A=await co(m.env);try{let L=A.alerts.getDelivery(t);if(!L)throw Error("Notification delivery was not found");A.alerts.updateDelivery(t,{state:"retrying",attempt:Math.max(0,L.attempt-1),nextAttemptAt:new Date().toISOString()}),e(JSON.stringify(await Wd(A.alerts).deliver(t),null,2))}finally{A.controlPlane.store.close()}})}async function gg(N,t){let m=await Ne(),A=y(process.cwd(),m),L=!N&&t?new $r(A.store).get(t):void 0,j=L?.environmentId?A.store.listEnvironments(A.project.id).find((Ce)=>Ce.id===L.environmentId)?.slug:void 0,G=N??j??Object.keys(m.environments??{})[0]??"production";if(!Object.hasOwn(m.environments??{},G))throw A.store.close(),Error(`Environment ${G} was not found`);let Z=A.environments.get(G);if(!Z)throw A.store.close(),Error(`Environment ${G} was not found in the control plane`);let ee=new $r(A.store),ne=new b(A.store,{workerId:`cli:${process.pid}`}),oe=new Mn(ee,{queue:ne}),be=A.store.listResources(A.project.id,Z.id);Or(ee,m,{organization:A.organization,project:A.project,environment:Z,resources:be});let we=A.store.getActorByExternalId("system","cli")??A.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:m,env:G,controlPlane:A,environmentRecord:Z,resources:be,store:ee,queue:ne,service:oe,actor:we}}async function mi(N,t,m){let A=await gg(N,m);try{return await t(A)}finally{A.controlPlane.store.close()}}async function Zr(N){try{await N()}catch(t){r(t instanceof Error?t.message:String(t))}}function Lo(N,t){let m=N.store.list(N.controlPlane.project.id,{environmentId:N.environmentRecord.id}),A=N.store.get(t)??m.find((L)=>L.name===t);if(!A||A.environmentId!==N.environmentRecord.id)throw Error(`Scheduled job ${t} was not found in ${N.env}`);return A}function bo(N,t,m,A){N.controlPlane.store.appendEvent({organizationId:N.controlPlane.organization.id,projectId:N.controlPlane.project.id,resourceId:A,actorId:N.actor.id,type:`job.${t}`,payload:m})}function qd(N,t,m){if(N.environmentRecord.kind==="production"&&m!==t)throw Error(`Production ${t} requires --confirm ${t}`)}function hg(N){if(!N)return{};if(!/^(?:secret|ssm|arn|env):\/\//.test(N))throw Error("Payloads must use a secret://, ssm://, arn://, or env:// reference");return{input:N}}function yg(N){return N.map((t)=>[t.id,t.enabled?"enabled":"disabled",t.provider,t.name,t.expression,t.timezone,t.nextRunAt??"—",t.reconciliationStatus])}function dl(N){N.command("jobs:list","List server cron, EventBridge, and recurring control-plane jobs").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>Zr(async()=>mi(t.env,async(m)=>{let A=m.store.list(m.controlPlane.project.id,{environmentId:m.environmentRecord.id});if(t.json)e(JSON.stringify(A,null,2));else c(["ID","State","Provider","Name","Expression","Timezone","Next run","Reconciliation"],yg(A))}))),N.command("jobs:preview <expression>","Preview the next runs without creating production work").option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--from <date>","Preview from an ISO timestamp").option("--count <count>","Number of runs",{default:"5"}).action(async(t,m)=>Zr(async()=>{let A=ot(t,m.timezone??"UTC",m.from?new Date(m.from):new Date,Number(m.count)||5);e(JSON.stringify({...A,productionExecutionCreated:!1},null,2))})),N.command("jobs:add <name> <expression>","Create a managed recurring job").option("--env <environment>","Target environment").option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--operation <id>","Safe server operation target (scheduler, backup, or worker)").option("--resource <id>","Resource ID or slug").option("--starts-at <date>","Schedule window start").option("--ends-at <date>","Schedule window end").option("--flexible-minutes <count>","Provider flexible window",{default:"0"}).option("--missed <policy>","skip or catch_up",{default:"skip"}).option("--overlap <policy>","allow, forbid, or replace",{default:"forbid"}).option("--attempts <count>","Maximum attempts",{default:"3"}).option("--backoff <seconds>","Retry backoff seconds",{default:"30"}).option("--timeout <seconds>","Execution timeout",{default:"900"}).option("--dead-letter <reference>","Dead-letter ARN or secret reference").option("--payload-ref <reference>","Referenced payload; inline values are forbidden").option("--disabled","Create disabled").action(async(t,m,A)=>Zr(async()=>mi(A.env,async(L)=>{if(!["skip","catch_up"].includes(A.missed??"skip"))throw Error("--missed must be skip or catch_up");if(!["allow","forbid","replace"].includes(A.overlap??"forbid"))throw Error("--overlap must be allow, forbid, or replace");let j=V(L.config),Z={workers:L.store.listWorkers(L.controlPlane.project.id,L.environmentRecord.id).map((Ce)=>({name:Ce.name}))},ee;if(j==="server"){let Ce=kr(L.config,Z).filter((Se)=>!Se.danger&&["scheduler","backup","worker"].includes(Se.group)),De=A.operation?Ce.find((Se)=>Se.id===A.operation):Ce.length===1?Ce[0]:void 0;if(!De)throw Error(`Choose a safe target with --operation. Available: ${Ce.map((Se)=>Se.id).join(", ")||"none"}`);ee={kind:"dashboard_operation",operationId:De.id}}else ee={kind:"serverless_scheduler",action:"run"};let ne=j==="server"?"server":"eventbridge",oe=Ln({provider:ne,expression:m,flexibleMinutes:Number(A.flexibleMinutes)||0,missedRunPolicy:A.missed??"skip",overlapPolicy:A.overlap??"forbid"});if(!oe.supported)throw Error(oe.notes.join(" "));let be=A.resource?L.resources.find((Ce)=>Ce.id===A.resource||Ce.slug===A.resource):void 0;if(A.resource&&!be)throw Error(`Resource ${A.resource} was not found in ${L.env}`);let we=L.store.create({organizationId:L.controlPlane.organization.id,projectId:L.controlPlane.project.id,environmentId:L.environmentRecord.id,resourceId:be?.id,name:t,provider:ne,expression:m,timezone:A.timezone??"UTC",startsAt:A.startsAt,endsAt:A.endsAt,flexibleMinutes:Number(A.flexibleMinutes)||0,target:ee,payloadRefs:hg(A.payloadRef),missedRunPolicy:A.missed??"skip",overlapPolicy:A.overlap??"forbid",retryPolicy:{maxAttempts:Number(A.attempts)||3,backoffSeconds:Number(A.backoff)||30,deadLetterRef:A.deadLetter},timeoutSeconds:Number(A.timeout)||900,enabled:!A.disabled,origin:"managed",ownerActorId:L.actor.id,observedState:{},reconciliationStatus:"pending"});bo(L,"created",{jobId:we.id,provider:we.provider,expression:we.normalizedExpression},we.resourceId),n(`Created ${we.name} (${we.id}); next run ${we.nextRunAt??"outside the active window"}`)}))),N.command("jobs:update <job>","Update a managed job schedule and policies").option("--env <environment>","Target environment").option("--expression <expression>","Cron, preset, or rate expression").option("--timezone <timezone>","IANA timezone").option("--missed <policy>","skip or catch_up").option("--overlap <policy>","allow, forbid, or replace").option("--timeout <seconds>","Execution timeout").action(async(t,m)=>Zr(async()=>mi(m.env,async(A)=>{let L=Lo(A,t);if(L.origin==="config")throw Error("Config-defined schedules must be edited in cloud.config.ts and reconciled");if(m.missed&&!["skip","catch_up"].includes(m.missed))throw Error("--missed must be skip or catch_up");if(m.overlap&&!["allow","forbid","replace"].includes(m.overlap))throw Error("--overlap must be allow, forbid, or replace");let j=A.store.updateSchedule(L.id,{expression:m.expression,timezone:m.timezone,missedRunPolicy:m.missed,overlapPolicy:m.overlap,timeoutSeconds:m.timeout==null?void 0:Number(m.timeout)});bo(A,"updated",{jobId:j.id,expression:j.normalizedExpression,version:j.version},j.resourceId),n(`Updated ${j.name}; next run ${j.nextRunAt??"outside the active window"}`)}))),N.command("jobs:run <job>","Queue a manual run through the durable operation worker").option("--env <environment>","Target environment").action(async(t,m)=>Zr(async()=>mi(m.env,async(A)=>{let L=Lo(A,t),j=A.service.enqueue(L,{trigger:"manual",actorId:A.actor.id});bo(A,"run_queued",{jobId:L.id,executionId:j.id},L.resourceId),n(`Queued execution ${j.id}; inspect it with cloud jobs:history "${L.name}"`)}))),N.command("jobs:dispatch <job>","Dispatch an externally triggered schedule into the durable queue").option("--env <environment>","Target environment").option("--scheduled-for <date>","Provider scheduled timestamp").option("--scheduled","Mark this as a provider schedule trigger").action(async(t,m)=>Zr(async()=>mi(m.env,async(A)=>{let L=Lo(A,t);if(!L.enabled)throw Error(`${L.name} is disabled`);let j=m.scheduledFor?new Date(m.scheduledFor):new Date;if(!m.scheduledFor)j.setUTCSeconds(0,0);let G=j.toISOString(),Z=A.service.enqueue(L,{trigger:m.scheduled?"scheduled":"external",scheduledFor:G,actorId:A.actor.id}),ee=it(L.normalizedExpression,L.timezone,j,1)[0];A.store.markScheduled(L.id,G,ee),n(`Accepted ${L.name} for ${G} as ${Z.id}`)},t)));for(let t of["enable","disable"])N.command(`jobs:${t} <job>`,`${t==="enable"?"Enable":"Disable"} a scheduled job`).option("--env <environment>","Target environment").option("--confirm <text>",`Production ${t} confirmation`).action(async(m,A)=>Zr(async()=>mi(A.env,async(L)=>{let j=Lo(L,m);if(t==="disable")qd(L,t,A.confirm);let G=L.store.setEnabled(j.id,t==="enable");bo(L,t==="enable"?"enabled":"disabled",{jobId:G.id},G.resourceId),n(`${G.name} is ${G.enabled?"enabled":"disabled"}`)})));N.command("jobs:delete <job>","Delete a managed schedule").option("--env <environment>","Target environment").option("--confirm <text>","Must be delete in production").action(async(t,m)=>Zr(async()=>mi(m.env,async(A)=>{let L=Lo(A,t);qd(A,"delete",m.confirm),A.store.remove(L.id),bo(A,"deleted",{jobId:L.id,name:L.name},L.resourceId),n(`Deleted ${L.name}`)}))),N.command("jobs:history <job>","Show execution attempts, output, errors, and redacted logs").option("--env <environment>","Target environment").option("--limit <count>","Maximum executions",{default:"100"}).option("--logs","Print execution logs").option("--json","Print structured JSON").action(async(t,m)=>Zr(async()=>mi(m.env,async(A)=>{let L=Lo(A,t),j=A.store.listExecutions(L.id,Number(m.limit)||100).map((G)=>({...G,logs:m.logs&&G.operationId?A.queue.logs(G.operationId,{limit:500}):[]}));if(m.json)e(JSON.stringify(j,null,2));else if(c(["ID","Status","Trigger","Scheduled for","Attempt","Started","Finished","Error"],j.map((G)=>[G.id,G.status,G.trigger,G.scheduledFor,String(G.attempt),G.startedAt??"—",G.finishedAt??"—",G.error??"—"])),m.logs)for(let G of j)for(let Z of G.logs)e(`${G.id} ${Z.sequence} ${Z.stream.toUpperCase()} ${Z.message}`)}))),N.command("jobs:reconcile","Import declarative schedules and workers without destructive deletion").option("--env <environment>","Target environment").action(async(t)=>Zr(async()=>mi(t.env,async(m)=>{let A=Or(m.store,m.config,{organization:m.controlPlane.organization,project:m.controlPlane.project,environment:m.environmentRecord,resources:m.resources});bo(m,"reconciled",A),n(`Reconciled ${A.jobs} schedules and ${A.workers} workers; retained ${A.drifted} missing definitions as drifted`)}))),N.command("workers:list","List configured systemd, ECS, and Lambda workers").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(t)=>Zr(async()=>mi(t.env,async(m)=>{let A=m.store.listWorkers(m.controlPlane.project.id,m.environmentRecord.id);if(t.json)e(JSON.stringify(A,null,2));else c(["ID","Provider","Name","Queue","Processes","Timeout","Restart","Reconciliation"],A.map((L)=>[L.id,L.provider,L.name,L.queue,String(L.processes),`${L.timeoutSeconds}s`,L.restartPolicy,L.reconciliationStatus]))}))),N.command("workers:restart <worker>","Gracefully restart an allowlisted systemd worker").option("--env <environment>","Target environment").option("--confirm <name>","Exact worker name confirmation").action(async(t,m)=>Zr(async()=>mi(m.env,async(A)=>{let L=A.store.listWorkers(A.controlPlane.project.id,A.environmentRecord.id),j=A.store.getWorker(t)??L.find((ee)=>ee.name===t);if(!j||j.environmentId!==A.environmentRecord.id)throw Error(`Worker ${t} was not found in ${A.env}`);if(m.confirm!==j.name)throw Error(`Restart requires --confirm "${j.name}"`);if(j.provider!=="systemd")throw Error(`${j.provider} workers are provider/config managed and do not expose a safe restart adapter`);let G=$n(String(j.target.operationId??""),A.config,{workers:L.map((ee)=>({name:ee.name}))});if(!G||G.danger||G.group!=="worker")throw Error("The allowlisted worker restart operation is unavailable");let Z=await _r(A.config,A.env,G);if(A.store.reconcileWorker(j.id,Z.ok?"in_sync":"unavailable",{...j.observedState,lastRestartAt:new Date().toISOString(),command:Z.command??null,error:Z.error??Z.stderr??null}),bo(A,"worker_restarted",{workerId:j.id,ok:Z.ok},j.resourceId),!Z.ok)throw Error(Z.error??Z.stderr??"Worker restart failed");n(`Restarted ${j.name}`)})))}function ul(N){N.command("tunnel","Start a local tunnel to expose your local server").option("--port <number>","Local port to expose",{default:"3000"}).option("--subdomain <name>","Custom subdomain (if available)").option("--host <hostname>","Local hostname",{default:"localhost"}).option("--server <url>","Tunnel server URL",{default:"localtunnel.dev"}).option("--verbose","Enable verbose logging").action(async(t)=>{i("Local Tunnel");let m=Number.parseInt(t.port);if(e(`Local server: ${t.host}:${m}`),e(`Tunnel server: ${t.server}`),t.subdomain)e(`Requested subdomain: ${t.subdomain}`);let A=new o("Connecting to tunnel server...");A.start();try{let L;try{let ne=await import("localtunnels");L=ne.TunnelClient}catch{A.fail("localtunnels package not found"),e(`
656
656
  To use tunnels, install the localtunnels package:`),e(" bun add localtunnels"),e(`
657
- Or use the standalone CLI:`),e(" bunx localtunnels --port 3000");return}let j=t.server.replace(/^(wss?|https?):\/\//,""),G=t.server.startsWith("wss://")||t.server.startsWith("https://")||j==="localtunnel.dev",Z=new L({localPort:p,localHost:t.host,subdomain:t.subdomain,host:j,port:G?443:80,secure:G,verbose:t.verbose});Z.on("connected",(re)=>{A.succeed("Connected!"),n(`
658
- Your tunnel URL: ${re.url}`),e(`Subdomain: ${re.subdomain}`),e(`
659
- Forwarding: ${re.url} -> http://${t.host}:${p}`),e(`
660
- Press Ctrl+C to stop the tunnel`)}),Z.on("request",(re)=>{if(t.verbose)e(`→ ${re.method} ${re.url}`)}),Z.on("response",(re)=>{if(t.verbose)e(`← ${re.status} (${re.size} bytes${re.duration?`, ${re.duration}ms`:""})`)}),Z.on("reconnecting",(re)=>{e(`Reconnecting... (attempt ${re.attempt}/${re.maxAttempts})`)}),Z.on("error",(re)=>{r(`Tunnel error: ${re.message}`)}),Z.on("close",()=>{e(`
657
+ Or use the standalone CLI:`),e(" bunx localtunnels --port 3000");return}let j=t.server.replace(/^(wss?|https?):\/\//,""),G=t.server.startsWith("wss://")||t.server.startsWith("https://")||j==="localtunnel.dev",Z=new L({localPort:m,localHost:t.host,subdomain:t.subdomain,host:j,port:G?443:80,secure:G,verbose:t.verbose});Z.on("connected",(ne)=>{A.succeed("Connected!"),n(`
658
+ Your tunnel URL: ${ne.url}`),e(`Subdomain: ${ne.subdomain}`),e(`
659
+ Forwarding: ${ne.url} -> http://${t.host}:${m}`),e(`
660
+ Press Ctrl+C to stop the tunnel`)}),Z.on("request",(ne)=>{if(t.verbose)e(`→ ${ne.method} ${ne.url}`)}),Z.on("response",(ne)=>{if(t.verbose)e(`← ${ne.status} (${ne.size} bytes${ne.duration?`, ${ne.duration}ms`:""})`)}),Z.on("reconnecting",(ne)=>{e(`Reconnecting... (attempt ${ne.attempt}/${ne.maxAttempts})`)}),Z.on("error",(ne)=>{r(`Tunnel error: ${ne.message}`)}),Z.on("close",()=>{e(`
661
661
  Tunnel closed`)});let ee=()=>{e(`
662
- Shutting down tunnel...`),Z.disconnect(),process.exit(0)};process.on("SIGINT",ee),process.on("SIGTERM",ee),await Z.connect(),await new Promise(()=>{})}catch(L){A.fail("Failed to connect"),r(`Error: ${L.message}`),process.exit(1)}}),N.command("tunnel:status","Check tunnel server status").option("--server <url>","Tunnel server URL",{default:"https://localtunnel.dev"}).action(async(t)=>{i("Tunnel Server Status");let p=new o("Checking server status...");p.start();try{let A=t.server.startsWith("http")?t.server:`https://${t.server}`,L=await fetch(`${A}/status`,{method:"GET",headers:{Accept:"application/json"}});if(L.ok){let j=await L.json();if(p.succeed("Server is online"),e(`
663
- Server: ${t.server}`),e(`Status: ${j.status||"operational"}`),j.version)e(`Version: ${j.version}`);if(j.connections!==void 0)e(`Active connections: ${j.connections}`);if(j.uptime)e(`Uptime: ${j.uptime}`);if(j.activeSubdomains?.length)e(`Active subdomains: ${j.activeSubdomains.join(", ")}`)}else p.fail(`Server returned status ${L.status}`)}catch(A){p.fail("Failed to check server status"),r(`Error: ${A.message}`),e(`
662
+ Shutting down tunnel...`),Z.disconnect(),process.exit(0)};process.on("SIGINT",ee),process.on("SIGTERM",ee),await Z.connect(),await new Promise(()=>{})}catch(L){A.fail("Failed to connect"),r(`Error: ${L.message}`),process.exit(1)}}),N.command("tunnel:status","Check tunnel server status").option("--server <url>","Tunnel server URL",{default:"https://localtunnel.dev"}).action(async(t)=>{i("Tunnel Server Status");let m=new o("Checking server status...");m.start();try{let A=t.server.startsWith("http")?t.server:`https://${t.server}`,L=await fetch(`${A}/status`,{method:"GET",headers:{Accept:"application/json"}});if(L.ok){let j=await L.json();if(m.succeed("Server is online"),e(`
663
+ Server: ${t.server}`),e(`Status: ${j.status||"operational"}`),j.version)e(`Version: ${j.version}`);if(j.connections!==void 0)e(`Active connections: ${j.connections}`);if(j.uptime)e(`Uptime: ${j.uptime}`);if(j.activeSubdomains?.length)e(`Active subdomains: ${j.activeSubdomains.join(", ")}`)}else m.fail(`Server returned status ${L.status}`)}catch(A){m.fail("Failed to check server status"),r(`Error: ${A.message}`),e(`
664
664
  The tunnel server may be offline or unreachable.`)}}),N.command("tunnel:info","Show tunnel configuration and setup info").action(async()=>{i("Local Tunnel Information"),e("ts-cloud uses localtunnels for secure tunnel connections."),e(""),e("Default server: localtunnel.dev"),e(""),e("Usage:"),e(" cloud tunnel --port 3000 # Expose port 3000"),e(" cloud tunnel --port 8080 --subdomain myapp"),e(""),e("Features:"),e(" - Secure WebSocket-based tunnels"),e(" - Custom subdomains (when available)"),e(" - Automatic reconnection"),e(" - Request logging"),e(" - Binary data support"),e(""),e("Self-hosted tunnel server:"),e(" You can run your own tunnel server using localtunnels."),e(" See: https://github.com/stacksjs/localtunnels"),e(""),e("Environment variables:"),e(" TUNNEL_SERVER - Custom tunnel server URL"),e(" TUNNEL_SUBDOMAIN - Default subdomain to request")}),N.command("tunnel:deploy","Deploy tunnel infrastructure to AWS").option("--region <region>","AWS region",{default:"us-east-1"}).option("--prefix <prefix>","Resource name prefix",{default:"localtunnel"}).option("--verbose","Enable verbose logging").action(async(t)=>{if(i("Deploy Tunnel Infrastructure"),e(`Region: ${t.region}`),e(`Prefix: ${t.prefix}`),e(""),e("This will deploy:"),e(" - DynamoDB tables for connection tracking"),e(" - Lambda functions for handling requests"),e(" - Lambda Function URLs for public access"),e(""),!await d("Deploy tunnel infrastructure?",!1)){e("Operation cancelled");return}let A=new o("Deploying infrastructure...");A.start();try{let L;try{let G=await import("localtunnels/cloud");L=G.deployTunnelInfrastructure}catch{A.fail("localtunnels package not found"),e(`
665
665
  To deploy tunnel infrastructure, install localtunnels:`),e(" bun add localtunnels"),e(`
666
666
  Or deploy using the localtunnels CLI directly:`),e(" bunx localtunnels deploy --region us-east-1");return}let j=await L({region:t.region,prefix:t.prefix,verbose:t.verbose});if(A.succeed("Deployment complete!"),e(""),e("Resources created:"),e(" DynamoDB Tables:"),e(` - ${j.connectionsTable}`),e(` - ${j.responsesTable}`),e(""),e(" Lambda Functions:"),e(` - ${j.functions.http}`),e(` - ${j.functions.message}`),e(""),j.httpUrl||j.wsUrl){if(e("Endpoints:"),j.httpUrl)e(` HTTP URL: ${j.httpUrl}`);if(j.wsUrl)e(` WebSocket URL: ${j.wsUrl}`)}}catch(L){if(A.fail("Deployment failed"),r(`Error: ${L.message}`),t.verbose)console.error(L.stack);process.exit(1)}}),N.command("tunnel:destroy","Destroy tunnel infrastructure from AWS").option("--region <region>","AWS region",{default:"us-east-1"}).option("--prefix <prefix>","Resource name prefix",{default:"localtunnel"}).option("--verbose","Enable verbose logging").action(async(t)=>{if(i("Destroy Tunnel Infrastructure"),e(`Region: ${t.region}`),e(`Prefix: ${t.prefix}`),e(""),a("This will permanently delete:"),a(" - All DynamoDB tables and data"),a(" - All Lambda functions"),a(" - All IAM roles and policies"),e(""),!await d("Are you sure you want to destroy this infrastructure?",!1)){e("Operation cancelled");return}let A=new o("Destroying infrastructure...");A.start();try{let L;try{let j=await import("localtunnels/cloud");L=j.destroyTunnelInfrastructure}catch{A.fail("localtunnels package not found"),e(`
667
667
  To destroy tunnel infrastructure, install localtunnels:`),e(" bun add localtunnels"),e(`
668
- Or destroy using the localtunnels CLI directly:`),e(" bunx localtunnels destroy --region us-east-1");return}await L({region:t.region,prefix:t.prefix,verbose:t.verbose}),A.succeed("Infrastructure destroyed!")}catch(L){if(A.fail("Destruction failed"),r(`Error: ${L.message}`),t.verbose)console.error(L.stack);process.exit(1)}}),N.command("tunnel:logs","View tunnel server logs").option("--region <region>","AWS region",{default:"us-east-1"}).option("--prefix <prefix>","Resource name prefix",{default:"localtunnel"}).option("--tail","Tail the logs").action(async(t)=>{i("Tunnel Server Logs"),e("Tunnel server log groups:"),e(` - /aws/lambda/${t.prefix}-connect`),e(` - /aws/lambda/${t.prefix}-disconnect`),e(` - /aws/lambda/${t.prefix}-message`),e(` - /aws/lambda/${t.prefix}-http`),e(""),e("To view logs:"),e(` aws logs tail /aws/lambda/${t.prefix}-http --region ${t.region}${t.tail?" --follow":""}`),e(""),e("Or use CloudWatch Insights:"),e(" 1. Go to CloudWatch > Logs Insights"),e(" 2. Select the tunnel log groups"),e(" 3. Run queries like:"),e(" fields @timestamp, @message | filter @message like /error/i")}),N.command("tunnel:test <url>","Test a tunnel connection").action(async(t)=>{i("Test Tunnel Connection");let p=new o(`Testing ${t}...`);p.start();try{let A=Date.now(),L=await fetch(t,{method:"GET",headers:{"User-Agent":"ts-cloud-tunnel-test"}}),j=Date.now()-A;p.succeed(`Connected in ${j}ms`),e(`
668
+ Or destroy using the localtunnels CLI directly:`),e(" bunx localtunnels destroy --region us-east-1");return}await L({region:t.region,prefix:t.prefix,verbose:t.verbose}),A.succeed("Infrastructure destroyed!")}catch(L){if(A.fail("Destruction failed"),r(`Error: ${L.message}`),t.verbose)console.error(L.stack);process.exit(1)}}),N.command("tunnel:logs","View tunnel server logs").option("--region <region>","AWS region",{default:"us-east-1"}).option("--prefix <prefix>","Resource name prefix",{default:"localtunnel"}).option("--tail","Tail the logs").action(async(t)=>{i("Tunnel Server Logs"),e("Tunnel server log groups:"),e(` - /aws/lambda/${t.prefix}-connect`),e(` - /aws/lambda/${t.prefix}-disconnect`),e(` - /aws/lambda/${t.prefix}-message`),e(` - /aws/lambda/${t.prefix}-http`),e(""),e("To view logs:"),e(` aws logs tail /aws/lambda/${t.prefix}-http --region ${t.region}${t.tail?" --follow":""}`),e(""),e("Or use CloudWatch Insights:"),e(" 1. Go to CloudWatch > Logs Insights"),e(" 2. Select the tunnel log groups"),e(" 3. Run queries like:"),e(" fields @timestamp, @message | filter @message like /error/i")}),N.command("tunnel:test <url>","Test a tunnel connection").action(async(t)=>{i("Test Tunnel Connection");let m=new o(`Testing ${t}...`);m.start();try{let A=Date.now(),L=await fetch(t,{method:"GET",headers:{"User-Agent":"ts-cloud-tunnel-test"}}),j=Date.now()-A;m.succeed(`Connected in ${j}ms`),e(`
669
669
  Status: ${L.status} ${L.statusText}`),e(`
670
670
  Response headers:`),L.headers.forEach((Z,ee)=>{e(` ${ee}: ${Z}`)});let G=await L.text();if(G.length>0){if(e(`
671
- Response body (first 500 chars):`),console.log(G.substring(0,500)),G.length>500)e(`... (${G.length-500} more characters)`)}}catch(A){p.fail("Connection failed"),r(`Error: ${A.message}`)}}),N.command("tunnel:server","Start a self-hosted tunnel server").option("--port <number>","Port to listen on",{default:"3000"}).option("--host <hostname>","Host to bind to",{default:"0.0.0.0"}).option("--domain <domain>","Domain for tunnel URLs",{default:"localhost"}).option("--verbose","Enable verbose logging").action(async(t)=>{i("Local Tunnel Server");let p=Number.parseInt(t.port);e(`Listening on: ${t.host}:${p}`),e(`Domain: ${t.domain}`),e("");let A=new o("Starting tunnel server...");A.start();try{let L;try{let Z=await import("localtunnels");L=Z.TunnelServer}catch{A.fail("localtunnels package not found"),e(`
671
+ Response body (first 500 chars):`),console.log(G.substring(0,500)),G.length>500)e(`... (${G.length-500} more characters)`)}}catch(A){m.fail("Connection failed"),r(`Error: ${A.message}`)}}),N.command("tunnel:server","Start a self-hosted tunnel server").option("--port <number>","Port to listen on",{default:"3000"}).option("--host <hostname>","Host to bind to",{default:"0.0.0.0"}).option("--domain <domain>","Domain for tunnel URLs",{default:"localhost"}).option("--verbose","Enable verbose logging").action(async(t)=>{i("Local Tunnel Server");let m=Number.parseInt(t.port);e(`Listening on: ${t.host}:${m}`),e(`Domain: ${t.domain}`),e("");let A=new o("Starting tunnel server...");A.start();try{let L;try{let Z=await import("localtunnels");L=Z.TunnelServer}catch{A.fail("localtunnels package not found"),e(`
672
672
  To run a tunnel server, install localtunnels:`),e(" bun add localtunnels"),e(`
673
- Or use the standalone CLI:`),e(" bunx localtunnels server --port 3000");return}let j=new L({port:p,host:t.host,verbose:t.verbose});j.on("connection",(Z)=>{e(`+ Client connected: ${Z.subdomain} (total: ${Z.totalConnections})`)}),j.on("disconnection",(Z)=>{e(`- Client disconnected: ${Z.subdomain}`)});let G=()=>{e(`
674
- Shutting down server...`),j.stop(),process.exit(0)};process.on("SIGINT",G),process.on("SIGTERM",G),await j.start(),A.succeed("Server running!"),e(""),e(`WebSocket URL: ws://${t.host==="0.0.0.0"?"localhost":t.host}:${p}`),e(`HTTP URL: http://${t.host==="0.0.0.0"?"localhost":t.host}:${p}`),e(""),e("Clients can connect with:"),e(` cloud tunnel --port 3000 --server ${t.host==="0.0.0.0"?"localhost":t.host}:${p}`),e(""),e("Press Ctrl+C to stop the server"),await new Promise(()=>{})}catch(L){A.fail("Failed to start server"),r(`Error: ${L.message}`),process.exit(1)}})}var Tt=new ca("cloud");Tt.option("--env <environment>","Environment (production, staging, development)").option("--region <region>","AWS Region").option("--profile <profile>","AWS CLI Profile").option("--verbose","Enable verbose logging").option("--dry-run","Show what would be done without making changes");Ua(Tt);ga(Tt);ba(Tt);$a(Tt);Aa(Tt);Ac(Tt);Ic(Tt);Pc(Tt);xc(Tt);Pa(Tt);Da(Tt);Ea(Tt);Ta(Tt);Na(Tt);La(Tt);ja(Tt);Oc(Tt);Lc(Tt);jc(Tt);Fc(Tt);Bc(Tt);Mc(Tt);Fa(Tt);Ha(Tt);_a(Tt);Gc(Tt);_c(Tt);zc(Tt);Uc(Tt);Vc(Tt);Wa(Tt);qa(Tt);Wc(Tt);qc(Tt);Hc(Tt);Kc(Tt);Jc(Tt);Yc(Tt);Zc(Tt);Qc(Tt);Xc(Tt);el(Tt);ec(Tt);nc(Tt);tc(Tt);ic(Tt);oc(Tt);ac(Tt);lc(Tt);uc(Tt);mc(Tt);pc(Tt);Ec(Tt);tl(Tt);Tc(Tt);Dc(Tt,la);Tt.version(la);Tt.help();Tt.parse();
673
+ Or use the standalone CLI:`),e(" bunx localtunnels server --port 3000");return}let j=new L({port:m,host:t.host,verbose:t.verbose});j.on("connection",(Z)=>{e(`+ Client connected: ${Z.subdomain} (total: ${Z.totalConnections})`)}),j.on("disconnection",(Z)=>{e(`- Client disconnected: ${Z.subdomain}`)});let G=()=>{e(`
674
+ Shutting down server...`),j.stop(),process.exit(0)};process.on("SIGINT",G),process.on("SIGTERM",G),await j.start(),A.succeed("Server running!"),e(""),e(`WebSocket URL: ws://${t.host==="0.0.0.0"?"localhost":t.host}:${m}`),e(`HTTP URL: http://${t.host==="0.0.0.0"?"localhost":t.host}:${m}`),e(""),e("Clients can connect with:"),e(` cloud tunnel --port 3000 --server ${t.host==="0.0.0.0"?"localhost":t.host}:${m}`),e(""),e("Press Ctrl+C to stop the server"),await new Promise(()=>{})}catch(L){A.fail("Failed to start server"),r(`Error: ${L.message}`),process.exit(1)}})}var Tt=new va("cloud");Tt.option("--env <environment>","Environment (production, staging, development)").option("--region <region>","AWS Region").option("--profile <profile>","AWS CLI Profile").option("--verbose","Enable verbose logging").option("--dry-run","Show what would be done without making changes");Xa(Tt);$a(Tt);Pa(Tt);Na(Tt);La(Tt);Oc(Tt);Fc(Tt);Bc(Tt);Mc(Tt);Ma(Tt);Ua(Tt);Va(Tt);Wa(Tt);qa(Tt);Ha(Tt);Ka(Tt);Wc(Tt);qc(Tt);Gc(Tt);Hc(Tt);Kc(Tt);Jc(Tt);Ja(Tt);ic(Tt);Qa(Tt);nl(Tt);Yc(Tt);Zc(Tt);Qc(Tt);Xc(Tt);ec(Tt);tc(Tt);el(Tt);tl(Tt);rl(Tt);il(Tt);ol(Tt);sl(Tt);al(Tt);cl(Tt);ll(Tt);dl(Tt);dc(Tt);pc(Tt);uc(Tt);fc(Tt);gc(Tt);yc(Tt);bc(Tt);Sc(Tt);$c(Tt);Cc(Tt);zc(Tt);ul(Tt);Uc(Tt);_c(Tt,ba);Tt.version(ba);Tt.help();Tt.parse();