@stacksjs/ts-cloud 0.9.4 → 0.10.0

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 (251) hide show
  1. package/dist/aws/index.js +1 -1
  2. package/dist/bin/chunk-08c0pzkb.js +1 -0
  3. package/dist/bin/chunk-12210qvj.js +1 -0
  4. package/dist/bin/chunk-1v6bt68w.js +6 -0
  5. package/dist/bin/chunk-35ttwn26.js +70 -0
  6. package/dist/bin/chunk-3r3be05z.js +1 -0
  7. package/dist/bin/chunk-41yqpdv7.js +2 -0
  8. package/dist/bin/chunk-5rv31chh.js +1 -0
  9. package/dist/bin/chunk-6eat8ezk.js +2 -0
  10. package/dist/bin/chunk-6gktf2m2.js +1 -0
  11. package/dist/bin/chunk-6zjy949y.js +141 -0
  12. package/dist/bin/chunk-7wzdaqh6.js +2 -0
  13. package/dist/bin/chunk-80ar3aty.js +1 -0
  14. package/dist/bin/chunk-8rwhsp5b.js +2 -0
  15. package/dist/bin/chunk-936vp7dx.js +44 -0
  16. package/dist/bin/chunk-9px3rg4a.js +4 -0
  17. package/dist/bin/{chunk-rxcvf10f.js → chunk-anm3ae6z.js} +129 -129
  18. package/dist/bin/{chunk-tx0tqq2k.js → chunk-axvr7dwa.js} +6 -6
  19. package/dist/bin/chunk-b0hj9m5e.js +2 -0
  20. package/dist/bin/{chunk-mh0kdzqp.js → chunk-b2ymh8k0.js} +1 -1
  21. package/dist/bin/chunk-b35989e3.js +50 -0
  22. package/dist/bin/chunk-c2b6nk0q.js +37 -0
  23. package/dist/bin/chunk-ckxpgwxw.js +3 -0
  24. package/dist/bin/chunk-d6zzgvtn.js +92 -0
  25. package/dist/bin/chunk-da8zpf82.js +1 -0
  26. package/dist/bin/chunk-ddmjjstg.js +1 -0
  27. package/dist/bin/chunk-dhhz4zyj.js +1 -0
  28. package/dist/bin/chunk-dj9a67c3.js +9 -0
  29. package/dist/bin/chunk-ds1z880f.js +1 -0
  30. package/dist/bin/chunk-dvkj4k6h.js +111 -0
  31. package/dist/bin/chunk-eh40fpvj.js +1 -0
  32. package/dist/bin/chunk-ey9fagrr.js +6 -0
  33. package/dist/bin/chunk-ezftw2q4.js +1 -0
  34. package/dist/bin/chunk-g6a4n3rh.js +1 -0
  35. package/dist/bin/chunk-g90nqfgv.js +585 -0
  36. package/dist/bin/chunk-gs40j75w.js +1 -0
  37. package/dist/bin/chunk-jjjts1vt.js +1 -0
  38. package/dist/bin/{chunk-etrb03mt.js → chunk-jnsbzqzz.js} +30 -30
  39. package/dist/bin/chunk-jnx4jgw8.js +1 -0
  40. package/dist/bin/chunk-jt1tndm2.js +3 -0
  41. package/dist/bin/chunk-kybdb0ws.js +2 -0
  42. package/dist/bin/chunk-m3nqw76h.js +3 -0
  43. package/dist/bin/chunk-mn6j22zn.js +6 -0
  44. package/dist/bin/chunk-nb0yjkv5.js +2 -0
  45. package/dist/bin/chunk-pjpa4x52.js +2 -0
  46. package/dist/bin/chunk-pkgkfe6s.js +2 -0
  47. package/dist/bin/chunk-pnz5hk8n.js +60 -0
  48. package/dist/bin/chunk-q46zr2tv.js +1 -0
  49. package/dist/bin/chunk-qtdngvs8.js +1 -0
  50. package/dist/bin/chunk-sbejksrh.js +2 -0
  51. package/dist/bin/chunk-svyd4v6c.js +34 -0
  52. package/dist/bin/chunk-tna7ay8d.js +1 -0
  53. package/dist/bin/chunk-tp4c2mv1.js +1 -0
  54. package/dist/bin/chunk-w5d6tr2x.js +1 -0
  55. package/dist/bin/chunk-wewcjam5.js +1 -0
  56. package/dist/bin/chunk-xv4bscrm.js +1 -0
  57. package/dist/bin/chunk-y9eyc5pw.js +1 -0
  58. package/dist/bin/chunk-y9f8649w.js +512 -0
  59. package/dist/bin/cli.js +428 -418
  60. package/dist/bin/dashboard-server.js +1 -1
  61. package/dist/{chunk-dskvqt67.js → chunk-06jk7ba9.js} +127 -127
  62. package/dist/chunk-0e16qt6k.js +5 -0
  63. package/dist/chunk-13hxhzaz.js +34 -0
  64. package/dist/chunk-2p89vt3b.js +2 -0
  65. package/dist/{chunk-xt3s24vr.js → chunk-77t2ctwn.js} +1 -1
  66. package/dist/chunk-88z37ey4.js +114 -0
  67. package/dist/chunk-8c4715hz.js +2 -0
  68. package/dist/chunk-9a22225r.js +60 -0
  69. package/dist/chunk-as6by62d.js +1 -0
  70. package/dist/chunk-cd0zpxhf.js +44 -0
  71. package/dist/chunk-cp54a0qn.js +6 -0
  72. package/dist/chunk-d67xbk4v.js +141 -0
  73. package/dist/chunk-dckz9xve.js +2 -0
  74. package/dist/chunk-e1666vjv.js +2 -0
  75. package/dist/chunk-hhdx5k96.js +442 -0
  76. package/dist/{chunk-gahsdmxf.js → chunk-kv2ztkp4.js} +52 -52
  77. package/dist/chunk-nrz6zypg.js +1 -0
  78. package/dist/chunk-q816js1y.js +1 -0
  79. package/dist/chunk-qh9ep11z.js +704 -0
  80. package/dist/chunk-rg720cqg.js +1 -0
  81. package/dist/chunk-snak8s7m.js +92 -0
  82. package/dist/chunk-vq737g82.js +1 -0
  83. package/dist/chunk-xd6w0c11.js +50 -0
  84. package/dist/chunk-xtwjt370.js +37 -0
  85. package/dist/chunk-y26yh5zp.js +1 -0
  86. package/dist/chunk-zmxffmmq.js +4 -0
  87. package/dist/deploy/attach-credentials.d.ts +4 -8
  88. package/dist/deploy/index.js +1 -1
  89. package/dist/dns/cloudflare.d.ts +27 -0
  90. package/dist/dns/godaddy.d.ts +18 -1
  91. package/dist/dns/index.d.ts +2 -0
  92. package/dist/dns/index.js +1 -1
  93. package/dist/dns/zone-migration.d.ts +92 -0
  94. package/dist/dns/zone-migration.test.d.ts +1 -0
  95. package/dist/drivers/hetzner/driver.d.ts +8 -1
  96. package/dist/drivers/index.js +1 -1
  97. package/dist/drivers/shared/compute-provision.d.ts +14 -2
  98. package/dist/drivers/shared/db-provision.d.ts +3 -2
  99. package/dist/drivers/shared/mail-provision.d.ts +116 -0
  100. package/dist/drivers/shared/mail-provision.test.d.ts +1 -0
  101. package/dist/drivers/shared/managed-services-probe.d.ts +47 -0
  102. package/dist/drivers/shared/managed-services-probe.test.d.ts +1 -0
  103. package/dist/drivers/shared/package-manager.d.ts +1 -0
  104. package/dist/drivers/shared/remote-failure.d.ts +21 -0
  105. package/dist/drivers/shared/remote-failure.test.d.ts +1 -0
  106. package/dist/drivers/shared/sqlite-shared-path.d.ts +50 -0
  107. package/dist/drivers/shared/sqlite-shared-path.test.d.ts +1 -0
  108. package/dist/index.d.ts +2 -0
  109. package/dist/index.js +89 -89
  110. package/dist/operations/plan.d.ts +136 -0
  111. package/dist/operations/plan.test.d.ts +1 -0
  112. package/dist/operations/server-rename.d.ts +84 -0
  113. package/dist/operations/server-rename.test.d.ts +1 -0
  114. package/dist/operations/site-move.d.ts +263 -0
  115. package/dist/operations/site-move.test.d.ts +1 -0
  116. package/dist/protection/index.js +1 -1
  117. package/dist/push/index.js +1 -1
  118. package/dist/spend/index.js +1 -1
  119. package/dist/ui/404.html +1 -1
  120. package/dist/ui/accept-invitation.html +1 -1
  121. package/dist/ui/access-denied.html +1 -1
  122. package/dist/ui/account/automation.html +6 -6
  123. package/dist/ui/account/security.html +1 -1
  124. package/dist/ui/applications/compose.html +5 -5
  125. package/dist/ui/applications/new.html +2 -2
  126. package/dist/ui/data/backups.html +80 -80
  127. package/dist/ui/data/services.html +78 -78
  128. package/dist/ui/data/volumes.html +50 -50
  129. package/dist/ui/index.html +50 -50
  130. package/dist/ui/infrastructure/topology.html +118 -118
  131. package/dist/ui/integrations.html +1 -1
  132. package/dist/ui/operations/alerts.html +47 -47
  133. package/dist/ui/operations/configuration.html +5 -5
  134. package/dist/ui/operations/jobs.html +76 -76
  135. package/dist/ui/operations/maintenance.html +6 -6
  136. package/dist/ui/operations/observability.html +82 -82
  137. package/dist/ui/operations/previews.html +6 -6
  138. package/dist/ui/operations/queue.html +6 -6
  139. package/dist/ui/operations/regions.html +6 -6
  140. package/dist/ui/operations/releases.html +6 -6
  141. package/dist/ui/operations/spend.html +54 -54
  142. package/dist/ui/operations/workloads.html +104 -104
  143. package/dist/ui/security.html +2 -2
  144. package/dist/ui/server/actions.html +56 -56
  145. package/dist/ui/server/activity.html +1 -1
  146. package/dist/ui/server/capacity.html +6 -6
  147. package/dist/ui/server/database.html +106 -106
  148. package/dist/ui/server/deployments.html +50 -50
  149. package/dist/ui/server/diagnostics.html +2 -2
  150. package/dist/ui/server/firewall.html +76 -76
  151. package/dist/ui/server/fleet.html +33 -33
  152. package/dist/ui/server/logs.html +23 -23
  153. package/dist/ui/server/metrics.html +112 -112
  154. package/dist/ui/server/security.html +2 -2
  155. package/dist/ui/server/services.html +27 -27
  156. package/dist/ui/server/sites.html +120 -120
  157. package/dist/ui/server/ssh-keys.html +52 -52
  158. package/dist/ui/server/team.html +144 -144
  159. package/dist/ui/server/terminal.html +15 -15
  160. package/dist/ui/serverless/alarms.html +52 -52
  161. package/dist/ui/serverless/assets.html +27 -27
  162. package/dist/ui/serverless/cost.html +2 -2
  163. package/dist/ui/serverless/data.html +36 -36
  164. package/dist/ui/serverless/deployments.html +26 -26
  165. package/dist/ui/serverless/firewall.html +1 -1
  166. package/dist/ui/serverless/functions.html +39 -39
  167. package/dist/ui/serverless/logs.html +24 -24
  168. package/dist/ui/serverless/metrics.html +2 -2
  169. package/dist/ui/serverless/queues.html +48 -48
  170. package/dist/ui/serverless/secrets.html +52 -52
  171. package/dist/ui/serverless/traces.html +6 -6
  172. package/dist/ui/serverless.html +54 -54
  173. package/dist/ui-src/src/charts.ts +17499 -12662
  174. package/dist/ui-src/src/topology.ts +2 -2
  175. package/package.json +3 -3
  176. package/dist/bin/chunk-00kg80qg.js +0 -1
  177. package/dist/bin/chunk-09nwf2gy.js +0 -34
  178. package/dist/bin/chunk-1k5kszjp.js +0 -111
  179. package/dist/bin/chunk-21e0mavm.js +0 -1
  180. package/dist/bin/chunk-271gk1n2.js +0 -6
  181. package/dist/bin/chunk-2empps47.js +0 -2
  182. package/dist/bin/chunk-38pq971s.js +0 -2
  183. package/dist/bin/chunk-3gykfyq4.js +0 -37
  184. package/dist/bin/chunk-3vqfexc4.js +0 -70
  185. package/dist/bin/chunk-4vrqm9h0.js +0 -2
  186. package/dist/bin/chunk-598yf4bs.js +0 -1
  187. package/dist/bin/chunk-5ectjq0b.js +0 -1
  188. package/dist/bin/chunk-5erkxpt7.js +0 -512
  189. package/dist/bin/chunk-5mrtzemj.js +0 -1
  190. package/dist/bin/chunk-5n9fmabb.js +0 -1
  191. package/dist/bin/chunk-9knpg30y.js +0 -1
  192. package/dist/bin/chunk-9pvsff8g.js +0 -1
  193. package/dist/bin/chunk-ax9mr6wm.js +0 -44
  194. package/dist/bin/chunk-axhnkn5g.js +0 -2
  195. package/dist/bin/chunk-c1vcdgvr.js +0 -1
  196. package/dist/bin/chunk-ctd2vg9w.js +0 -2
  197. package/dist/bin/chunk-dbgffky8.js +0 -1
  198. package/dist/bin/chunk-ddk4rt4j.js +0 -6
  199. package/dist/bin/chunk-dwr30vbv.js +0 -1
  200. package/dist/bin/chunk-e0rkrq70.js +0 -1
  201. package/dist/bin/chunk-e3xrwh9z.js +0 -1
  202. package/dist/bin/chunk-fhc6ntvd.js +0 -2
  203. package/dist/bin/chunk-fpm59y25.js +0 -6
  204. package/dist/bin/chunk-fvbxvq6b.js +0 -1
  205. package/dist/bin/chunk-fy8j3y8p.js +0 -92
  206. package/dist/bin/chunk-fz21b57w.js +0 -1
  207. package/dist/bin/chunk-gavvf0pg.js +0 -141
  208. package/dist/bin/chunk-ggn7ax8p.js +0 -4
  209. package/dist/bin/chunk-gpe2prkm.js +0 -1
  210. package/dist/bin/chunk-h5pa5men.js +0 -2
  211. package/dist/bin/chunk-hm08xdxf.js +0 -2
  212. package/dist/bin/chunk-hzgz13nf.js +0 -1
  213. package/dist/bin/chunk-j703btc5.js +0 -50
  214. package/dist/bin/chunk-k9t9wvms.js +0 -1
  215. package/dist/bin/chunk-m669wtgr.js +0 -1
  216. package/dist/bin/chunk-m7dgskt1.js +0 -1
  217. package/dist/bin/chunk-mr0atd2s.js +0 -2
  218. package/dist/bin/chunk-mz0wbbsj.js +0 -3
  219. package/dist/bin/chunk-ngcyftm2.js +0 -1
  220. package/dist/bin/chunk-p66ptpmr.js +0 -2
  221. package/dist/bin/chunk-qk9qgepa.js +0 -1
  222. package/dist/bin/chunk-rytk89az.js +0 -60
  223. package/dist/bin/chunk-tga3hhwk.js +0 -6
  224. package/dist/bin/chunk-vek8a0w6.js +0 -1
  225. package/dist/bin/chunk-vm3q5gf2.js +0 -3
  226. package/dist/bin/chunk-y7s5r8y6.js +0 -3
  227. package/dist/bin/chunk-yv9sth30.js +0 -1
  228. package/dist/bin/chunk-zmegvjp8.js +0 -581
  229. package/dist/chunk-0ccta6z6.js +0 -6
  230. package/dist/chunk-0k1cs6pp.js +0 -1
  231. package/dist/chunk-1egjn203.js +0 -704
  232. package/dist/chunk-2wx29qga.js +0 -5
  233. package/dist/chunk-5m5rqmv7.js +0 -1
  234. package/dist/chunk-8ea5022g.js +0 -114
  235. package/dist/chunk-8xqn3c0w.js +0 -2
  236. package/dist/chunk-b4esxhmw.js +0 -2
  237. package/dist/chunk-b8e5wzwn.js +0 -44
  238. package/dist/chunk-bggt4n6e.js +0 -2
  239. package/dist/chunk-cwkg37rs.js +0 -37
  240. package/dist/chunk-gnv3r2jg.js +0 -1
  241. package/dist/chunk-hg3c48cp.js +0 -34
  242. package/dist/chunk-m5azeva3.js +0 -2
  243. package/dist/chunk-r3qf56b3.js +0 -92
  244. package/dist/chunk-sdqa9ghv.js +0 -141
  245. package/dist/chunk-svrvvcm7.js +0 -1
  246. package/dist/chunk-v0p3hpc3.js +0 -60
  247. package/dist/chunk-wdzjbecg.js +0 -50
  248. package/dist/chunk-ww8k3g7j.js +0 -1
  249. package/dist/chunk-xmqfc62w.js +0 -1
  250. package/dist/chunk-xrebj679.js +0 -435
  251. package/dist/chunk-y21zk0pn.js +0 -4
package/dist/bin/cli.js CHANGED
@@ -1,51 +1,42 @@
1
1
  #!/usr/bin/env bun
2
- import{i as d1,j as KJ,k as z1,l as jJ,n as BJ,o as b2}from"./chunk-hzgz13nf.js";import{v as o1}from"./chunk-rytk89az.js";import{$ as u1,A as B4,Aa as y2,B as x4,Ba as S$,C as _1,Ca as GY,D as O0,Da as l0,E as i4,Ea as hY,F as o4,Fa as yY,G as $9,Ga as CY,H as J9,Ha as i5,I as Y9,Ia as fY,J as G9,Ja as s5,K as X9,Ka as LY,L as Q9,La as EY,M as m$,Ma as TY,N as K9,Na as P5,O as y1,Oa as b$,P as e6,Pa as l$,Q as j9,Qa as Q0,R as B9,Ra as l5,S as V9,Sa as PY,T as A9,Ta as Q6,U as q9,V as R9,W as L9,X as E9,Y as T9,Z as o6,_ as C1,aa as u9,ba as l9,ca as TJ,da as r9,ea as a9,fa as n9,ga as s9,ha as DJ,ia as i9,ja as t9,ka as e9,la as o9,ma as bY,na as IY,oa as kY,pa as K2,qa as j2,ra as B2,sa as W5,ta as z5,ua as I2,va as F5,w as S0,wa as U5,x as Q$,xa as k2,y as JY,ya as h2,z as u,za as M5}from"./chunk-5erkxpt7.js";import"./chunk-38pq971s.js";import{$a as Z2,$b as y9,Ab as k9,Bb as qJ,Gc as f9,Jc as g9,Pc as d$,Tc as v9,Uc as RJ,Vc as LJ,Wa as v$,Wc as m9,Ya as G2,Za as X2,_a as Q2,_b as h9,ab as W2,bb as z2,cb as F2,db as U2,eb as M2,fb as UY,hb as MY,ib as HY,jb as KY,kb as H2,lb as p1,mb as w4,nb as r1,pb as F1,qb as D5,rb as x5,sb as a5,tb as DY,ub as xY,xb as NY,xc as C9,yb as N5}from"./chunk-1k5kszjp.js";import{cd as P0}from"./chunk-hm08xdxf.js";import{dd as h1}from"./chunk-fhc6ntvd.js";import{ed as eJ,fd as oJ,gd as $5,id as B0,kd as n6,ld as U0,md as O6}from"./chunk-ctd2vg9w.js";import{nd as $$}from"./chunk-gavvf0pg.js";import"./chunk-y7s5r8y6.js";import{sd as o$,td as f0,ud as P9,vd as S9,wd as N0}from"./chunk-vm3q5gf2.js";import{yd as X0}from"./chunk-4vrqm9h0.js";import{Ad as V0,Ed as Y2,zd as M0}from"./chunk-mz0wbbsj.js";import{Rd as e$}from"./chunk-p66ptpmr.js";import{$d as m4,Sd as D6,Td as b1,Ud as o0,Vd as $1,Wd as g4,Xd as C6,Yd as rJ,Zd as f6,_d as v4,ae as d4,be as c4,ce as u4,de as l4,ee as g6,fe as r4,ge as v6,he as a4,ie as p4,je as I1,ke as n4,le as j0,me as s4,ne as G5,oe as e1,pe as uJ,qe as X5,re as c$,te as Q5,ue as Z5}from"./chunk-zmegvjp8.js";import{Me as VJ}from"./chunk-3gykfyq4.js";import{Ue as HJ}from"./chunk-ax9mr6wm.js";import{Xe as E$}from"./chunk-ggn7ax8p.js";import"./chunk-ddk4rt4j.js";import{jf as B1,kf as iJ,lf as J5,mf as Y5,nf as S2,of as _2}from"./chunk-tga3hhwk.js";import"./chunk-axhnkn5g.js";import{Uf as Y$}from"./chunk-h5pa5men.js";import{Vf as K$,Wf as u$}from"./chunk-09nwf2gy.js";import"./chunk-j703btc5.js";import{$f as Z$,_f as x9}from"./chunk-fy8j3y8p.js";import{ag as A4,dg as q4,eg as R4,fg as L4,gg as E4,hg as T4,ig as O4}from"./chunk-tx0tqq2k.js";import{jg as G6,ng as OY,og as YY}from"./chunk-etrb03mt.js";import"./chunk-2empps47.js";import{rg as V1}from"./chunk-mr0atd2s.js";import{Ag as R,Bg as E,Cg as p,Dg as k,Eg as E6,Fg as w,Gg as q1,Hg as n$,Ig as i2,Jg as t2,Kg as e2,Lg as o2,Mg as v,sg as Z0,tg as w2,ug as B,vg as K,wg as N,xg as X1,yg as W,zg as y}from"./chunk-271gk1n2.js";import{Og as WJ,Qg as O2}from"./chunk-fpm59y25.js";import{Ah as i$,Ai as a2,Bh as x2,Ch as N2,Dh as P2,Gh as J2,Mh as P6,Qh as S6,Rh as F$,Sh as _4,Th as N1,Uh as H0,Xh as _6,ai as b4,bi as y0,ci as P1,di as b6,ei as I4,fi as k4,ii as h4,lh as m2,nh as j1,oh as sJ,oi as y4,ph as D2,pi as S1,qh as c0,rh as N4,vi as r2,wh as P4,xh as S4}from"./chunk-rxcvf10f.js";import{Vo as h}from"./chunk-mh0kdzqp.js";import{existsSync as gY}from"node:fs";import F6 from"node:fs/promises";import o5 from"node:os";import U6 from"node:path";import E0 from"node:process";import{EventEmitter as vY}from"node:events";import z0 from"node:process";import $4 from"node:process";import j6 from"node:process";import T1 from"node:process";import n0 from"node:process";import G4 from"node:tty";import x8,{stdin as QZ,stdout as ZZ}from"node:process";import Q4,{stdin as S8,stdout as _8}from"node:process";import b8 from"node:readline";class W4{configPath;config=null;events=[];retryCount=0;maxRetries=3;retryDelayMs=1000;constructor(){let G=o5.homedir(),$=U6.join(G,".config","clapp");this.configPath=U6.join($,"telemetry.json")}async isEnabled(){if(E0.env.DO_NOT_TRACK==="1"||E0.env.DO_NOT_TRACK==="true")return!1;if(E0.env.NO_TELEMETRY==="1"||E0.env.NO_TELEMETRY==="true")return!1;return(await this.loadConfig()).enabled}async enable(){let G=await this.loadConfig();if(G.enabled=!0,!G.userId)G.userId=this.generateUserId();await this.saveConfig(G)}async disable(){let G=await this.loadConfig();G.enabled=!1,await this.saveConfig(G)}async track(G,$){if(!await this.isEnabled())return;let Y={event:G,...$,timestamp:Date.now(),platform:o5.platform(),nodeVersion:E0.version};if(this.events.push(Y),this.events.length>=10)await this.send()}async trackCommand(G,$){await this.track("command",{command:G,duration:$})}async trackError(G,$){await this.track("error",{error:G,command:$})}async send(){if(!await this.isEnabled()||this.events.length===0)return!0;try{this.events=[],this.retryCount=0;let $=await this.loadConfig();return $.lastSent=Date.now(),await this.saveConfig($),!0}catch{if(this.retryCount<this.maxRetries){this.retryCount++;let $=this.retryDelayMs*2**(this.retryCount-1);return await this.sleep($),this.send()}return this.events=[],this.retryCount=0,!1}}async flush(){if(this.events.length===0)return!0;return this.send()}sleep(G){return new Promise(($)=>setTimeout($,G))}async status(){let G=await this.loadConfig();return{enabled:G.enabled,doNotTrack:E0.env.DO_NOT_TRACK==="1"||E0.env.DO_NOT_TRACK==="true",eventsQueued:this.events.length,lastSent:G.lastSent}}async loadConfig(){if(this.config)return this.config;try{if(gY(this.configPath)){let G=await F6.readFile(this.configPath,"utf-8");return this.config=JSON.parse(G),this.config}}catch{}return this.config={enabled:!1},this.config}async saveConfig(G){this.config=G;try{let $=U6.dirname(this.configPath);await F6.mkdir($,{recursive:!0}),await F6.writeFile(this.configPath,JSON.stringify(G,null,2),"utf-8")}catch{}}generateUserId(){let G=Math.random().toString(36).substring(2,15),$=Date.now().toString(36);return`${G}-${$}`}}var pQ=new W4;function mY(G,$={}){let J={_:[]},Y=$.alias||{},X=new Set($.boolean||[]),Q={};for(let z of Object.keys(Y))for(let F of Y[z])Q[F]=z;for(let z of X)if(Y[z])for(let F of Y[z])X.add(F);function Z(z,F){let U=Q[z]||z;if(J[U]=F,Y[U])for(let M of Y[U])J[M]=F;if(Q[z]&&Y[Q[z]])for(let M of Y[Q[z]])J[M]=F;J[z]=F}for(let z=0;z<G.length;z++){let F=G[z];if(F==="--"){J._.push(...G.slice(z+1));break}if(F.startsWith("--")){let U=F.indexOf("=");if(U!==-1){let M=F.slice(2,U),H=F.slice(U+1);Z(M,H)}else{let M=F.slice(2);if(M.startsWith("no-")){let j=M.slice(3);Z(j,!1);continue}let H=Q[M]||M;if(X.has(H)||X.has(M))Z(M,!0);else{let j=G[z+1];if(j!==void 0&&!j.startsWith("-"))Z(M,j),z++;else Z(M,!0)}}}else if(F.startsWith("-")&&F.length>1){let U=F.slice(1);for(let M=0;M<U.length;M++){let H=U[M],j=Q[H]||H;if(M===U.length-1&&!X.has(j)&&!X.has(H)){let V=G[z+1];if(V!==void 0&&!V.startsWith("-"))Z(H,V),z++;else Z(H,!0)}else Z(H,!0)}}else J._.push(F)}return J}function K6(G){return G.replace(/[<[].+/,"").trim()}function dY(G){let $=/<([^>]+)>/g,J=/\[([^\]]+)\]/g,Y=[],X=(z)=>{let F=!1,U=z[1];if(U.startsWith("..."))U=U.slice(3),F=!0;return{required:z[0].startsWith("<"),value:U,variadic:F}},Q;while(Q=$.exec(G))Y.push(X(Q));let Z;while(Z=J.exec(G))Y.push(X(Z));return Y}function cY(G){let $={alias:{},boolean:[]};for(let[J,Y]of G.entries()){if(Y.names.length>1)$.alias[Y.names[0]]=Y.names.slice(1);if(Y.isBoolean)if(Y.negated){if(!G.some((Q,Z)=>{return Z!==J&&Q.names.some((z)=>Y.names.includes(z))&&typeof Q.required==="boolean"}))$.boolean.push(Y.names[0])}else $.boolean.push(Y.names[0])}return $}function J4(G){return G.reduce(($,J)=>$.length>=J.length?$:J,"")}function M6(G,$){return G.length>=$?G:`${G}${" ".repeat($-G.length)}`}function uY(G){return G.replace(/([a-z])-([a-z])/g,($,J,Y)=>{return J+Y.toUpperCase()})}function lY(G,$,J){let Y=0,X=$.length,Q=G,Z;for(;Y<X;++Y)Z=Q[$[Y]],Q=Q[$[Y]]=Y===X-1?J:Z!=null?Z:!!~$[Y+1].indexOf(".")||!(+$[Y+1]>-1)?{}:[]}function rY(G,$){for(let J of Object.keys($)){let Y=$[J];if(Y.shouldTransform){if(G[J]=Array.prototype.concat.call([],G[J]),typeof Y.transformFunction==="function")G[J]=G[J].map(Y.transformFunction)}}}function aY(G){let $=/([^\\/]+)$/.exec(G);return $?$[1]:""}function z4(G){return G.split(".").map(($,J)=>{return J===0?uY($):$}).join(".")}class E1 extends Error{exitCode=2;isUsageError=!0;constructor(G){super(G);if(this.name=this.constructor.name,typeof Error.captureStackTrace==="function")Error.captureStackTrace(this,this.constructor);else this.stack=Error(G).stack}format(G=!1){if(G&&this.stack)return`${this.message}
2
+ import{i as Qn,j as Ei,k as vn,l as Di,n as Ti,o as dl}from"./chunk-da8zpf82.js";import{v as lr}from"./chunk-pnz5hk8n.js";import{$ as gi,A as Is,Aa as pl,B as Js,Ba as Ro,C as ra,Ca as fl,D as Mn,Da as Te,E as Un,Ea as jl,F as Xe,Fa as Qt,G as Ea,Ga as pd,H as Na,Ha as fd,I as Oa,Ia as gd,J as La,Ja as ds,K as ja,Ka as us,L as Fa,La as hd,M as Ma,Ma as Xl,N as Ba,Na as ed,O as qe,Oa as td,P as Ka,Pa as Vo,Q as Gn,Qa as Oe,R as fi,Ra as Je,S as Ha,Sa as ct,T as Ja,Ta as os,U as Ya,Ua as ad,V as Za,Va as gr,W as Qa,X as Xa,Y as ec,Z as tc,_ as nc,aa as Kn,ba as er,ca as Cc,da as Ac,ea as Mi,fa as kc,ga as Rc,ha as Pc,ia as xc,ja as zi,ka as Ec,la as Dc,ma as Tc,na as Nc,oa as dd,pa as ud,qa as md,ra as Kc,sa as Hc,ta as Jc,ua as $o,va as Co,w as Tt,wa as ul,x as le,xa as Ao,y as Ol,ya as ko,z as q,za as ml}from"./chunk-y9f8649w.js";import"./chunk-41yqpdv7.js";import{$a as Mc,Ab as Uo,Cb as pc,Db as Oi,Ic as yc,Lc as vc,Rc as Ge,Vc as bc,Wc as Li,Xc as ji,Ya as We,Yc as wc,_a as Fc,ab as Bc,ac as fc,bb as _c,bc as gc,cb as zc,db as Uc,eb as Vc,fb as Wc,gb as qc,hb as Vl,ib as Wl,kb as ql,lb as Gl,mb as Gc,nb as ir,ob as js,pb as nr,rb as bn,sb as _o,tb as zo,ub as as,vb as id,wb as od,zb as sd,zc as hc}from"./chunk-dvkj4k6h.js";import{ed as Dt}from"./chunk-8rwhsp5b.js";import{fd as qn}from"./chunk-b0hj9m5e.js";import{gd as ii,hd as oi,id as pn,jd as go,kd as ho,md as si,nd as ai,od as ci,pd as li,qd as vt,sd as di,td as Re,ud as Dr}from"./chunk-nb0yjkv5.js";import{vd as ne}from"./chunk-6zjy949y.js";import"./chunk-m3nqw76h.js";import{Ad as nt,Bd as qt,Cd as cc,Dd as lc,Ed as Et}from"./chunk-jt1tndm2.js";import{Gd as at}from"./chunk-sbejksrh.js";import{Hd as pt,Id as bt,Md as jc}from"./chunk-ckxpgwxw.js";import{Zd as tt}from"./chunk-pjpa4x52.js";import{$d as yo,Ae as Ra,Be as Ia,Ce as Pa,De as ht,Ee as xa,Ge as wo,He as So,_d as Nr,ae as _t,be as oo,ce as rn,de as jr,ee as ea,fe as ao,ge as ta,he as Kr,ie as na,je as ln,ke as Vt,le as Wt,me as ya,ne as va,oe as Rt,pe as ba,qe as Ke,re as Hr,se as wa,te as Gr,ue as Jr,ve as Sa,we as $a,xe as Ca,ye as Aa,ze as ka}from"./chunk-g90nqfgv.js";import{Ze as xi}from"./chunk-c2b6nk0q.js";import{ff as Pi}from"./chunk-936vp7dx.js";import{if as Ce}from"./chunk-9px3rg4a.js";import"./chunk-mn6j22zn.js";import{Af as cl,Bf as ll,If as Zs,Lf as Qs,Pf as Xs,tf as Ys,wf as kn,xf as po,yf as vo,zf as bo}from"./chunk-dj9a67c3.js";import"./chunk-7wzdaqh6.js";import{hg as ie}from"./chunk-6eat8ezk.js";import{ig as he,jg as He}from"./chunk-svyd4v6c.js";import{mg as Wn}from"./chunk-b35989e3.js";import{ng as sc,og as de}from"./chunk-d6zzgvtn.js";import{pg as xs,sg as Es,tg as Ds,ug as Ts,vg as Ns,wg as Os,xg as Ls}from"./chunk-axvr7dwa.js";import{Cg as nd,Dg as Ll,yg as pr}from"./chunk-jnsbzqzz.js";import"./chunk-pkgkfe6s.js";import{Gg as Rn}from"./chunk-kybdb0ws.js";import{$g as z,Hg as lt,Ig as il,Jg as g,Kg as p,Lg as I,Mg as fn,Ng as a,Og as M,Pg as w,Qg as C,Rg as Z,Sg as O,Tg as xr,Ug as x,Vg as Pn,Wg as Qe,Xg as xl,Yg as El,Zg as Dl,_g as Tl}from"./chunk-1v6bt68w.js";import{bh as Ci,dh as rl}from"./chunk-ey9fagrr.js";import{Ah as bl,Bi as ua,Ch as An,Dh as mo,Eh as Fn,Hi as ma,Ii as zn,Jh as Yt,Kh as ia,Oi as Al,Ph as oa,Qh as sa,Th as Ve,Ti as kl,Uh as ol,Vh as sl,Wh as al,Zh as Lc,di as Mr,hi as Br,ii as me,ji as aa,ki as Bn,li as ft,oi as _r,ti as ca,ui as zt,vi as _n,wi as zr,xi as la,yi as da}from"./chunk-anm3ae6z.js";import{mp as F}from"./chunk-b2ymh8k0.js";import{existsSync as yd}from"node:fs";import br from"node:fs/promises";import fs from"node:os";import wr from"node:path";import At from"node:process";import{EventEmitter as vd}from"node:events";import ut from"node:process";import gs from"node:process";import Cr from"node:process";import Dn from"node:process";import nn from"node:process";import bs from"node:tty";import su,{stdin as qf,stdout as Gf}from"node:process";class ws{configPath;config=null;events=[];retryCount=0;maxRetries=3;retryDelayMs=1000;constructor(){let r=fs.homedir(),e=wr.join(r,".config","clapp");this.configPath=wr.join(e,"telemetry.json")}async isEnabled(){if(At.env.DO_NOT_TRACK==="1"||At.env.DO_NOT_TRACK==="true")return!1;if(At.env.NO_TELEMETRY==="1"||At.env.NO_TELEMETRY==="true")return!1;return(await this.loadConfig()).enabled}async enable(){let r=await this.loadConfig();if(r.enabled=!0,!r.userId)r.userId=this.generateUserId();await this.saveConfig(r)}async disable(){let r=await this.loadConfig();r.enabled=!1,await this.saveConfig(r)}async track(r,e){if(!await this.isEnabled())return;let n={event:r,...e,timestamp:Date.now(),platform:fs.platform(),nodeVersion:At.version};if(this.events.push(n),this.events.length>=10)await this.send()}async trackCommand(r,e){await this.track("command",{command:r,duration:e})}async trackError(r,e){await this.track("error",{error:r,command:e})}async send(){if(!await this.isEnabled()||this.events.length===0)return!0;try{this.events=[],this.retryCount=0;let e=await this.loadConfig();return e.lastSent=Date.now(),await this.saveConfig(e),!0}catch{if(this.retryCount<this.maxRetries){this.retryCount++;let e=this.retryDelayMs*2**(this.retryCount-1);return await this.sleep(e),this.send()}return this.events=[],this.retryCount=0,!1}}async flush(){if(this.events.length===0)return!0;return this.send()}sleep(r){return new Promise((e)=>setTimeout(e,r))}async status(){let r=await this.loadConfig();return{enabled:r.enabled,doNotTrack:At.env.DO_NOT_TRACK==="1"||At.env.DO_NOT_TRACK==="true",eventsQueued:this.events.length,lastSent:r.lastSent}}async loadConfig(){if(this.config)return this.config;try{if(yd(this.configPath)){let r=await br.readFile(this.configPath,"utf-8");return this.config=JSON.parse(r),this.config}}catch{}return this.config={enabled:!1},this.config}async saveConfig(r){this.config=r;try{let e=wr.dirname(this.configPath);await br.mkdir(e,{recursive:!0}),await br.writeFile(this.configPath,JSON.stringify(r,null,2),"utf-8")}catch{}}generateUserId(){let r=Math.random().toString(36).substring(2,15),e=Date.now().toString(36);return`${r}-${e}`}}var Tf=new ws;function bd(r,e={}){let t={_:[]},n=e.alias||{},i=new Set(e.boolean||[]),o={};for(let c of Object.keys(n))for(let l of n[c])o[l]=c;for(let c of i)if(n[c])for(let l of n[c])i.add(l);function s(c,l){let d=o[c]||c;if(t[d]=l,n[d])for(let u of n[d])t[u]=l;if(o[c]&&n[o[c]])for(let u of n[o[c]])t[u]=l;t[c]=l}for(let c=0;c<r.length;c++){let l=r[c];if(l==="--"){t._.push(...r.slice(c+1));break}if(l.startsWith("--")){let d=l.indexOf("=");if(d!==-1){let u=l.slice(2,d),m=l.slice(d+1);s(u,m)}else{let u=l.slice(2);if(u.startsWith("no-")){let f=u.slice(3);s(f,!1);continue}let m=o[u]||u;if(i.has(m)||i.has(u))s(u,!0);else{let f=r[c+1];if(f!==void 0&&!f.startsWith("-"))s(u,f),c++;else s(u,!0)}}}else if(l.startsWith("-")&&l.length>1){let d=l.slice(1);for(let u=0;u<d.length;u++){let m=d[u],f=o[m]||m;if(u===d.length-1&&!i.has(f)&&!i.has(m)){let h=r[c+1];if(h!==void 0&&!h.startsWith("-"))s(m,h),c++;else s(m,!0)}else s(m,!0)}}else t._.push(l)}return t}function $r(r){return r.replace(/[<[].+/,"").trim()}function wd(r){let e=/<([^>]+)>/g,t=/\[([^\]]+)\]/g,n=[],i=(c)=>{let l=!1,d=c[1];if(d.startsWith("..."))d=d.slice(3),l=!0;return{required:c[0].startsWith("<"),value:d,variadic:l}},o;while(o=e.exec(r))n.push(i(o));let s;while(s=t.exec(r))n.push(i(s));return n}function Sd(r){let e={alias:{},boolean:[]};for(let[t,n]of r.entries()){if(n.names.length>1)e.alias[n.names[0]]=n.names.slice(1);if(n.isBoolean)if(n.negated){if(!r.some((o,s)=>s!==t&&o.names.some((c)=>n.names.includes(c))&&typeof o.required==="boolean"))e.boolean.push(n.names[0])}else e.boolean.push(n.names[0])}return e}function hs(r){return r.reduce((e,t)=>e.length>=t.length?e:t,"")}function Sr(r,e){return r.length>=e?r:`${r}${" ".repeat(e-r.length)}`}function $d(r){return r.replace(/([a-z])-([a-z])/g,(e,t,n)=>t+n.toUpperCase())}function Cd(r,e,t){let n=0,i=e.length,o=r,s;for(;n<i;++n)s=o[e[n]],o=o[e[n]]=n===i-1?t:s!=null?s:!!~e[n+1].indexOf(".")||!(+e[n+1]>-1)?{}:[]}function Ad(r,e){for(let t of Object.keys(e)){let n=e[t];if(n.shouldTransform){if(r[t]=Array.prototype.concat.call([],r[t]),typeof n.transformFunction==="function")r[t]=r[t].map(n.transformFunction)}}}function kd(r){let e=/([^\\/]+)$/.exec(r);return e?e[1]:""}function Ss(r){return r.split(".").map((e,t)=>t===0?$d(e):e).join(".")}class En extends Error{exitCode=2;isUsageError=!0;constructor(r){super(r);if(this.name=this.constructor.name,typeof Error.captureStackTrace==="function")Error.captureStackTrace(this,this.constructor);else this.stack=Error(r).stack}format(r=!1){if(r&&this.stack)return`${this.message}
3
3
 
4
4
  Stack trace:
5
- ${this.stack}`;return this.message}}function pY(){let{env:G}=$4,{TERM:$,TERM_PROGRAM:J}=G;if($4.platform!=="win32")return $!=="linux";return Boolean(G.WT_SESSION)||Boolean(G.TERMINUS_SUBLIME)||G.ConEmuTask==="{cmd::Cmder}"||J==="Terminus-Sublime"||J==="vscode"||$==="xterm-256color"||$==="alacritty"||$==="rxvt-unicode"||$==="rxvt-unicode-256color"||G.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var D1="\x1B",J$=`${D1}[`;var p$={to(G,$){if(!$)return`${J$}${G+1}G`;return`${J$}${$+1};${G+1}H`},move(G,$){let J="";if(G<0)J+=`${J$}${-G}D`;else if(G>0)J+=`${J$}${G}C`;if($<0)J+=`${J$}${-$}A`;else if($>0)J+=`${J$}${$}B`;return J},up:(G=1)=>`${J$}${G}A`,down:(G=1)=>`${J$}${G}B`,forward:(G=1)=>`${J$}${G}C`,backward:(G=1)=>`${J$}${G}D`,nextLine:(G=1)=>`${J$}E`.repeat(G),prevLine:(G=1)=>`${J$}F`.repeat(G),left:`${J$}G`,hide:`${J$}?25l`,show:`${J$}?25h`,save:`${D1}7`,restore:`${D1}8`};var H6={screen:`${J$}2J`,up:(G=1)=>`${J$}1J`.repeat(G),down:(G=1)=>`${J$}J`.repeat(G),line:`${J$}2K`,lineEnd:`${J$}K`,lineStart:`${J$}1K`,lines(G){let $="";for(let J=0;J<G;J++)$+=this.line+(J<G-1?p$.up():"");if(G)$+=p$.left;return $}},tQ={screen:`${D1}c`};function nY(G,$){let J=[];for(let Y=0;Y<=$.length;Y++)J[Y]=[Y];for(let Y=0;Y<=G.length;Y++)J[0][Y]=Y;for(let Y=1;Y<=$.length;Y++)for(let X=1;X<=G.length;X++)if($.charAt(Y-1)===G.charAt(X-1))J[Y][X]=J[Y-1][X-1];else J[Y][X]=Math.min(J[Y-1][X-1]+1,J[Y][X-1]+1,J[Y-1][X]+1);return J[$.length][G.length]}function F4(G,$,J=2,Y=3){return $.map((X)=>({cmd:X,distance:nY(G,X)})).filter(({distance:X})=>X<=J).sort((X,Q)=>X.distance-Q.distance).slice(0,Y).map(({cmd:X})=>X)}class U4{rawName;description;name;names;isBoolean;required;config;negated;constructor(G,$,J){if(this.rawName=G,this.description=$,this.config=Object.assign({},J),G=G.replace(/\.\*/g,""),this.negated=!1,this.names=K6(G).split(",").map((Y)=>{let X=Y.trim().replace(/^-{1,2}/,"");if(X.startsWith("no-"))this.negated=!0,X=X.replace(/^no-/,"");return z4(X)}).sort((Y,X)=>Y.length>X.length?1:-1),this.name=this.names[this.names.length-1],this.negated&&this.config.default==null)this.config.default=!0;if(G.includes("<"))this.required=!0;else if(G.includes("["))this.required=!1;else this.isBoolean=!0}}var oQ=j6.argv,sY=`${j6.platform}-${j6.arch} bun-v${typeof Bun<"u"?Bun.version:"unknown"}`,Y4=T1.argv,iY=`${T1.platform}-${T1.arch} node-${T1.version}`;class B6{rawName;description;config;cli;options;aliasNames;name;namespace;args;commandAction;usageText;versionNumber;examples;helpCallback;globalCommand;beforeHooks;afterHooks;middleware;constructor(G,$,J,Y){this.rawName=G,this.description=$,this.config=J,this.cli=Y,this.options=[],this.aliasNames=[],this.name=K6(G);let X=K6(G),Q=X.indexOf(":");if(Q>0)this.namespace=X.substring(0,Q),this.name=X.substring(Q+1);if(this.args=dY(G),this.examples=[],this.beforeHooks=[],this.afterHooks=[],this.middleware=[],!J)this.config={}}usage(G){return this.usageText=G,this}allowUnknownOptions(){return this.config.allowUnknownOptions=!0,this}ignoreOptionDefaultValue(){return this.config.ignoreOptionDefaultValue=!0,this}version(G,$="-v, --version"){return this.versionNumber=G,this.option($,"Display version number"),this}example(G){return this.examples.push(G),this}option(G,$,J){let Y=new U4(G,$,J);return this.options.push(Y),this}alias(G){return this.aliasNames.push(G),this}action(G){return this.commandAction=G,this}before(G){return this.beforeHooks.push(G),this}after(G){return this.afterHooks.push(G),this}use(G){return this.middleware.push(G),this}isMatched(G){if(this.aliasNames.includes(G))return!0;if(this.namespace)return`${this.namespace}:${this.name}`===G;return this.name===G}get isDefaultCommand(){return this.name===""||this.aliasNames.includes("!")}get isGlobalCommand(){return this instanceof V6}get displayName(){return this.namespace?`${this.namespace}:${this.name}`:this.name}hasOption(G){return G=G.split(".")[0],!!this.options.find(($)=>{return $.names.includes(G)})}outputHelp(){let{name:G,commands:$}=this.cli,{versionNumber:J,options:Y,helpCallback:X}=this.cli.globalCommand,Q=[{body:`${G}${J?`/${J}`:""}`}];if(Q.push({title:"Usage",body:` $ ${G} ${this.usageText||this.rawName}`}),(this.isGlobalCommand||this.isDefaultCommand)&&$.length>0){let F=J4($.map((V)=>V.rawName)),U=new Map,M=[];for(let V of $)if(V.namespace){if(!U.has(V.namespace))U.set(V.namespace,[]);U.get(V.namespace).push(V)}else M.push(V);let H="";if(M.length>0)H+=M.map((V)=>{return` ${M6(V.rawName,F.length)} ${V.description}`}).join(`
6
- `);let j=Array.from(U.keys()).sort();for(let V of j){let T=U.get(V);if(H.length>0)H+=`
7
-
8
- `;H+=` ${V}:
9
- `,H+=T.map((A)=>{return` ${M6(A.rawName,F.length-2)} ${A.description}`}).join(`
10
- `)}Q.push({title:"Commands",body:H}),Q.push({title:"For more info, run any command with the `--help` flag",body:$.map((V)=>` $ ${G}${V.displayName===""?"":` ${V.displayName}`} --help`).join(`
11
- `)})}let z=this.isGlobalCommand?Y:[...this.options,...Y||[]];if(!this.isGlobalCommand&&!this.isDefaultCommand)z=z.filter((F)=>F.name!=="version");if(z.length>0){let F=J4(z.map((U)=>U.rawName));Q.push({title:"Options",body:z.map((U)=>{return` ${M6(U.rawName,F.length)} ${U.description} ${U.config.default===void 0?"":`(default: ${U.config.default})`}`}).join(`
12
- `)})}if(this.examples.length>0)Q.push({title:"Examples",body:this.examples.map((F)=>{if(typeof F==="function")return F(G);return F}).join(`
13
- `)});if(X)Q=X(Q)||Q;console.log(Q.map((F)=>{return F.title?`${F.title}:
14
- ${F.body}`:F.body}).join(`
15
-
16
- `))}outputVersion(){let{name:G}=this.cli,{versionNumber:$}=this.cli.globalCommand;if($)console.log(`${G}/${$} ${typeof Bun<"u"?sY:iY}`)}checkRequiredArgs(){let G=this.args.filter(($)=>$.required).length;if(this.cli.args.length<G){let J=this.args.filter((X)=>X.required).slice(this.cli.args.length),Y=J.map((X)=>`<${X.value}>`).join(" ");throw new E1(`Missing required argument${J.length>1?"s":""}: ${Y}
17
-
18
- Run \`${this.cli.name} ${this.rawName} --help\` for usage information.`)}}checkUnknownOptions(){let{options:G,globalCommand:$}=this.cli;if(!this.config.allowUnknownOptions){for(let J of Object.keys(G))if(J!=="--"&&!this.hasOption(J)&&!$.hasOption(J)){let X=[...$.options,...this.options].flatMap((F)=>F.names),Q=J.length>1?`--${J}`:`-${J}`,Z=F4(J,X),z=`Unknown option \`${Q}\``;if(Z.length>0)z+=`
19
-
20
- Did you mean one of these?`,Z.forEach((F)=>{let U=F.length>1?`--${F}`:`-${F}`;z+=`
21
- • ${U}`});throw z+=`
22
-
23
- Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`,new E1(z)}}}checkOptionValue(){let{options:G,globalCommand:$}=this.cli,J=[...$.options,...this.options];for(let Y of J){let X=G[Y.name.split(".")[0]];if(Y.required){let Q=J.some((Z)=>Z.negated&&Z.names.includes(Y.name));if(X===!0||X===!1&&!Q)throw new E1(`Option \`${Y.rawName}\` requires a value.
24
-
25
- Example: ${this.cli.name} ${this.rawName} ${Y.rawName} <value>`)}}}}class V6 extends B6{constructor(G){super("@@global@@","",{},G)}}var tY=B6,F0={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 a0={primary:"blue",secondary:"cyan",success:"green",warning:"yellow",error:"red",info:"magenta",muted:"gray"};function eY(){return!0}function oY(){let G={};G.supportsColor=eY();function $(Y,X=[]){let Q=Y===""?[]:[...X,Y],Z=function(F){if(!G.supportsColor)return F;let U="",M="";for(let H of Q)if(H in a0&&a0[H]in F0){let j=a0[H];U+=F0[j][0],M=F0[j][1]+M}else if(H in F0)U+=F0[H][0],M=F0[H][1]+M;return U+F+M},z=[...Object.keys(F0),...Object.keys(a0)];for(let F of z)if(!(F in Z))Object.defineProperty(Z,F,{get(){return $(F,Q)}});return Z}let J=[...Object.keys(F0),...Object.keys(a0)];for(let Y of J)if(!(Y in G))Object.defineProperty(G,Y,{get(){return $(Y)}});return G}var p0=oY();class A6 extends vY{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(G=""){super();this.name=G,this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.globalCommand=new V6(this),this.globalCommand.usage("<command> [options]")}handleSignals(G){if(this.signalHandlersSet)return this;let $=async(J)=>{if(console.log(`
26
-
27
- Received ${J}, cleaning up...`),G)try{await G()}catch(Y){console.error("Error during cleanup:",Y)}z0.exit(0)};return z0.on("SIGINT",()=>$("SIGINT")),z0.on("SIGTERM",()=>$("SIGTERM")),this.signalHandlersSet=!0,this}didYouMean(G=!0){return this.enableDidYouMean=G,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(G){return this.globalCommand.usage(G),this}command(G,$,J){if(!J)J={};let Y=new tY(G,$||"",J,this);return Y.globalCommand=this.globalCommand,this.commands.push(Y),Y}option(G,$,J){return this.globalCommand.option(G,$,J),this}help(G){return this.globalCommand.option("-h, --help","Display this message"),this.globalCommand.helpCallback=G,this.showHelpOnExit=!0,this}version(G,$="-v, --version"){return this.globalCommand.version(G,$),this.showVersionOnExit=!0,this}example(G){return this.globalCommand.example(G),this}outputHelp(){if(this.matchedCommand)this.matchedCommand.outputHelp();else this.globalCommand.outputHelp()}outputVersion(){this.globalCommand.outputVersion()}setParsedInfo({args:G,options:$},J,Y){if(this.args=G,this.options=$,J)this.matchedCommand=J;if(Y)this.matchedCommandName=Y;return this}unsetMatchedCommand(){this.matchedCommand=void 0,this.matchedCommandName=void 0}showCommandNotFound(G){if(console.log(p0.red(`
28
- ✗ Command "${G}" not found.
29
- `)),this.enableDidYouMean){let $=[];for(let Y of this.commands){if(Y.displayName)$.push(Y.displayName);if(Y.aliasNames)$.push(...Y.aliasNames)}let J=F4(G,$);if(J.length>0)console.log(p0.yellow("Did you mean one of these?")),J.forEach((Y)=>console.log(` ${p0.dim("•")} ${this.name} ${Y}`)),console.log("")}console.log(p0.dim("Run"),`${this.name} --help`,p0.dim("to see all available commands")),z0.exit(1)}async parse(G=Y4,$={}){let{run:J=!0,exitOnError:Y=!1}=$;if(Y)try{return await this.parse(G,{run:J})}catch(F){throw this.handleUsageError(F),F}if(this.rawArgs=G,!this.name)this.name=G[1]?aY(G[1]):"cli";let X=!0,Q=G.slice(2),Z=Q[0];if(Z&&!Z.startsWith("-")){for(let F of this.commands)if(F.isMatched(Z)){let U=this.mri(Q,F);X=!1;let M={...U,args:U.args.slice(1)};this.setParsedInfo(M,F,Z),this.emit(`command:${Z}`,F);break}}if(X){for(let F of this.commands)if(F.name===""){X=!1;let U=this.mri(Q,F);this.setParsedInfo(U,F),this.emit("command:!",F);break}}if(X){let F=this.mri(G.slice(2));this.setParsedInfo(F)}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.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!==void 0)this.useEmoji=!this.options.noEmoji;if(this.options.theme)this.theme=String(this.options.theme);if(this.options.noCache!==void 0)this.isNoCache=Boolean(this.options.noCache);if(this.options.help&&this.showHelpOnExit)this.outputHelp(),J=!1,this.unsetMatchedCommand();if(this.options.version&&this.showVersionOnExit&&this.matchedCommandName==null)this.outputVersion(),J=!1,this.unsetMatchedCommand();let z={args:this.args,options:this.options};if(J)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(G,$){let J=[...this.globalCommand.options,...$?$.options:[]],Y=cY(J),X=[],Q=G.indexOf("--");if(Q>-1)X=G.slice(Q+1),G=G.slice(0,Q);let Z=mY(G,Y),z={_:Z._};for(let j of Object.keys(Z))if(j!=="_")z[z4(j)]=Z[j];let F=z._,U={"--":X},M=$&&$.config.ignoreOptionDefaultValue?$.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,H=Object.create(null);for(let j of J){if(!M&&j.config.default!==void 0)for(let V of j.names)U[V]=j.config.default;if(Array.isArray(j.config.type)){if(H[j.name]===void 0)H[j.name]={shouldTransform:!0,transformFunction:j.config.type[0]}}}for(let j of Object.keys(z))if(j!=="_"){let V=j.split(".");lY(U,V,z[j]),rY(U,H)}return{args:F,options:U}}async run(G=Y4){return this.parse(G,{run:!0,exitOnError:!0})}handleUsageError(G){if(!(!!G&&typeof G==="object"&&G.name==="ClappError"&&G.isUsageError!==!1))return;let Y=G,X=Y.message??"command-line error",Q=this.name?`${this.name}: `:"",Z=/--help/.test(X)?"":`
30
- Run \`${this.name??"cli"} --help\` for usage.`;z0.stderr.write(`${Q}${X}${Z}
31
- `),z0.exit(Y.exitCode??2)}async runMatchedCommand(){let{args:G,options:$,matchedCommand:J}=this;if(!J||!J.commandAction)return;J.checkUnknownOptions(),J.checkOptionValue(),J.checkRequiredArgs();let Y=[];J.args.forEach((z,F)=>{if(z.variadic)Y.push(G.slice(F));else Y.push(G[F])}),Y.push($);let X={command:J,args:Y,options:$};for(let z of J.beforeHooks)await z(X);let Q,Z=async()=>{let z=J.commandAction.apply(this,Y);if(z instanceof Promise)Q=await z;else Q=z;return Q};if(J.middleware.length>0){let z=0,F=async()=>{if(z<J.middleware.length){let U=J.middleware[z++];await U({...X,next:F})}else await Z()};await F()}else await Z();for(let z of J.afterHooks)await z(X);return Q}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return z0.removeAllListeners("SIGINT"),z0.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 M4{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(G){if(!this.enabled){this.misses++;return}let $=this.cache.get(G);if(!$){this.misses++;return}if(Date.now()-$.timestamp>$.ttl){this.cache.delete(G),this.misses++;return}return this.hits++,$.value}set(G,$,J=5000){if(!this.enabled)return;this.cache.set(G,{value:$,timestamp:Date.now(),ttl:J})}has(G){if(!this.enabled)return!1;let $=this.cache.get(G);if(!$)return!1;if(Date.now()-$.timestamp>$.ttl)return this.cache.delete(G),!1;return!0}delete(G){this.cache.delete(G)}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 G=Date.now();for(let[$,J]of this.cache.entries())if(G-J.timestamp>J.ttl)this.cache.delete($)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var JZ=new M4;function $8(){if("FORCE_COLOR"in n0.env)return n0.env.FORCE_COLOR!=="0";if("NO_COLOR"in n0.env||n0.env.TERM==="dumb")return!1;if(n0.platform==="win32")return!0;return G4.isatty(1)&&G4.isatty(2)}var H4=$8();function o(G,$){if(!H4)return(J)=>J;return(J)=>G+J+$}var J8=o("\x1B[0m","\x1B[0m"),Y8=o("\x1B[31m","\x1B[39m"),G8=o("\x1B[32m","\x1B[39m"),X8=o("\x1B[33m","\x1B[39m"),Q8=o("\x1B[34m","\x1B[39m"),Z8=o("\x1B[35m","\x1B[39m"),W8=o("\x1B[36m","\x1B[39m"),z8=o("\x1B[37m","\x1B[39m"),F8=o("\x1B[90m","\x1B[39m"),U8=o("\x1B[1m","\x1B[22m"),M8=o("\x1B[3m","\x1B[23m"),H8=o("\x1B[4m","\x1B[24m"),K8=o("\x1B[2m","\x1B[22m"),j8=o("\x1B[7m","\x1B[27m"),B8=o("\x1B[8m","\x1B[28m"),V8=o("\x1B[9m","\x1B[29m"),A8=o("\x1B[41m","\x1B[49m"),q8=o("\x1B[42m","\x1B[49m"),R8=o("\x1B[43m","\x1B[49m"),L8=o("\x1B[44m","\x1B[49m"),E8=o("\x1B[45m","\x1B[49m"),T8=o("\x1B[46m","\x1B[49m"),O8=o("\x1B[47m","\x1B[49m"),w8=H4,D8={reset:J8,red:Y8,green:G8,yellow:X8,blue:Q8,magenta:Z8,cyan:W8,white:z8,gray:F8,bold:U8,italic:M8,underline:H8,dim:K8,inverse:j8,hidden:B8,strikethrough:V8,bgRed:A8,bgGreen:q8,bgYellow:R8,bgBlue:L8,bgMagenta:E8,bgCyan:T8,bgWhite:O8,isColorSupported:w8},O1=D8,N8=["up","down","left","right","space","enter","cancel"],w1={actions:new Set(N8),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};function K4(G,$){if(typeof G==="string")return w1.aliases.get(G)===$;for(let J of G){if(J===void 0)continue;if(K4(J,$))return!0}return!1}function P8(G,$){if(G===$)return[];let J=G.split(`
32
- `),Y=$.split(`
33
- `),X=[];for(let Q=0;Q<Math.max(J.length,Y.length);Q++)if(J[Q]!==Y[Q])X.push(Q);return X}var WZ=x8.platform.startsWith("win"),X4=Symbol("clapp:cancel");function L1(G,$){let J=G;if(J.isTTY)J.setRawMode($)}function I8(G){return G.replace(/\x1b\[[0-9;]*m/g,"")}function k8(G){return I8(G).length}function Z4(G,$,J){if(!$||$<1)return G;let Y=J?.hard??!1,X=J?.trim??!0,Q=G.split(`
34
- `),Z=[];for(let z of Q){if(k8(z)<=$){Z.push(X?z.trimEnd():z);continue}if(Y){let F="",U=0,M=0;while(M<z.length){let H=z.slice(M).match(/^\x1b\[[0-9;]*m/);if(H){F+=H[0],M+=H[0].length;continue}if(U>=$)Z.push(X?F.trimEnd():F),F="",U=0;F+=z[M],U++,M++}if(F)Z.push(X?F.trimEnd():F)}else Z.push(X?z.trimEnd():z)}return Z.join(`
35
- `)}class q6{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;_manualLine="";state="initial";error="";value;userInput="";constructor(G,$=!0){let{input:J=S8,output:Y=_8,render:X,signal:Q,...Z}=G;this.opts=Z,this.onKeypress=this.onKeypress.bind(this),this.close=this.close.bind(this),this.render=this.render.bind(this),this._render=X.bind(this),this._track=$,this._abortSignal=Q,this.input=J,this.output=Y}unsubscribe(){this._subscribers.clear()}setSubscriber(G,$){let J=this._subscribers.get(G)??[];J.push($),this._subscribers.set(G,J)}on(G,$){return this.setSubscriber(G,{cb:$}),this}once(G,$){return this.setSubscriber(G,{cb:$,once:!0}),this}emit(G,...$){let J=this._subscribers.get(G)??[],Y=[];for(let X of J)if(X.cb(...$),X.once)Y.push(()=>J.splice(J.indexOf(X),1));for(let X of Y)X();return this}prompt(){return new Promise((G)=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),G(X4);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}if(this.rl=b8.createInterface({input:this.input,tabSize:2,prompt:"",escapeCodeTimeout:50,terminal:!0}),this.rl.prompt(),this.opts.initialUserInput!==void 0)this._setUserInput(this.opts.initialUserInput,!0);this.input.on("keypress",this.onKeypress),L1(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(p$.show),this.output.off("resize",this.render),L1(this.input,!1),G(this.value)}),this.once("cancel",()=>{this.output.write(p$.show),this.output.off("resize",this.render),L1(this.input,!1),G(X4)})})}_isActionKey(G,$){return G==="\t"}_setValue(G){this.value=G,this.emit("value",this.value)}_setUserInput(G,$){if(this.userInput=G??"",this.emit("userInput",this.userInput),$&&this._track&&this.rl)this.rl.write(this.userInput),this._cursor=this.rl.cursor}onKeypress(G,$){if(this._track&&$.name!=="return"){if($.name&&this._isActionKey(G,$))this.rl?.write(null,{ctrl:!0,name:"h"});this._cursor=this.rl?.cursor??0;let J=$.name==="tab"||$.name==="escape"||$.name==="backspace"||$.name==="delete"||$.name==="enter"||$.name==="return"||$.name&&["up","down","left","right"].includes($.name);if(G&&!J&&G.length===1&&G>=" ")this._manualLine+=G;else if($.name==="backspace"&&this._manualLine.length>0)this._manualLine=this._manualLine.slice(0,-1);let Y=this._manualLine.length>=(this.rl?.line?.length||0)?this._manualLine:this.rl?.line;this._setUserInput(Y)}if(this.state==="error")this.state="active",this.error="";if($?.name){if(!this._track&&w1.aliases.has($.name))this.emit("cursor",w1.aliases.get($.name));if(w1.actions.has($.name))this.emit("cursor",$.name)}if(G&&(G.toLowerCase()==="y"||G.toLowerCase()==="n"))this.emit("confirm",G.toLowerCase()==="y");if(this.emit("key",G?.toLowerCase(),$),$?.name==="return"){if(this.opts.validate){let J=this.opts.validate(this.value);if(J)this.error=J instanceof Error?J.message:J,this.state="error"}if(this.state!=="error")this.state="submit"}if(K4([G,$?.name,$?.sequence],"cancel"))this.state="cancel";if(this.state==="submit"||this.state==="cancel")this.emit("finalize");if(this.render(),this.state==="submit"||this.state==="cancel")this.close()}close(){if(this.input.unpipe(),this.input.removeListener("keypress",this.onKeypress),this.output.write(`
36
- `),L1(this.input,!1),this.rl?.close(),this.rl=void 0,this.emit(`${this.state}`,this.value),this.state==="cancel")setTimeout(()=>{this.unsubscribe()},10);else this.unsubscribe()}restoreCursor(){let G=Z4(this._prevFrame,Q4.stdout.columns,{hard:!0,trim:!1}).split(`
37
- `).length-1;this.output.write(p$.move(-999,G*-1))}render(){let G=Z4(this._render(this)??"",Q4.stdout.columns,{hard:!0,trim:!1});if(G===this._prevFrame)return;if(this.state==="initial")this.output.write(p$.hide);else{let $=P8(this._prevFrame,G);if(this.restoreCursor(),$&&$?.length===1){let J=$[0];this.output.write(p$.move(0,J)),this.output.write(H6.lines(1));let Y=G.split(`
38
- `);this.output.write(Y[J]),this._prevFrame=G,this.output.write(p$.move(0,Y.length-J-1));return}if($&&$?.length>1){let J=$[0];this.output.write(p$.move(0,J)),this.output.write(H6.down());let X=G.split(`
39
- `).slice(J);this.output.write(X.join(`
40
- `)),this._prevFrame=G;return}this.output.write(H6.down())}if(this.output.write(G),this.state==="initial")this.state="active";this._prevFrame=G}}function h8(G,$){if(G===void 0)return 0;if($.length===0)return 0;let Y=$.findIndex((X)=>X.value===G);return Y!==-1?Y:0}function y8(G,$){return($.label??String($.value)).toLowerCase().includes(G.toLowerCase())}function C8(G,$){if(!$)return;if(G)return $;return $[0]}class f8 extends q6{filteredOptions;multiple;isNavigating=!1;selectedValues=[];focusedValue;#$=0;#Y="";#G;#J;get cursor(){return this.#$}get userInputWithCursor(){if(!this.userInput)return O1.inverse(O1.hidden("_"));if(this._cursor>=this.userInput.length)return`${this.userInput}█`;let G=this.userInput.slice(0,this._cursor),[$,...J]=this.userInput.slice(this._cursor);return`${G}${O1.inverse($)}${J.join("")}`}get options(){if(typeof this.#J==="function")return this.#J();return this.#J}constructor(G){super(G);this.#J=G.options;let $=this.options;this.filteredOptions=[...$],this.multiple=G.multiple===!0,this.#G=G.filter??y8;let J;if(G.initialValue&&Array.isArray(G.initialValue))if(this.multiple)J=G.initialValue;else J=G.initialValue.slice(0,1);else if(!this.multiple&&this.options.length>0)J=[this.options[0].value];if(J)for(let Y of J){let X=$.findIndex((Q)=>Q.value===Y);if(X!==-1)this.toggleSelected(Y),this.#$=X}this.focusedValue=this.options[this.#$]?.value,this.on("key",(Y,X)=>this.#X(Y,X)),this.on("userInput",(Y)=>this.#Q(Y))}_isActionKey(G,$){return G==="\t"||this.multiple&&this.isNavigating&&$.name==="space"&&G!==void 0&&G!==""}#X(G,$){let J=$.name==="up",Y=$.name==="down",X=$.name==="return";if(J||Y){if(this.#$=Math.max(0,Math.min(this.#$+(J?-1:1),this.filteredOptions.length-1)),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)this.selectedValues=[this.focusedValue];this.isNavigating=!0}else if(X)this.value=C8(this.multiple,this.selectedValues);else if(this.multiple)if(this.focusedValue!==void 0&&($.name==="tab"||this.isNavigating&&$.name==="space"))this.toggleSelected(this.focusedValue);else this.isNavigating=!1;else{if(this.focusedValue)this.selectedValues=[this.focusedValue];this.isNavigating=!1}}deselectAll(){this.selectedValues=[]}toggleSelected(G){if(this.filteredOptions.length===0)return;if(this.multiple)if(this.selectedValues.includes(G))this.selectedValues=this.selectedValues.filter(($)=>$!==G);else this.selectedValues=[...this.selectedValues,G];else this.selectedValues=[G]}#Q(G){if(G!==this.#Y){this.#Y=G;let $=this.options;if(G)this.filteredOptions=$.filter((J)=>this.#G(G,J));else this.filteredOptions=[...$];if(this.#$=h8(this.focusedValue,this.filteredOptions),this.focusedValue=this.filteredOptions[this.#$]?.value,!this.multiple)if(this.focusedValue!==void 0)this.toggleSelected(this.focusedValue);else this.deselectAll()}}}class g8 extends q6{options;cursor=0;#$;getGroupItems(G){return this.options.filter(($)=>$.group===G)}isGroupSelected(G){let $=this.getGroupItems(G),J=this.value;if(J===void 0)return!1;return $.every((Y)=>J.includes(Y.value))}toggleValue(){let G=this.options[this.cursor];if(this.value===void 0)this.value=[];if(G.group===!0){let $=String(G.value),J=this.getGroupItems($);if(this.isGroupSelected($))this.value=this.value.filter((Y)=>J.findIndex((X)=>X.value===Y)===-1);else this.value=[...this.value,...J.map((Y)=>Y.value)];this.value=Array.from(new Set(this.value))}else{let $=this.value.includes(G.value);this.value=$?this.value.filter((J)=>J!==G.value):[...this.value,G.value]}}constructor(G){super(G,!1);let{options:$}=G;this.#$=G.selectableGroups!==!1,this.options=Object.entries($).flatMap(([J,Y])=>[{value:J,group:!0,label:J},...Y.map((X)=>({...X,group:J}))]),this.value=[...G.initialValues??[]],this.cursor=Math.max(this.options.findIndex(({value:J})=>J===G.cursorAt),this.#$?0:1),this.on("cursor",(J)=>{switch(J){case"left":case"up":{this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;let Y=this.options[this.cursor]?.group===!0;if(!this.#$&&Y)this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;break}case"down":case"right":{this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;let Y=this.options[this.cursor]?.group===!0;if(!this.#$&&Y)this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;break}case"space":this.toggleValue();break}})}}var v8=pY();var t=(G,$)=>v8?G:$,UZ=t("◆","*"),MZ=t("■","x"),HZ=t("▲","x"),KZ=t("◇","o"),jZ=t("┌","T"),m8=t("│","|"),BZ=t("└","—"),VZ=t("●",">"),AZ=t("○"," "),qZ=t("◻","[•]"),RZ=t("◼","[+]"),LZ=t("◻","[ ]"),EZ=t("▪","•"),TZ=t("─","-"),OZ=t("╮","+"),wZ=t("├","+"),DZ=t("╯","+"),xZ=t("●","•"),NZ=t("◆","*"),PZ=t("▲","!"),SZ=t("■","x");var _Z={light:t("─","-"),heavy:t("━","="),block:t("█","#")};function d8(){return`${O1.gray(m8)} `}var bZ=d8();var R6="0.9.4";import{existsSync as L6}from"node:fs";import{mkdir as u8,writeFile as j4}from"node:fs/promises";function T6(G){G.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($)=>{if(R("Initializing ts-cloud Project"),L6("cloud.config.ts")){if(!await k("cloud.config.ts already exists. Overwrite?",!1)){W("Initialization cancelled");return}}let J=$?.name||await p("Project name","my-app"),Y=$?.mode||await E6("Select deployment mode",["serverless","server"]),X=$?.region||await E6("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),Q=new E("Creating configuration file...");Q.start();let Z=`import { defineConfig } from '@ts-cloud/core'
5
+ ${this.stack}`;return this.message}}function Rd(){let{env:r}=gs,{TERM:e,TERM_PROGRAM:t}=r;if(gs.platform!=="win32")return e!=="linux";return Boolean(r.WT_SESSION)||Boolean(r.TERMINUS_SUBLIME)||r.ConEmuTask==="{cmd::Cmder}"||t==="Terminus-Sublime"||t==="vscode"||e==="xterm-256color"||e==="alacritty"||e==="rxvt-unicode"||e==="rxvt-unicode-256color"||r.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var Tn="\x1B",re=`${Tn}[`;var ys={to(r,e){if(!e)return`${re}${r+1}G`;return`${re}${e+1};${r+1}H`},move(r,e){let t="";if(r<0)t+=`${re}${-r}D`;else if(r>0)t+=`${re}${r}C`;if(e<0)t+=`${re}${-e}A`;else if(e>0)t+=`${re}${e}B`;return t},up:(r=1)=>`${re}${r}A`,down:(r=1)=>`${re}${r}B`,forward:(r=1)=>`${re}${r}C`,backward:(r=1)=>`${re}${r}D`,nextLine:(r=1)=>`${re}E`.repeat(r),prevLine:(r=1)=>`${re}F`.repeat(r),left:`${re}G`,hide:`${re}?25l`,show:`${re}?25h`,save:`${Tn}7`,restore:`${Tn}8`};var jf={screen:`${re}2J`,up:(r=1)=>`${re}1J`.repeat(r),down:(r=1)=>`${re}J`.repeat(r),line:`${re}2K`,lineEnd:`${re}K`,lineStart:`${re}1K`,lines(r){let e="";for(let t=0;t<r;t++)e+=this.line+(t<r-1?ys.up():"");if(r)e+=ys.left;return e}},Ff={screen:`${Tn}c`};function Id(r,e){let t=[];for(let n=0;n<=e.length;n++)t[n]=[n];for(let n=0;n<=r.length;n++)t[0][n]=n;for(let n=1;n<=e.length;n++)for(let i=1;i<=r.length;i++)if(e.charAt(n-1)===r.charAt(i-1))t[n][i]=t[n-1][i-1];else t[n][i]=Math.min(t[n-1][i-1]+1,t[n][i-1]+1,t[n-1][i]+1);return t[e.length][r.length]}function $s(r,e,t=2,n=3){return e.map((i)=>({cmd:i,distance:Id(r,i)})).filter(({distance:i})=>i<=t).sort((i,o)=>i.distance-o.distance).slice(0,n).map(({cmd:i})=>i)}class Cs{rawName;description;name;names;isBoolean;required;config;negated;constructor(r,e,t){if(this.rawName=r,this.description=e,this.config=Object.assign({},t),r=r.replace(/\.\*/g,""),this.negated=!1,this.names=$r(r).split(",").map((n)=>{let i=n.trim().replace(/^-{1,2}/,"");if(i.startsWith("no-"))this.negated=!0,i=i.replace(/^no-/,"");return Ss(i)}).sort((n,i)=>n.length>i.length?1:-1),this.name=this.names[this.names.length-1],this.negated&&this.config.default==null)this.config.default=!0;if(r.includes("<"))this.required=!0;else if(r.includes("["))this.required=!1;else this.isBoolean=!0}}var Bf=Cr.argv,Pd=`${Cr.platform}-${Cr.arch} bun-v${typeof Bun<"u"?Bun.version:"unknown"}`,vs=Dn.argv,xd=`${Dn.platform}-${Dn.arch} node-${Dn.version}`;class Ar{rawName;description;config;cli;options;aliasNames;name;namespace;args;commandAction;usageText;versionNumber;examples;helpCallback;globalCommand;beforeHooks;afterHooks;middleware;constructor(r,e,t,n){this.rawName=r,this.description=e,this.config=t,this.cli=n,this.options=[],this.aliasNames=[],this.name=$r(r);let i=$r(r),o=i.indexOf(":");if(o>0)this.namespace=i.substring(0,o),this.name=i.substring(o+1);if(this.args=wd(r),this.examples=[],this.beforeHooks=[],this.afterHooks=[],this.middleware=[],!t)this.config={}}usage(r){return this.usageText=r,this}allowUnknownOptions(){return this.config.allowUnknownOptions=!0,this}ignoreOptionDefaultValue(){return this.config.ignoreOptionDefaultValue=!0,this}version(r,e="-v, --version"){return this.versionNumber=r,this.option(e,"Display version number"),this}example(r){return this.examples.push(r),this}option(r,e,t){let n=new Cs(r,e,t);return this.options.push(n),this}alias(r){return this.aliasNames.push(r),this}action(r){return this.commandAction=r,this}before(r){return this.beforeHooks.push(r),this}after(r){return this.afterHooks.push(r),this}use(r){return this.middleware.push(r),this}isMatched(r){if(this.aliasNames.includes(r))return!0;if(this.namespace)return`${this.namespace}:${this.name}`===r;return this.name===r}get isDefaultCommand(){return this.name===""||this.aliasNames.includes("!")}get isGlobalCommand(){return this instanceof kr}get displayName(){return this.namespace?`${this.namespace}:${this.name}`:this.name}hasOption(r){return r=r.split(".")[0],!!this.options.find((e)=>e.names.includes(r))}outputHelp(){let{name:r,commands:e}=this.cli,{versionNumber:t,options:n,helpCallback:i}=this.cli.globalCommand,o=[{body:`${r}${t?`/${t}`:""}`}];if(o.push({title:"Usage",body:` $ ${r} ${this.usageText||this.rawName}`}),(this.isGlobalCommand||this.isDefaultCommand)&&e.length>0){let l=hs(e.map((h)=>h.rawName)),d=new Map,u=[];for(let h of e)if(h.namespace){if(!d.has(h.namespace))d.set(h.namespace,[]);d.get(h.namespace).push(h)}else u.push(h);let m="";if(u.length>0)m+=u.map((h)=>` ${Sr(h.rawName,l.length)} ${h.description}`).join(`
6
+ `);let f=Array.from(d.keys()).sort();for(let h of f){let S=d.get(h);if(m.length>0)m+=`
7
+
8
+ `;m+=` ${h}:
9
+ `,m+=S.map((y)=>` ${Sr(y.rawName,l.length-2)} ${y.description}`).join(`
10
+ `)}o.push({title:"Commands",body:m}),o.push({title:"For more info, run any command with the `--help` flag",body:e.map((h)=>` $ ${r}${h.displayName===""?"":` ${h.displayName}`} --help`).join(`
11
+ `)})}let c=this.isGlobalCommand?n:[...this.options,...n||[]];if(!this.isGlobalCommand&&!this.isDefaultCommand)c=c.filter((l)=>l.name!=="version");if(c.length>0){let l=hs(c.map((d)=>d.rawName));o.push({title:"Options",body:c.map((d)=>` ${Sr(d.rawName,l.length)} ${d.description} ${d.config.default===void 0?"":`(default: ${d.config.default})`}`).join(`
12
+ `)})}if(this.examples.length>0)o.push({title:"Examples",body:this.examples.map((l)=>{if(typeof l==="function")return l(r);return l}).join(`
13
+ `)});if(i)o=i(o)||o;console.log(o.map((l)=>l.title?`${l.title}:
14
+ ${l.body}`:l.body).join(`
15
+
16
+ `))}outputVersion(){let{name:r}=this.cli,{versionNumber:e}=this.cli.globalCommand;if(e)console.log(`${r}/${e} ${typeof Bun<"u"?Pd:xd}`)}checkRequiredArgs(){let r=this.args.filter((e)=>e.required).length;if(this.cli.args.length<r){let t=this.args.filter((i)=>i.required).slice(this.cli.args.length),n=t.map((i)=>`<${i.value}>`).join(" ");throw new En(`Missing required argument${t.length>1?"s":""}: ${n}
17
+
18
+ Run \`${this.cli.name} ${this.rawName} --help\` for usage information.`)}}checkUnknownOptions(){let{options:r,globalCommand:e}=this.cli;if(!this.config.allowUnknownOptions){for(let t of Object.keys(r))if(t!=="--"&&!this.hasOption(t)&&!e.hasOption(t)){let i=[...e.options,...this.options].flatMap((l)=>l.names),o=t.length>1?`--${t}`:`-${t}`,s=$s(t,i),c=`Unknown option \`${o}\``;if(s.length>0)c+=`
19
+
20
+ Did you mean one of these?`,s.forEach((l)=>{let d=l.length>1?`--${l}`:`-${l}`;c+=`
21
+ • ${d}`});throw c+=`
22
+
23
+ Run \`${this.cli.name} ${this.rawName} --help\` to see available options.`,new En(c)}}}checkOptionValue(){let{options:r,globalCommand:e}=this.cli,t=[...e.options,...this.options];for(let n of t){let i=r[n.name.split(".")[0]];if(n.required){let o=t.some((s)=>s.negated&&s.names.includes(n.name));if(i===!0||i===!1&&!o)throw new En(`Option \`${n.rawName}\` requires a value.
24
+
25
+ Example: ${this.cli.name} ${this.rawName} ${n.rawName} <value>`)}}}}class kr extends Ar{constructor(r){super("@@global@@","",{},r)}}var Ed=Ar,mt={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 en={primary:"blue",secondary:"cyan",success:"green",warning:"yellow",error:"red",info:"magenta",muted:"gray"};function Dd(){return!0}function Td(){let r={};r.supportsColor=Dd();function e(n,i=[]){let o=n===""?[]:[...i,n],s=function(l){if(!r.supportsColor)return l;let d="",u="";for(let m of o)if(m in en&&en[m]in mt){let f=en[m];d+=mt[f][0],u=mt[f][1]+u}else if(m in mt)d+=mt[m][0],u=mt[m][1]+u;return d+l+u},c=[...Object.keys(mt),...Object.keys(en)];for(let l of c)if(!(l in s))Object.defineProperty(s,l,{get(){return e(l,o)}});return s}let t=[...Object.keys(mt),...Object.keys(en)];for(let n of t)if(!(n in r))Object.defineProperty(r,n,{get(){return e(n)}});return r}var tn=Td();class Rr extends vd{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(r=""){super();this.name=r,this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.globalCommand=new kr(this),this.globalCommand.usage("<command> [options]")}handleSignals(r){if(this.signalHandlersSet)return this;let e=async(t)=>{if(console.log(`
26
+
27
+ Received ${t}, cleaning up...`),r)try{await r()}catch(n){console.error("Error during cleanup:",n)}ut.exit(0)};return ut.on("SIGINT",()=>e("SIGINT")),ut.on("SIGTERM",()=>e("SIGTERM")),this.signalHandlersSet=!0,this}didYouMean(r=!0){return this.enableDidYouMean=r,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(r){return this.globalCommand.usage(r),this}command(r,e,t){if(!t)t={};let n=new Ed(r,e||"",t,this);return n.globalCommand=this.globalCommand,this.commands.push(n),n}option(r,e,t){return this.globalCommand.option(r,e,t),this}help(r){return this.globalCommand.option("-h, --help","Display this message"),this.globalCommand.helpCallback=r,this.showHelpOnExit=!0,this}version(r,e="-v, --version"){return this.globalCommand.version(r,e),this.showVersionOnExit=!0,this}example(r){return this.globalCommand.example(r),this}outputHelp(){if(this.matchedCommand)this.matchedCommand.outputHelp();else this.globalCommand.outputHelp()}outputVersion(){this.globalCommand.outputVersion()}setParsedInfo({args:r,options:e},t,n){if(this.args=r,this.options=e,t)this.matchedCommand=t;if(n)this.matchedCommandName=n;return this}unsetMatchedCommand(){this.matchedCommand=void 0,this.matchedCommandName=void 0}showCommandNotFound(r){if(console.log(tn.red(`
28
+ ✗ Command "${r}" not found.
29
+ `)),this.enableDidYouMean){let e=[];for(let n of this.commands){if(n.displayName)e.push(n.displayName);if(n.aliasNames)e.push(...n.aliasNames)}let t=$s(r,e);if(t.length>0)console.log(tn.yellow("Did you mean one of these?")),t.forEach((n)=>console.log(` ${tn.dim("•")} ${this.name} ${n}`)),console.log("")}console.log(tn.dim("Run"),`${this.name} --help`,tn.dim("to see all available commands")),ut.exit(1)}async parse(r=vs,e={}){let{run:t=!0,exitOnError:n=!1}=e;if(n)try{return await this.parse(r,{run:t})}catch(l){throw this.handleUsageError(l),l}if(this.rawArgs=r,!this.name)this.name=r[1]?kd(r[1]):"cli";let i=!0,o=r.slice(2),s=o[0];if(s&&!s.startsWith("-")){for(let l of this.commands)if(l.isMatched(s)){let d=this.mri(o,l);i=!1;let u={...d,args:d.args.slice(1)};this.setParsedInfo(u,l,s),this.emit(`command:${s}`,l);break}}if(i){for(let l of this.commands)if(l.name===""){i=!1;let d=this.mri(o,l);this.setParsedInfo(d,l),this.emit("command:!",l);break}}if(i){let l=this.mri(r.slice(2));this.setParsedInfo(l)}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.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!==void 0)this.useEmoji=!this.options.noEmoji;if(this.options.theme)this.theme=String(this.options.theme);if(this.options.noCache!==void 0)this.isNoCache=Boolean(this.options.noCache);if(this.options.help&&this.showHelpOnExit)this.outputHelp(),t=!1,this.unsetMatchedCommand();if(this.options.version&&this.showVersionOnExit&&this.matchedCommandName==null)this.outputVersion(),t=!1,this.unsetMatchedCommand();let c={args:this.args,options:this.options};if(t)await this.runMatchedCommand();if(!this.matchedCommand&&this.args[0]){if(this.emit("command:*"),!(this.listenerCount("command:*")>0))this.showCommandNotFound(this.args[0])}return c}mri(r,e){let t=[...this.globalCommand.options,...e?e.options:[]],n=Sd(t),i=[],o=r.indexOf("--");if(o>-1)i=r.slice(o+1),r=r.slice(0,o);let s=bd(r,n),c={_:s._};for(let f of Object.keys(s))if(f!=="_")c[Ss(f)]=s[f];let l=c._,d={"--":i},u=e&&e.config.ignoreOptionDefaultValue?e.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,m=Object.create(null);for(let f of t){if(!u&&f.config.default!==void 0)for(let h of f.names)d[h]=f.config.default;if(Array.isArray(f.config.type)){if(m[f.name]===void 0)m[f.name]={shouldTransform:!0,transformFunction:f.config.type[0]}}}for(let f of Object.keys(c))if(f!=="_"){let h=f.split(".");Cd(d,h,c[f]),Ad(d,m)}return{args:l,options:d}}async run(r=vs){return this.parse(r,{run:!0,exitOnError:!0})}handleUsageError(r){if(!(!!r&&typeof r==="object"&&r.name==="ClappError"&&r.isUsageError!==!1))return;let n=r,i=n.message??"command-line error",o=this.name?`${this.name}: `:"",s=/--help/.test(i)?"":`
30
+ Run \`${this.name??"cli"} --help\` for usage.`;ut.stderr.write(`${o}${i}${s}
31
+ `),ut.exit(n.exitCode??2)}async runMatchedCommand(){let{args:r,options:e,matchedCommand:t}=this;if(!t||!t.commandAction)return;t.checkUnknownOptions(),t.checkOptionValue(),t.checkRequiredArgs();let n=[];t.args.forEach((c,l)=>{if(c.variadic)n.push(r.slice(l));else n.push(r[l])}),n.push(e);let i={command:t,args:n,options:e};for(let c of t.beforeHooks)await c(i);let o,s=async()=>{let c=t.commandAction.apply(this,n);if(c instanceof Promise)o=await c;else o=c;return o};if(t.middleware.length>0){let c=0,l=async()=>{if(c<t.middleware.length){let d=t.middleware[c++];await d({...i,next:l})}else await s()};await l()}else await s();for(let c of t.afterHooks)await c(i);return o}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return ut.removeAllListeners("SIGINT"),ut.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 As{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(r){if(!this.enabled){this.misses++;return}let e=this.cache.get(r);if(!e){this.misses++;return}if(Date.now()-e.timestamp>e.ttl){this.cache.delete(r),this.misses++;return}return this.hits++,e.value}set(r,e,t=5000){if(!this.enabled)return;this.cache.set(r,{value:e,timestamp:Date.now(),ttl:t})}has(r){if(!this.enabled)return!1;let e=this.cache.get(r);if(!e)return!1;if(Date.now()-e.timestamp>e.ttl)return this.cache.delete(r),!1;return!0}delete(r){this.cache.delete(r)}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 r=Date.now();for(let[e,t]of this.cache.entries())if(r-t.timestamp>t.ttl)this.cache.delete(e)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var zf=new As;function Nd(){if("FORCE_COLOR"in nn.env)return nn.env.FORCE_COLOR!=="0";if("NO_COLOR"in nn.env||nn.env.TERM==="dumb")return!1;if(nn.platform==="win32")return!0;return bs.isatty(1)&&bs.isatty(2)}var ks=Nd();function te(r,e){if(!ks)return(t)=>t;return(t)=>r+t+e}var Od=te("\x1B[0m","\x1B[0m"),Ld=te("\x1B[31m","\x1B[39m"),jd=te("\x1B[32m","\x1B[39m"),Fd=te("\x1B[33m","\x1B[39m"),Md=te("\x1B[34m","\x1B[39m"),Bd=te("\x1B[35m","\x1B[39m"),_d=te("\x1B[36m","\x1B[39m"),zd=te("\x1B[37m","\x1B[39m"),Ud=te("\x1B[90m","\x1B[39m"),Vd=te("\x1B[1m","\x1B[22m"),Wd=te("\x1B[3m","\x1B[23m"),qd=te("\x1B[4m","\x1B[24m"),Gd=te("\x1B[2m","\x1B[22m"),Kd=te("\x1B[7m","\x1B[27m"),Hd=te("\x1B[8m","\x1B[28m"),Jd=te("\x1B[9m","\x1B[29m"),Yd=te("\x1B[41m","\x1B[49m"),Zd=te("\x1B[42m","\x1B[49m"),Qd=te("\x1B[43m","\x1B[49m"),Xd=te("\x1B[44m","\x1B[49m"),eu=te("\x1B[45m","\x1B[49m"),tu=te("\x1B[46m","\x1B[49m"),nu=te("\x1B[47m","\x1B[49m"),ru=ks,iu={reset:Od,red:Ld,green:jd,yellow:Fd,blue:Md,magenta:Bd,cyan:_d,white:zd,gray:Ud,bold:Vd,italic:Wd,underline:qd,dim:Gd,inverse:Kd,hidden:Hd,strikethrough:Jd,bgRed:Yd,bgGreen:Zd,bgYellow:Qd,bgBlue:Xd,bgMagenta:eu,bgCyan:tu,bgWhite:nu,isColorSupported:ru},ou=iu,au=["up","down","left","right","space","enter","cancel"],Kf={actions:new Set(au),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};var Hf=su.platform.startsWith("win"),Jf=Symbol("clapp:cancel");var cu=Rd();var ee=(r,e)=>cu?r:e,Yf=ee("","*"),Zf=ee("■","x"),Qf=ee("▲","x"),Xf=ee("◇","o"),eg=ee("┌","T"),lu=ee("│","|"),tg=ee("└","—"),ng=ee("●",">"),rg=ee("○"," "),ig=ee("◻","[•]"),og=ee("◼","[+]"),sg=ee("◻","[ ]"),ag=ee("▪","•"),cg=ee("─","-"),lg=ee("╮","+"),dg=ee("├","+"),ug=ee("╯","+"),mg=ee("●","•"),pg=ee("◆","*"),fg=ee("▲","!"),gg=ee("■","x");var hg={light:ee("─","-"),heavy:ee("━","="),block:ee("█","#")};function du(){return`${ou.gray(lu)} `}var yg=du();var Ir="0.10.0";import{existsSync as Pr}from"node:fs";import{mkdir as mu,writeFile as Rs}from"node:fs/promises";function Er(r){r.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(e)=>{if(w("Initializing ts-cloud Project"),Pr("cloud.config.ts")){if(!await O("cloud.config.ts already exists. Overwrite?",!1)){a("Initialization cancelled");return}}let t=e?.name||await Z("Project name","my-app"),n=e?.mode||await xr("Select deployment mode",["serverless","server"]),i=e?.region||await xr("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),o=new C("Creating configuration file...");o.start();let s=`import { defineConfig } from '@ts-cloud/core'
41
32
 
42
33
  export default defineConfig({
43
34
  project: {
44
- name: '${J}',
45
- slug: '${J.toLowerCase().replace(/[^a-z0-9-]/g,"-")}',
46
- region: '${X}',
35
+ name: '${t}',
36
+ slug: '${t.toLowerCase().replace(/[^a-z0-9-]/g,"-")}',
37
+ region: '${i}',
47
38
  },
48
- mode: '${Y}',
39
+ mode: '${n}',
49
40
  environments: {
50
41
  production: {
51
42
  enabled: true,
@@ -61,31 +52,49 @@ export default defineConfig({
61
52
  // Add your infrastructure configuration here
62
53
  },
63
54
  })
64
- `;if(await j4("cloud.config.ts",Z),Q.succeed("Created cloud.config.ts"),!L6(".gitignore"))await j4(".gitignore",`.env
55
+ `;if(await Rs("cloud.config.ts",s),o.succeed("Created cloud.config.ts"),!Pr(".gitignore"))await Rs(".gitignore",`.env
65
56
  .env.*
66
57
  node_modules/
67
58
  dist/
68
59
  cloudformation/
69
60
  *.log
70
61
  .DS_Store
71
- `),B("Created .gitignore");if(!L6("cloudformation"))await u8("cloudformation",{recursive:!0}),B("Created cloudformation/ directory");n$(`ts-cloud project initialized!
62
+ `),g("Created .gitignore");if(!Pr("cloudformation"))await mu("cloudformation",{recursive:!0}),g("Created cloudformation/ directory");Qe(`ts-cloud project initialized!
72
63
 
73
64
  Next steps:
74
65
  1. Edit cloud.config.ts to configure your infrastructure
75
66
  2. Run 'cloud generate' to create CloudFormation templates
76
- 3. Run 'cloud deploy' to deploy your infrastructure`,"green")}),G.command("init:server","Initialize server-based (EC2) project").action(async()=>{R("Initializing Server-Based Project"),await G.parse(["init","--mode","server"])}),G.command("init:serverless","Initialize serverless (Fargate/Lambda) project").action(async()=>{R("Initializing Serverless Project"),await G.parse(["init","--mode","serverless"])}),G.command("init:hybrid","Initialize hybrid project").action(async()=>{R("Initializing Hybrid Project"),await G.parse(["init","--mode","hybrid"])})}import{readFile as V4,writeFile as l8}from"node:fs/promises";import{resolve as x1}from"node:path";async function x(){let G=await B4();if(!G.project)throw Error("Missing required project configuration in cloud.config.ts");return G}function U$(G){if(G)switch(G.toLowerCase()){case"porkbun":{let Y=process.env.PORKBUN_API_KEY,X=process.env.PORKBUN_SECRET_KEY;if(!Y||!X)throw Error("PORKBUN_API_KEY and PORKBUN_SECRET_KEY environment variables are required for Porkbun provider");return{provider:"porkbun",apiKey:Y,secretKey:X}}case"godaddy":{let Y=process.env.GODADDY_API_KEY,X=process.env.GODADDY_API_SECRET;if(!Y||!X)throw Error("GODADDY_API_KEY and GODADDY_API_SECRET environment variables are required for GoDaddy provider");let Q=process.env.GODADDY_ENVIRONMENT||"production";return{provider:"godaddy",apiKey:Y,apiSecret:X,environment:Q}}case"route53":{let Y=process.env.AWS_REGION||"us-east-1",X=process.env.AWS_HOSTED_ZONE_ID;return{provider:"route53",region:Y,hostedZoneId:X}}case"cloudflare":{let Y=process.env.CLOUDFLARE_API_TOKEN;if(!Y)throw Error("CLOUDFLARE_API_TOKEN environment variable is required for Cloudflare provider");return{provider:"cloudflare",apiToken:Y,zoneId:process.env.CLOUDFLARE_ZONE_ID,accountId:process.env.CLOUDFLARE_ACCOUNT_ID}}default:throw Error(`Unknown DNS provider: ${G}. Supported: porkbun, godaddy, route53, cloudflare`)}if(new O6().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 C$(G){let $=U$(G);if(!$)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 U0($)}async function r8(G){let $=await x(),J=u(process.cwd(),$),Y=G??Object.keys($.environments??{})[0]??"production",X=J.environments.get(Y);if(!X)throw J.store.close(),Error(`Environment ${Y} was not found.`);let Q=$.project?.region??"us-east-1",Z=Q$(process.cwd()),z=new A4(J.store),F=new T4(z,{encryptionKey:Z,backends:[new q4(J.store,Z),new R4(new M0(Q),Q),new L4(new D6(Q),Q),new E4]}),U=J.store.getActorByExternalId("system","cli")??J.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return await O4(F,J,$,U.id),{config:$,controlPlane:J,env:Y,environmentRecord:X,store:z,service:F,actor:U}}async function f$(G,$){let J=await r8(G);try{return await $(J)}finally{J.controlPlane.store.close()}}async function B$(G){try{await G()}catch($){K($ instanceof Error?$.message:String($))}}function a8(G){return G.map(($)=>[$.key,$.kind,$.scope.type,$.inherited?"inherited":$.overridden?"override":"direct",$.kind==="secret"?$.backend??"restricted":$.value??"",$.backendVersion??"—",$.required?"required":"optional",$.updatedAt])}function D4(G,$=process.env){if(G.value!=null)throw Error("Secret values are not accepted in command arguments; use --from-env or the interactive prompt.");if(!G.fromEnv)return;let J=$[G.fromEnv];if(!J)throw Error(`${G.fromEnv} is empty or unavailable.`);return J}function w$(G,$){let J=$.scope??"environment";if(J==="project")return{type:J,id:G.controlPlane.project.id};if(J==="environment"){let Y=$.target?G.controlPlane.store.listEnvironments(G.controlPlane.project.id).find((X)=>X.id===$.target||X.slug===$.target):G.environmentRecord;if(!Y)throw Error(`Environment ${$.target} was not found.`);return{type:J,id:Y.id,environmentId:Y.id}}if(J==="service"){let Y=G.controlPlane.store.listResources(G.controlPlane.project.id),X=Y.find((Q)=>Q.id===$.target||Q.slug===$.target);if(!X)throw Error("Service scope requires --target with a resource ID or slug.");return{type:J,id:X.id,environmentId:X.environmentId,resourceId:X.id}}if(J==="function"){let Y=G.controlPlane.store.listResources(G.controlPlane.project.id),X=Y.find((Q)=>Q.kind==="function"&&(Q.id===$.target||Q.slug===$.target));if(!X)throw Error("Function scope requires --target with a function resource ID or slug.");return{type:J,id:X.id,environmentId:X.environmentId,resourceId:X.id}}if(J==="preview"){let Y=new w4(G.controlPlane.store).listInstances({projectId:G.controlPlane.project.id}).find((X)=>X.id===$.target||X.name===$.target);if(!Y)throw Error("Preview scope requires --target with a preview ID or name.");return{type:J,id:Y.id,environmentId:Y.baseEnvironmentId,resourceId:Y.resourceId,previewId:Y.id}}throw Error("--scope must be project, environment, service, function, or preview.")}function w6(G){let $=G??"local_encrypted";if(!["local_encrypted","aws_secrets_manager","aws_ssm","external"].includes($))throw Error("--backend must be local_encrypted, aws_secrets_manager, aws_ssm, or external.");return $}function h0(G,$,J){let Y=G.store.find(G.controlPlane.project.id,J,$);if(!Y)throw Error(`${$} was not found in the selected scope.`);return Y}async function p8(G,$){if(G==="secret")return D4($)??await p("Enter secret value (input is write-only)","");if($.fromEnv){let J=process.env[$.fromEnv];if(J==null)throw Error(`${$.fromEnv} is unavailable.`);return J}if($.value!=null)return $.value;return await p("Enter variable value","")}function g$(G){return G.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 x6(G){G.command("config","Show validated file configuration").action(async()=>B$(async()=>{let $=await x();console.log(JSON.stringify($,null,2))})),G.command("config:validate","Validate configuration file").action(async()=>B$(async()=>{let $=await x();if(!$.project?.name||!$.project?.slug||!$.mode)throw Error("project.name, project.slug, and mode are required.");B(`Configuration is valid: ${$.project.name} · ${$.mode} · ${$.project.region||"us-east-1"}`)})),g$(G.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($)=>B$(async()=>f$($.env,async(J)=>{let Y=w$(J,$),X=["variable","secret"].includes($.kind??"")?$.kind:void 0,Q=J.service.list({projectId:J.controlPlane.project.id,scope:Y,kind:X,search:$.search,canReadSecretMetadata:!0});if($.json)console.log(JSON.stringify(Q,null,2));else w(["Key","Kind","Scope","Precedence","Value/backend","Backend version","Requirement","Updated"],a8(Q))}))),g$(G.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($,J)=>B$(async()=>f$(J.env,async(Y)=>{let X=w$(Y,J),Q=J.secret?"secret":"variable",Z=Q==="secret"?w6(J.backend):void 0,z=Z==="external"?void 0:await p8(Q,J),F=await Y.service.set({organizationId:Y.controlPlane.organization.id,projectId:Y.controlPlane.project.id,scope:X,key:$,kind:Q,value:z,reference:J.reference,backend:Z,required:!!J.required,confirmed:J.confirm===$,actorId:Y.actor.id,idempotencyKey:`cli:${crypto.randomUUID()}`});if(J.json)console.log(JSON.stringify({entry:Y.service.list({projectId:Y.controlPlane.project.id,scope:X,canReadSecretMetadata:!0}).find((U)=>U.id===F.entry.id),mutation:F.mutation,warnings:F.warnings},null,2));else B(`${$}: ${F.mutation.added.length?"created":F.mutation.changed.length?"updated":"unchanged"}; ${F.mutation.affectedResourceIds.length} affected service(s).`)}))),g$(G.command("config:delete <key>","Delete a scoped variable or secret")).option("--confirm <key>","Exact key confirmation").action(async($,J)=>B$(async()=>f$(J.env,async(Y)=>{let X=h0(Y,$,w$(Y,J));await Y.service.remove({entryId:X.id,expectedVersion:X.version,confirmed:J.confirm===$,actorId:Y.actor.id}),B(`Deleted ${$}.`)}))),g$(G.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($,J)=>B$(async()=>f$(J.env,async(Y)=>{let X=await Y.service.importDotenv({organizationId:Y.controlPlane.organization.id,projectId:Y.controlPlane.project.id,scope:w$(Y,J),source:await V4(x1($),"utf8"),secretKeys:J.secretKeys?.split(",").map((Q)=>Q.trim()).filter(Boolean),backend:w6(J.backend),confirmed:J.confirm==="import",actorId:Y.actor.id,idempotencyKey:`cli-import:${crypto.randomUUID()}`});if(!X.document.valid)throw Error(X.document.diagnostics.map((Q)=>`line ${Q.line}: ${Q.message}`).join(`
77
- `));if(J.json)console.log(JSON.stringify({mutation:X.mutation,diagnostics:X.document.diagnostics,warnings:X.warnings},null,2));else B(`Imported ${X.mutation?.added.length??0} new, ${X.mutation?.changed.length??0} changed, and ${X.mutation?.unchanged.length??0} unchanged entries.`)}))),g$(G.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($,J)=>B$(async()=>f$(J.env,async(Y)=>{let{parseDotenv:X}=await import("./chunk-9knpg30y.js"),Q=X(await V4(x1($),"utf8"));if(!Q.valid)throw Error(Q.diagnostics.map((z)=>`line ${z.line}: ${z.message}`).join(`
78
- `));let Z=Y.service.plan({projectId:Y.controlPlane.project.id,scope:w$(Y,J),values:Q.values,removeMissing:!!J.removeMissing});if(J.json)console.log(JSON.stringify(Z,null,2));else w(["Action","Keys"],[["Add",Z.added.join(", ")||"—"],["Change",Z.changed.join(", ")||"—"],["Remove",Z.removed.join(", ")||"—"],["Unchanged",Z.unchanged.join(", ")||"—"]]),W(`${Z.affectedResourceIds.length} affected service(s); no secret values were printed.`)}))),g$(G.command("config:export [file]","Export plaintext variables only as dotenv")).action(async($,J)=>B$(async()=>f$(J.env,async(Y)=>{let X=Y.service.exportVariables({projectId:Y.controlPlane.project.id,scope:w$(Y,J)});if($)await l8(x1($),X,{mode:384}),B(`Exported variables to ${x1($)}; secrets were excluded.`);else process.stdout.write(X)}))),g$(G.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($,J)=>B$(async()=>f$(J.env,async(Y)=>{let X=w$(Y,J),Q=h0(Y,$,X),Z=w$(Y,{env:J.env,scope:J.toScope,target:J.toTarget}),z=await Y.service.transfer({entryId:Q.id,targetScope:Z,mode:J.move?"move":"copy",confirmed:J.confirm===$,actorId:Y.actor.id,idempotencyKey:`cli-transfer:${crypto.randomUUID()}`});B(`${$} ${J.move?"moved":"copied"}; ${z.mutation.affectedResourceIds.length} affected service(s).`)}))),g$(G.command("config:reveal <key>","Reveal a secret after exact confirmation")).option("--confirm <key>","Exact key confirmation").action(async($,J)=>B$(async()=>f$(J.env,async(Y)=>{if(J.confirm!==$)throw Error(`Use --confirm ${$} to reveal this secret.`);let X=h0(Y,$,w$(Y,J));console.log(await Y.service.reveal({entryId:X.id,canRevealSecrets:!0,recentlyAuthenticated:!0,actorId:Y.actor.id}))}))),g$(G.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($)=>B$(async()=>f$($.environment??$.env,async(J)=>{let Y=w$(J,{env:$.environment,scope:"environment",target:$.environment});if($.list)w(["Key","Value","Updated"],J.service.list({projectId:J.controlPlane.project.id,scope:Y,kind:"variable"}).map((X)=>[X.key,X.value??"",X.updatedAt]));else if($.set){let X=$.set.indexOf("=");if(X<1)throw Error("Use --set KEY=VALUE.");let Q=$.set.slice(0,X),Z=$.set.slice(X+1);await J.service.set({organizationId:J.controlPlane.organization.id,projectId:J.controlPlane.project.id,scope:Y,key:Q,kind:"variable",value:Z,confirmed:!1,actorId:J.actor.id}),B(`Set ${Q}.`)}else if($.unset){let X=h0(J,$.unset,Y);await J.service.remove({entryId:X.id,expectedVersion:X.version,confirmed:await k(`Delete ${X.key}?`,!1),actorId:J.actor.id}),B(`Deleted ${X.key}.`)}else W("Use --list, --set KEY=VALUE, or --unset KEY.")}))),g$(G.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($)=>B$(async()=>f$($.env,async(J)=>{let Y=w$(J,$);if($.list)w(["Key","Backend","Version","Rotated"],J.service.list({projectId:J.controlPlane.project.id,scope:Y,kind:"secret",canReadSecretMetadata:!0}).map((X)=>[X.key,X.backend??"restricted",X.backendVersion??"—",X.rotatedAt??"never"]));else if($.create||$.update){let X=$.create??$.update,Q=D4($)??await p("Enter secret value (write-only)","");await J.service.set({organizationId:J.controlPlane.organization.id,projectId:J.controlPlane.project.id,scope:Y,key:X,kind:"secret",value:Q,backend:w6($.backend),confirmed:$.confirm===X,actorId:J.actor.id}),B(`${X} ${$.update?"rotated":"created"}.`)}else if($.get){if($.confirm!==$.get)throw Error(`Use --confirm ${$.get} to reveal.`);let X=h0(J,$.get,Y);console.log(await J.service.reveal({entryId:X.id,canRevealSecrets:!0,recentlyAuthenticated:!0,actorId:J.actor.id}))}else if($.delete){let X=h0(J,$.delete,Y);await J.service.remove({entryId:X.id,expectedVersion:X.version,confirmed:$.confirm===X.key,actorId:J.actor.id}),B(`Deleted ${X.key}.`)}else W("Use --list, --create, --update, --get, or --delete.")})))}import{existsSync as n8}from"node:fs";import{readFile as s8,writeFile as i8}from"node:fs/promises";function N6(G){G.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($,J)=>{let Y=J?.config||"cloud.config.ts",X=J?.root;if(!X){K("Missing --root <path>");return}if(!n8(Y)){K(`Config file not found: ${Y}`);return}try{let Q=x4({configText:await s8(Y,"utf8"),name:$,root:X,domain:J?.domain,path:J?.path,deploy:t8(J?.deploy),build:J?.build,start:J?.start,port:o8(J?.port),type:J?.type,pathRewriteStyle:e8(J?.pathRewriteStyle)});if(J?.dryRun){console.log(Q);return}await i8(Y,Q),B(`Added site '${$}' to ${Y}`)}catch(Q){K(Q instanceof Error?Q.message:String(Q))}})}function t8(G){if(!G)return;if(G==="bucket"||G==="server")return G;throw Error(`Invalid --deploy '${G}'. Expected 'server' or 'bucket'.`)}function e8(G){if(!G)return;if(G==="directory"||G==="flat")return G;throw Error(`Invalid --path-rewrite-style '${G}'. Expected 'directory' or 'flat'.`)}function o8(G){if(!G)return;let $=Number.parseInt(G,10);if(!Number.isInteger($)||$<1||$>65535)throw Error(`Invalid --port '${G}'. Expected a TCP port between 1 and 65535.`);return $}import{existsSync as XG}from"node:fs";import{mkdir as QG,writeFile as ZG}from"node:fs/promises";import{join as WG}from"node:path";class C0{builder;config;environment;mergedConfig;serverEipLogicalIds=new Map;constructor(G){this.config=G.config,this.environment=G.environment,this.builder=new N4(`${this.config.project.name} - ${this.environment}`),this.mergedConfig=this.mergeEnvironmentConfig()}mergeEnvironmentConfig(){let $=this.config.environments[this.environment]?.infrastructure;if(!$)return this.config;return{...this.config,infrastructure:{...this.config.infrastructure,...$,compute:{...this.config.infrastructure?.compute,...$.compute},storage:{...this.config.infrastructure?.storage,...$.storage},functions:{...this.config.infrastructure?.functions,...$.functions},servers:{...this.config.infrastructure?.servers,...$.servers},databases:{...this.config.infrastructure?.databases,...$.databases},cdn:{...this.config.infrastructure?.cdn,...$.cdn},sftp:$.sftp??this.config.infrastructure?.sftp,queues:{...this.config.infrastructure?.queues,...$.queues},redirects:{...this.config.infrastructure?.redirects,...$.redirects},realtime:{...this.config.infrastructure?.realtime,...$.realtime},cache:{...this.config.infrastructure?.cache,...$.cache},fileSystem:{...this.config.infrastructure?.fileSystem,...$.fileSystem},email:{...this.config.infrastructure?.email,...$.email},search:{...this.config.infrastructure?.search,...$.search},ai:{...this.config.infrastructure?.ai,...$.ai}}}}shouldDeploy(G){if(G.environments&&!G.environments.includes(this.environment))return!1;if(G.requiresFeatures){let $=this.config.features||{};if(!G.requiresFeatures.every((Y)=>$[Y]===!0))return!1}if(G.regions){let $=this.config.environments[this.environment]?.region||this.config.project.region;if(!G.regions.includes($))return!1}if(G.condition&&typeof G.condition==="function")return G.condition(this.config,this.environment);return!0}resolveApiOriginPort(){let G=this.mergedConfig.infrastructure?.api?.port??this.mergedConfig.ports?.api,$=Number(G||3008);return Number.isFinite($)&&$>0?$: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(G,$){return G==="public"||$.routeCompute===!0}storageBucketName(G,$,J){return this.mergedConfig.infrastructure?.storage?.[J]?.bucket??`${G}-${$}-${J}`}fileSystemLogicalId(G,$,J){return S4(P4({slug:`${G}-${J}`,environment:$,resourceType:"efs"}))}resolveSftpStorage(G,$,J){let Y=G.storage;if(Y?.type==="efs"&&Y.fileSystem){if(!this.mergedConfig.infrastructure?.fileSystem?.[Y.fileSystem])throw Error(`sftp: storage.fileSystem "${Y.fileSystem}" is not defined in infrastructure.fileSystem`);return{type:"efs",fileSystemId:{Ref:this.fileSystemLogicalId($,J,Y.fileSystem)},posixProfile:Y.posixProfile}}if(Y?.type==="s3"&&Y.storageBucket){if(!this.mergedConfig.infrastructure?.storage?.[Y.storageBucket])throw Error(`sftp: storage.storageBucket "${Y.storageBucket}" is not defined in infrastructure.storage`);return{type:"s3",bucket:this.storageBucketName($,J,Y.storageBucket)}}return}resolveComputeCachePathPatterns(G){if(G&&G.length>0)return G;return this.defaultComputeCachePathPatterns()}createComputeCacheBehavior(G,$){return{PathPattern:G,TargetOriginId:$,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(G,$,J,Y,X,Q){let Z=this.serverEipLogicalIds.get("app"),z=this.serverEipLogicalIds.get("appInstance");if(!Z)return;let F=`EC2-${Y}-${X}-api`,U=this.mergedConfig.infrastructure?.servers?.app?.region||this.mergedConfig.project?.region||"us-east-1",M=U==="us-east-1"?".compute-1.amazonaws.com":`.${U}.compute.amazonaws.com`,H={"Fn::Join":["",["ec2-",{"Fn::Join":["-",{"Fn::Split":[".",{Ref:Z}]}]},M]]},j=this.resolveApiOriginPort();if(G.push({Id:F,DomainName:H,CustomOriginConfig:{HTTPPort:j,HTTPSPort:443,OriginProtocolPolicy:"http-only",OriginSSLProtocols:["TLSv1.2"]}}),J.push(Z),z)J.push(z);for(let V of Q)$.push(this.createComputeCacheBehavior(V,F))}buildCaddyfile(G){let $=G.filter(([,X])=>typeof X.domain==="string"&&X.domain&&typeof X.port==="number");if($.length===0)return;let J=new Map;for(let[,X]of $){let Q=J.get(X.domain)??[];Q.push({port:X.port,path:X.path}),J.set(X.domain,Q)}let Y=[];for(let[X,Q]of J){let z=[...Q].sort((F,U)=>{let M=!F.path||F.path==="/",H=!U.path||U.path==="/";if(M&&!H)return 1;if(!M&&H)return-1;return(U.path?.length??0)-(F.path?.length??0)}).map((F)=>{let U=!F.path||F.path==="/",M=`reverse_proxy localhost:${F.port}`;return U?` handle {
79
- ${M}
80
- }`:` handle ${F.path} {
81
- ${M}
82
- }`});Y.push(`${X} {
83
- ${z.join(`
67
+ 3. Run 'cloud deploy' to deploy your infrastructure`,"green")}),r.command("init:server","Initialize server-based (EC2) project").action(async()=>{w("Initializing Server-Based Project"),await r.parse(["init","--mode","server"])}),r.command("init:serverless","Initialize serverless (Fargate/Lambda) project").action(async()=>{w("Initializing Serverless Project"),await r.parse(["init","--mode","serverless"])}),r.command("init:hybrid","Initialize hybrid project").action(async()=>{w("Initializing Hybrid Project"),await r.parse(["init","--mode","hybrid"])})}import{readFile as Ps,writeFile as pu}from"node:fs/promises";import{resolve as Nn}from"node:path";async function P(){let r=await Is();if(!r.project)throw Error("Missing required project configuration in cloud.config.ts");return r}function ce(r){if(r)switch(r.toLowerCase()){case"porkbun":{let n=process.env.PORKBUN_API_KEY,i=process.env.PORKBUN_SECRET_KEY;if(!n||!i)throw Error("PORKBUN_API_KEY and PORKBUN_SECRET_KEY environment variables are required for Porkbun provider");return{provider:"porkbun",apiKey:n,secretKey:i}}case"godaddy":{let n=process.env.GODADDY_API_KEY,i=process.env.GODADDY_API_SECRET;if(!n||!i)throw Error("GODADDY_API_KEY and GODADDY_API_SECRET environment variables are required for GoDaddy provider");let o=process.env.GODADDY_ENVIRONMENT||"production";return{provider:"godaddy",apiKey:n,apiSecret:i,environment:o}}case"route53":{let n=process.env.AWS_REGION||"us-east-1",i=process.env.AWS_HOSTED_ZONE_ID;return{provider:"route53",region:n,hostedZoneId:i}}case"cloudflare":{let n=process.env.CLOUDFLARE_API_TOKEN;if(!n)throw Error("CLOUDFLARE_API_TOKEN environment variable is required for Cloudflare provider");return{provider:"cloudflare",apiToken:n,zoneId:process.env.CLOUDFLARE_ZONE_ID,accountId:process.env.CLOUDFLARE_ACCOUNT_ID}}default:throw Error(`Unknown DNS provider: ${r}. Supported: porkbun, godaddy, route53, cloudflare`)}if(new Dr().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 Be(r){let e=ce(r);if(!e)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 Re(e)}async function fu(r){let e=await P(),t=q(process.cwd(),e),n=r??Object.keys(e.environments??{})[0]??"production",i=t.environments.get(n);if(!i)throw t.store.close(),Error(`Environment ${n} was not found.`);let o=e.project?.region??"us-east-1",s=le(process.cwd()),c=new xs(t.store),l=new Os(c,{encryptionKey:s,backends:[new Es(t.store,s),new Ds(new pt(o),o),new Ts(new Nr(o),o),new Ns]}),d=t.store.getActorByExternalId("system","cli")??t.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return await Ls(l,t,e,d.id),{config:e,controlPlane:t,env:n,environmentRecord:i,store:c,service:l,actor:d}}async function _e(r,e){let t=await fu(r);try{return await e(t)}finally{t.controlPlane.store.close()}}async function ve(r){try{await r()}catch(e){p(e instanceof Error?e.message:String(e))}}function gu(r){return r.map((e)=>[e.key,e.kind,e.scope.type,e.inherited?"inherited":e.overridden?"override":"direct",e.kind==="secret"?e.backend??"restricted":e.value??"",e.backendVersion??"—",e.required?"required":"optional",e.updatedAt])}function Fs(r,e=process.env){if(r.value!=null)throw Error("Secret values are not accepted in command arguments; use --from-env or the interactive prompt.");if(!r.fromEnv)return;let t=e[r.fromEnv];if(!t)throw Error(`${r.fromEnv} is empty or unavailable.`);return t}function Ie(r,e){let t=e.scope??"environment";if(t==="project")return{type:t,id:r.controlPlane.project.id};if(t==="environment"){let n=e.target?r.controlPlane.store.listEnvironments(r.controlPlane.project.id).find((i)=>i.id===e.target||i.slug===e.target):r.environmentRecord;if(!n)throw Error(`Environment ${e.target} was not found.`);return{type:t,id:n.id,environmentId:n.id}}if(t==="service"){let n=r.controlPlane.store.listResources(r.controlPlane.project.id),i=n.find((o)=>o.id===e.target||o.slug===e.target);if(!i)throw Error("Service scope requires --target with a resource ID or slug.");return{type:t,id:i.id,environmentId:i.environmentId,resourceId:i.id}}if(t==="function"){let n=r.controlPlane.store.listResources(r.controlPlane.project.id),i=n.find((o)=>o.kind==="function"&&(o.id===e.target||o.slug===e.target));if(!i)throw Error("Function scope requires --target with a function resource ID or slug.");return{type:t,id:i.id,environmentId:i.environmentId,resourceId:i.id}}if(t==="preview"){let n=new js(r.controlPlane.store).listInstances({projectId:r.controlPlane.project.id}).find((i)=>i.id===e.target||i.name===e.target);if(!n)throw Error("Preview scope requires --target with a preview ID or name.");return{type:t,id:n.id,environmentId:n.baseEnvironmentId,resourceId:n.resourceId,previewId:n.id}}throw Error("--scope must be project, environment, service, function, or preview.")}function Tr(r){let e=r??"local_encrypted";if(!["local_encrypted","aws_secrets_manager","aws_ssm","external"].includes(e))throw Error("--backend must be local_encrypted, aws_secrets_manager, aws_ssm, or external.");return e}function Ft(r,e,t){let n=r.store.find(r.controlPlane.project.id,t,e);if(!n)throw Error(`${e} was not found in the selected scope.`);return n}async function hu(r,e){if(r==="secret")return Fs(e)??await Z("Enter secret value (input is write-only)","");if(e.fromEnv){let t=process.env[e.fromEnv];if(t==null)throw Error(`${e.fromEnv} is unavailable.`);return t}if(e.value!=null)return e.value;return await Z("Enter variable value","")}function ze(r){return r.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 Or(r){r.command("config","Show validated file configuration").action(async()=>ve(async()=>{let e=await P();console.log(JSON.stringify(e,null,2))})),r.command("config:validate","Validate configuration file").action(async()=>ve(async()=>{let e=await P();if(!e.project?.name||!e.project?.slug||!e.mode)throw Error("project.name, project.slug, and mode are required.");g(`Configuration is valid: ${e.project.name} · ${e.mode} · ${e.project.region||"us-east-1"}`)})),ze(r.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(e)=>ve(async()=>_e(e.env,async(t)=>{let n=Ie(t,e),i=["variable","secret"].includes(e.kind??"")?e.kind:void 0,o=t.service.list({projectId:t.controlPlane.project.id,scope:n,kind:i,search:e.search,canReadSecretMetadata:!0});if(e.json)console.log(JSON.stringify(o,null,2));else x(["Key","Kind","Scope","Precedence","Value/backend","Backend version","Requirement","Updated"],gu(o))}))),ze(r.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(e,t)=>ve(async()=>_e(t.env,async(n)=>{let i=Ie(n,t),o=t.secret?"secret":"variable",s=o==="secret"?Tr(t.backend):void 0,c=s==="external"?void 0:await hu(o,t),l=await n.service.set({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,scope:i,key:e,kind:o,value:c,reference:t.reference,backend:s,required:!!t.required,confirmed:t.confirm===e,actorId:n.actor.id,idempotencyKey:`cli:${crypto.randomUUID()}`});if(t.json)console.log(JSON.stringify({entry:n.service.list({projectId:n.controlPlane.project.id,scope:i,canReadSecretMetadata:!0}).find((d)=>d.id===l.entry.id),mutation:l.mutation,warnings:l.warnings},null,2));else g(`${e}: ${l.mutation.added.length?"created":l.mutation.changed.length?"updated":"unchanged"}; ${l.mutation.affectedResourceIds.length} affected service(s).`)}))),ze(r.command("config:delete <key>","Delete a scoped variable or secret")).option("--confirm <key>","Exact key confirmation").action(async(e,t)=>ve(async()=>_e(t.env,async(n)=>{let i=Ft(n,e,Ie(n,t));await n.service.remove({entryId:i.id,expectedVersion:i.version,confirmed:t.confirm===e,actorId:n.actor.id}),g(`Deleted ${e}.`)}))),ze(r.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(e,t)=>ve(async()=>_e(t.env,async(n)=>{let i=await n.service.importDotenv({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,scope:Ie(n,t),source:await Ps(Nn(e),"utf8"),secretKeys:t.secretKeys?.split(",").map((o)=>o.trim()).filter(Boolean),backend:Tr(t.backend),confirmed:t.confirm==="import",actorId:n.actor.id,idempotencyKey:`cli-import:${crypto.randomUUID()}`});if(!i.document.valid)throw Error(i.document.diagnostics.map((o)=>`line ${o.line}: ${o.message}`).join(`
68
+ `));if(t.json)console.log(JSON.stringify({mutation:i.mutation,diagnostics:i.document.diagnostics,warnings:i.warnings},null,2));else g(`Imported ${i.mutation?.added.length??0} new, ${i.mutation?.changed.length??0} changed, and ${i.mutation?.unchanged.length??0} unchanged entries.`)}))),ze(r.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(e,t)=>ve(async()=>_e(t.env,async(n)=>{let{parseDotenv:i}=await import("./chunk-80ar3aty.js"),o=i(await Ps(Nn(e),"utf8"));if(!o.valid)throw Error(o.diagnostics.map((c)=>`line ${c.line}: ${c.message}`).join(`
69
+ `));let s=n.service.plan({projectId:n.controlPlane.project.id,scope:Ie(n,t),values:o.values,removeMissing:!!t.removeMissing});if(t.json)console.log(JSON.stringify(s,null,2));else x(["Action","Keys"],[["Add",s.added.join(", ")||"—"],["Change",s.changed.join(", ")||"—"],["Remove",s.removed.join(", ")||"—"],["Unchanged",s.unchanged.join(", ")||"—"]]),a(`${s.affectedResourceIds.length} affected service(s); no secret values were printed.`)}))),ze(r.command("config:export [file]","Export plaintext variables only as dotenv")).action(async(e,t)=>ve(async()=>_e(t.env,async(n)=>{let i=n.service.exportVariables({projectId:n.controlPlane.project.id,scope:Ie(n,t)});if(e)await pu(Nn(e),i,{mode:384}),g(`Exported variables to ${Nn(e)}; secrets were excluded.`);else process.stdout.write(i)}))),ze(r.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(e,t)=>ve(async()=>_e(t.env,async(n)=>{let i=Ie(n,t),o=Ft(n,e,i),s=Ie(n,{env:t.env,scope:t.toScope,target:t.toTarget}),c=await n.service.transfer({entryId:o.id,targetScope:s,mode:t.move?"move":"copy",confirmed:t.confirm===e,actorId:n.actor.id,idempotencyKey:`cli-transfer:${crypto.randomUUID()}`});g(`${e} ${t.move?"moved":"copied"}; ${c.mutation.affectedResourceIds.length} affected service(s).`)}))),ze(r.command("config:reveal <key>","Reveal a secret after exact confirmation")).option("--confirm <key>","Exact key confirmation").action(async(e,t)=>ve(async()=>_e(t.env,async(n)=>{if(t.confirm!==e)throw Error(`Use --confirm ${e} to reveal this secret.`);let i=Ft(n,e,Ie(n,t));console.log(await n.service.reveal({entryId:i.id,canRevealSecrets:!0,recentlyAuthenticated:!0,actorId:n.actor.id}))}))),ze(r.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(e)=>ve(async()=>_e(e.environment??e.env,async(t)=>{let n=Ie(t,{env:e.environment,scope:"environment",target:e.environment});if(e.list)x(["Key","Value","Updated"],t.service.list({projectId:t.controlPlane.project.id,scope:n,kind:"variable"}).map((i)=>[i.key,i.value??"",i.updatedAt]));else if(e.set){let i=e.set.indexOf("=");if(i<1)throw Error("Use --set KEY=VALUE.");let o=e.set.slice(0,i),s=e.set.slice(i+1);await t.service.set({organizationId:t.controlPlane.organization.id,projectId:t.controlPlane.project.id,scope:n,key:o,kind:"variable",value:s,confirmed:!1,actorId:t.actor.id}),g(`Set ${o}.`)}else if(e.unset){let i=Ft(t,e.unset,n);await t.service.remove({entryId:i.id,expectedVersion:i.version,confirmed:await O(`Delete ${i.key}?`,!1),actorId:t.actor.id}),g(`Deleted ${i.key}.`)}else a("Use --list, --set KEY=VALUE, or --unset KEY.")}))),ze(r.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(e)=>ve(async()=>_e(e.env,async(t)=>{let n=Ie(t,e);if(e.list)x(["Key","Backend","Version","Rotated"],t.service.list({projectId:t.controlPlane.project.id,scope:n,kind:"secret",canReadSecretMetadata:!0}).map((i)=>[i.key,i.backend??"restricted",i.backendVersion??"—",i.rotatedAt??"never"]));else if(e.create||e.update){let i=e.create??e.update,o=Fs(e)??await Z("Enter secret value (write-only)","");await t.service.set({organizationId:t.controlPlane.organization.id,projectId:t.controlPlane.project.id,scope:n,key:i,kind:"secret",value:o,backend:Tr(e.backend),confirmed:e.confirm===i,actorId:t.actor.id}),g(`${i} ${e.update?"rotated":"created"}.`)}else if(e.get){if(e.confirm!==e.get)throw Error(`Use --confirm ${e.get} to reveal.`);let i=Ft(t,e.get,n);console.log(await t.service.reveal({entryId:i.id,canRevealSecrets:!0,recentlyAuthenticated:!0,actorId:t.actor.id}))}else if(e.delete){let i=Ft(t,e.delete,n);await t.service.remove({entryId:i.id,expectedVersion:i.version,confirmed:e.confirm===i.key,actorId:t.actor.id}),g(`Deleted ${i.key}.`)}else a("Use --list, --create, --update, --get, or --delete.")})))}import{existsSync as xu}from"node:fs";import{readFile as Eu,writeFile as Du}from"node:fs/promises";async function On(r){let e=[];for(let t of r.steps)try{e.push({step:t,state:await t.satisfied()?"satisfied":"pending"})}catch(n){e.push({step:t,state:"unknown",reason:n instanceof Error?n.message:String(n)})}return e}function Ms(r){return r.filter((e)=>e.state!=="satisfied")}function yu(r){return Ms(r).some((e)=>e.step.destructive===!0)}function Ln(r,e){let t=Ms(e),n=[`${r.operation} ${r.target}`];if(t.length===0)return n.push(" Nothing to do — every step is already satisfied."),n;for(let{step:o,state:s,reason:c}of e){let l=s==="satisfied"?"ok ":s==="unknown"?"? ":"→ ",d=[o.destructive?"DESTRUCTIVE":"",s==="satisfied"?"already done":""].filter(Boolean);if(n.push(` ${l}${o.title}${d.length>0?` [${d.join(", ")}]`:""}`),o.change)n.push(` ${o.change.from} → ${o.change.to}`);if(c)n.push(` could not check: ${c}`)}let i=t.filter((o)=>o.step.destructive).length;return n.push(` ${t.length} step(s) would run`+(i>0?`, ${i} irreversible — re-run with --confirm ${r.target}`:"")),n}async function jn(r,e,t={}){let n={operation:r.operation,target:r.target,steps:[],success:!0};if(yu(e)&&t.confirm!==r.target)throw Error(`${r.operation} includes an irreversible step. Re-run with --confirm ${r.target} to authorize it.`);for(let{step:i,state:o}of e){if(o==="satisfied"){n.steps.push({id:i.id,title:i.title,state:"skipped"}),t.log?.(`skip ${i.title} (already done)`);continue}t.audit?.({operation:r.operation,target:r.target,step:i.id,state:"started"});try{await i.apply(),n.steps.push({id:i.id,title:i.title,state:"applied"}),t.log?.(`done ${i.title}`),t.audit?.({operation:r.operation,target:r.target,step:i.id,state:"applied"})}catch(s){let c=s instanceof Error?s.message:String(s);return n.steps.push({id:i.id,title:i.title,state:"failed",error:c}),n.success=!1,t.log?.(`FAIL ${i.title}: ${c}`),t.audit?.({operation:r.operation,target:r.target,step:i.id,state:"failed",error:c}),n}}return n}function Mt(r,e){return`/tmp/ts-cloud-move-${r}-${e}.tar.gz`}function Bs(r,e){return`${r}-${e}*.service`}function J(r){return`'${r.split("'").join("'\\''")}'`}function vu(r,e){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${J(_s(r,e))} || true); do`,' systemctl stop "$unit" 2>/dev/null || true',"done"].join(`
70
+ `)}function _s(r,e){return`^${r}-${e}-((queue|daemon)-.*|scheduler)\\.service$`}function bu(r,e){return zs(_s(r,e))}function zs(r){return[`ls /etc/systemd/system/ 2>/dev/null | grep -E ${J(r)} | while read -r unit; do`,' systemctl is-active "$unit" >/dev/null 2>&1 && echo "active:$unit" || true',"done"].join(`
71
+ `)}function wu(r){return!r.split(`
72
+ `).some((e)=>e.trim().startsWith("active:"))}function Su(r,e,t){let n=Mt(r,e);return["set -eu",`test -d ${J(t)} || { echo "no site tree at ${t}" >&2; exit 1; }`,`rm -f ${J(n)}`,`tar czf ${J(n)} -C "$(dirname ${J(t)})" "$(basename ${J(t)})" -C /etc/systemd/system $(cd /etc/systemd/system && ls ${Bs(r,e)} 2>/dev/null | tr '\\n' ' ')`,`sha256sum ${J(n)} | cut -d' ' -f1`].join(`
73
+ `)}function $u(r,e,t){let n=Mt(r,e);return["set -eu",`test -f ${J(n)} || { echo "archive not staged at ${n}" >&2; exit 1; }`,`mkdir -p "$(dirname ${J(t)})"`,`tar xzf ${J(n)} -C "$(dirname ${J(t)})" "$(basename ${J(t)})"`,`tar xzf ${J(n)} -C /etc/systemd/system --wildcards ${J(Bs(r,e))} 2>/dev/null || true`,"systemctl daemon-reload",`systemctl enable ${r}-${e}.service 2>/dev/null || true`,`systemctl restart ${r}-${e}.service`,`rm -f ${J(n)}`].join(`
74
+ `)}function Cu(r,e,t){return[`test -d ${J(t)} && echo tree:present || echo tree:absent`,`systemctl is-active ${r}-${e}.service >/dev/null 2>&1 && echo unit:active || echo unit:inactive`].join(`
75
+ `)}function Au(r){return r.includes("tree:present")&&r.includes("unit:active")}function ku(r,e="/",t=10){let n=`http://127.0.0.1:${r}${e.startsWith("/")?e:`/${e}`}`;return["set -eu",`for i in $(seq 1 ${t}); do`,` if curl -fsS -o /dev/null --max-time 5 ${J(n)}; then echo healthy; exit 0; fi`," sleep 3","done",`echo "no healthy response from ${n}" >&2`,"exit 1"].join(`
76
+ `)}function Ru(r,e){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${J(Us(r,e))} || true); do`,' systemctl disable --now "$unit" 2>/dev/null || true',"done",`rm -f /etc/rpx/sites.d/${r}.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 Us(r,e){return`^${r}-${e}[-@.]`}function Iu(r,e){return zs(Us(r,e))}function Pu(r){return!r.split(`
78
+ `).some((e)=>e.trim().startsWith("active:"))}function Vs(r,e){return`/tmp/ts-cloud-move-${r}-${e}-certs.tar.gz`}function Ws(r,e){return["set -u",...e.map((t)=>`if [ -s ${J(`${r}/${t}.crt`)} ]; then echo "cert:${t}:$(sha256sum ${J(`${r}/${t}.crt`)} | cut -d' ' -f1)"; else echo "cert:${t}:absent"; fi`)].join(`
79
+ `)}function Lr(r){let e=new Map;for(let t of r.split(`
80
+ `)){let n=t.trim();if(!n.startsWith("cert:"))continue;let[,i,o]=n.split(":");if(i&&o)e.set(i,o)}return e}function qs(r,e){for(let[t,n]of r){if(n==="absent")continue;if(e.get(t)!==n)return!1}return!0}function Gs(r,e,t){let n=e.flatMap((i)=>[`${i}.crt`,`${i}.key`]);return["set -eu",`rm -f ${J(t)}`,`cd ${J(r)} 2>/dev/null || { echo "no certs dir at ${r}" >&2; exit 0; }`,`TS_CLOUD_CERTS="$(ls ${n.map(J).join(" ")} 2>/dev/null | tr '\\n' ' ' || true)"`,'[ -n "$TS_CLOUD_CERTS" ] || { echo "no certificates to carry"; exit 0; }',`tar czf ${J(t)} $TS_CLOUD_CERTS`].join(`
81
+ `)}function Ks(r,e){return["set -eu",`[ -s ${J(e)} ] || { echo "no certificate archive staged"; exit 0; }`,`mkdir -p ${J(r)}`,`tar xzf ${J(e)} -C ${J(r)}`,`chmod 600 ${J(r)}/*.key 2>/dev/null || true`,`rm -f ${J(e)}`].join(`
82
+ `)}async function Hs(r,e){let{slug:t,siteName:n,appBase:i,from:o,to:s,targetAddress:c}=r;if(o===s)throw Error(`'${n}' is already on ${s}.`);if(r.database&&!e.database)throw Error(`'${n}' uses the on-box database '${r.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(r.database&&e.database&&await e.database.targetHasData())throw Error(`${s} already has a database named '${r.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 l=[{id:"pause-workers",title:`Stop background work on ${o} so the snapshot is consistent`,satisfied:async()=>wu(await e.runOnSource(bu(t,n))),apply:async()=>{await e.runOnSource(vu(t,n))}},...e.database?[{id:"database-dump",title:`Dump the '${r.database?.name}' database on ${o}`,satisfied:async()=>!1,apply:()=>e.database.dump()}]:[],{id:"snapshot",title:`Archive the site's tree and units on ${o}`,change:{from:`${o}:${i}`,to:Mt(t,n)},satisfied:async()=>!1,apply:async()=>{await e.runOnSource(Su(t,n,i))}},{id:"transfer",title:`Carry the archive to ${s}`,change:{from:o,to:s},satisfied:()=>e.archiveStaged(),apply:()=>e.transferArchive()},...e.database?[{id:"database-transfer",title:`Carry the database dump to ${s}`,change:{from:o,to:s},satisfied:()=>e.database.dumpStaged(),apply:()=>e.database.transferDump()},{id:"database-restore",title:`Create and load '${r.database?.name}' on ${s}`,satisfied:async()=>!1,apply:()=>e.database.restore()}]:[],{id:"restore",title:`Unpack the site on ${s} and start it`,change:{from:Mt(t,n),to:`${s}:${i}`},satisfied:async()=>Au(await e.runOnTarget(Cu(t,n,i))),apply:async()=>{await e.runOnTarget($u(t,n,i))}}];if(r.port!=null){let{port:d}=r;l.push({id:"health",title:`Wait for the site to answer on ${s} (127.0.0.1:${d})`,satisfied:async()=>!1,apply:async()=>{await e.runOnTarget(ku(d,r.healthCheckPath??"/"))}})}return l.push(...e.certificates?[{id:"certificates",title:`Carry the site's TLS material to ${s}`,satisfied:()=>e.certificates.inPlace(),apply:()=>e.certificates.carry()}]:[],{id:"gateway",title:`Route the site on ${s}`,satisfied:()=>e.targetRoutesSite(),apply:()=>e.refreshTargetGateway()},{id:"dns",title:`Point DNS at ${s}`,change:{from:`${o}`,to:c},satisfied:async()=>await e.publishedAddress()===c,apply:async()=>{let d=await e.cutoverDns();if(d.length>0)throw Error(d.join("; "))}},{id:"drain-source",title:`Stop the site on ${o}, leaving its files in place`,satisfied:async()=>Pu(await e.runOnSource(Iu(t,n))),apply:async()=>{await e.runOnSource(Ru(t,n))}}),{operation:"site:move",target:n,steps:l}}async function kt(r,e,t){let n=await r.exec(e,t);if(n.code!==0)throw Error(`${e.name}: ${n.stderr.trim()||n.stdout.trim()||`exited ${n.code}`}`);return n.stdout}async function Bt(r,e,t){let n=Bun.spawn(["scp","-P",String(r.sshPort),"-o","BatchMode=yes","-o","StrictHostKeyChecking=yes",e,t],{stdout:"pipe",stderr:"pipe"}),[i,o]=await Promise.all([n.exited,new Response(n.stderr).text()]);if(i!==0)throw Error(`scp failed: ${o.trim()||`exited ${i}`}`)}async function Tu(r,e){if(!e.to)throw Error("Name the target server with --to <server>.");let t=await P(),n=t.sites?.[r];if(!n)throw Error(`Site '${r}' is not in cloud.config.ts.`);let i=t.project.slug,o=q(process.cwd(),t);try{let c=new Xe(o.store).list(o.project.id),l=(L)=>{let H=c.find((j)=>j.id===L||j.name===L);if(!H)throw Error(`Server ${L} was not found. Run \`cloud server:list\`.`);if(H.trustState!=="pinned")throw Error(`${H.name} has no pinned host key. Run \`cloud server:validate ${H.name}\` first.`);return H},d=l(e.to),u=c.filter((L)=>L.id!==d.id);if(!e.from&&u.length!==1)throw Error(`Name the source server with --from <server> — ${u.length} others are enrolled.`);let m=l(e.from??u[0].name),f=new Mn,h=Ys(i,r),S=Mt(i,r),y=n.domain,v=t.infrastructure?.dns?.provider,R=t.infrastructure?.compute?.proxy?.engine==="rpx"?t.infrastructure.compute.proxy:void 0,b=t.infrastructure?.dns?.domain,A=(L)=>b&&L.endsWith(b)?b:L.split(".").slice(-2).join("."),k=Fn(t),T=k?.name&&Zs(k)?{name:k.name}:void 0,D=`/tmp/ts-cloud-move-${i}-${r}.sql.gz`,E=Vs(i,r),N=R?.certsDir??ea,_=[y,...n.aliases??[]].filter((L)=>!!L),B=k?.engine??"mysql",V={runOnSource:(L)=>kt(f,m,L),runOnTarget:(L)=>kt(f,d,L),archiveStaged:async()=>(await f.exec(d,`test -f ${S} && echo staged || true`)).stdout.includes("staged"),transferArchive:async()=>{let L=`${process.cwd()}/.ts-cloud-move-${i}-${r}.tar.gz`;await Bt(m,`${m.sshUser}@${m.endpoint}:${S}`,L),await Bt(d,L,`${d.sshUser}@${d.endpoint}:${S}`),await Bun.file(L).delete().catch(()=>{})},targetRoutesSite:async()=>{if(!R||!y)return!0;return(await f.exec(d,`grep -qF ${JSON.stringify(y)} /etc/rpx/sites.d/${i}.json 2>/dev/null && echo routed || true`)).stdout.includes("routed")},refreshTargetGateway:async()=>{if(!R)throw Error(`This project does not run the rpx gateway, so the move cannot publish a route. Point ${d.name}'s web server at ${h}/current yourself, then re-run to continue.`);await kt(f,d,na({config:ta(t.sites??{},{proxy:R,slug:i}),slug:i,preserveManagementDashboardRoutes:!0}).join(`
83
+ `))},publishedAddress:async()=>{if(!y||!v)return d.endpoint;let L=ce(v);if(!L)return;return await jr(Re(L),A(y),y,d.endpoint,"A")?d.endpoint:void 0},...T?{database:{dump:async()=>{await kt(f,m,["set -euo pipefail",'eval "$(cd /opt/pantry && pantry env 2>/dev/null)" || true',...ra(B,T.name,"/tmp",k),`mv -f "$(ls -1t /tmp/${T.name}-*.sql.gz | head -1)" ${D}`].join(`
84
+ `))},dumpStaged:async()=>(await f.exec(d,`test -s ${D} && echo staged || true`)).stdout.includes("staged"),transferDump:async()=>{let L=`${process.cwd()}/.ts-cloud-move-${i}-${r}.sql.gz`;await Bt(m,`${m.sshUser}@${m.endpoint}:${D}`,L),await Bt(d,L,`${d.sshUser}@${d.endpoint}:${D}`),await Bun.file(L).delete().catch(()=>{})},restore:async()=>{await kt(f,d,[...Qs(k,t.infrastructure?.compute?.managedServices??{}),...Xs(k,{from:D}),`rm -f ${D}`].join(`
85
+ `))},targetHasData:async()=>{let L=B==="postgres"?`psql -tAc "select count(*) from information_schema.tables where table_schema='public'" -d ${T.name} 2>/dev/null || echo 0`:`mysql -N -B -e "select count(*) from information_schema.tables where table_schema='${T.name}'" 2>/dev/null || echo 0`,H=await f.exec(d,`eval "$(cd /opt/pantry && pantry env 2>/dev/null)" || true
86
+ ${L}`);return Number.parseInt(H.stdout.trim().split(`
87
+ `).pop()??"0",10)>0}}}:{},...R?{certificates:{inPlace:async()=>{let L=Ws(N,_),[H,j]=await Promise.all([f.exec(m,L),f.exec(d,L)]);return qs(Lr(H.stdout),Lr(j.stdout))},carry:async()=>{await kt(f,m,Gs(N,_,E));let L=`${process.cwd()}/.ts-cloud-move-${i}-${r}-certs.tar.gz`;if(!(await f.exec(m,`test -s ${E} && echo staged || true`)).stdout.includes("staged"))return;await Bt(m,`${m.sshUser}@${m.endpoint}:${E}`,L),await Bt(d,L,`${d.sshUser}@${d.endpoint}:${E}`),await Bun.file(L).delete().catch(()=>{}),await kt(f,d,Ks(N,E)),await f.exec(m,`rm -f ${E}`)}}}:{},cutoverDns:async()=>{if(!y)return[];if(!v)return[`No DNS provider configured — point ${y} at ${d.endpoint} manually.`];let L=ce(v);if(!L)return[`DNS provider '${v}' is not configured.`];return(await rn({provider:Re(L),zone:A(y),fqdn:y,ipv4:d.endpoint,ipv6:_t(void 0)})).warnings}},K=await Hs({slug:i,siteName:r,appBase:h,from:m.name,to:d.name,targetAddress:d.endpoint,port:n.port,healthCheckPath:n.healthCheck?.path,database:T},V),oe=await On(K);if(!e.apply){if(e.json){console.log(JSON.stringify({schemaVersion:1,operation:K.operation,target:K.target,move:{site:r,from:m.name,to:d.name,address:d.endpoint},steps:oe.map((L)=>({id:L.step.id,title:L.step.title,state:L.state,change:L.step.change,reason:L.reason}))},null,2));return}for(let L of Ln(K,oe))console.log(L);console.log(` The source keeps its files either way — reversible until ${m.name} is destroyed.`),console.log(" Re-run with --apply to perform it.");return}let G=await jn(K,oe,{log:(L)=>a(L),audit:(L)=>o.store.appendEvent({projectId:o.project.id,type:`${L.operation}.${L.step}.${L.state}`,level:L.state==="failed"?"error":"info",payload:{site:r,from:m.name,to:d.name,...L.error?{error:L.error}:{}}})});if(e.json)console.log(JSON.stringify({schemaVersion:1,outcome:G},null,2));if(!G.success){let L=G.steps.find((H)=>H.state==="failed");throw Error(`${K.operation} stopped at '${L?.title}': ${L?.error}. ${m.name} still has the site and its files; re-run the same command to continue from here.`)}if(!e.json)g(`Moved ${r} from ${m.name} to ${d.name}. ${m.name} keeps its copy until you destroy it.`)}finally{o.store.close()}}function Fr(r){r.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(e,t)=>{try{await Tu(e,t)}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}}),r.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(e,t)=>{let n=t?.config||"cloud.config.ts",i=t?.root;if(!i){p("Missing --root <path>");return}if(!xu(n)){p(`Config file not found: ${n}`);return}try{let o=Js({configText:await Eu(n,"utf8"),name:e,root:i,domain:t?.domain,path:t?.path,deploy:Nu(t?.deploy),build:t?.build,start:t?.start,port:Lu(t?.port),type:t?.type,pathRewriteStyle:Ou(t?.pathRewriteStyle)});if(t?.dryRun){console.log(o);return}await Du(n,o),g(`Added site '${e}' to ${n}`)}catch(o){p(o instanceof Error?o.message:String(o))}})}function Nu(r){if(!r)return;if(r==="bucket"||r==="server")return r;throw Error(`Invalid --deploy '${r}'. Expected 'server' or 'bucket'.`)}function Ou(r){if(!r)return;if(r==="directory"||r==="flat")return r;throw Error(`Invalid --path-rewrite-style '${r}'. Expected 'directory' or 'flat'.`)}function Lu(r){if(!r)return;let e=Number.parseInt(r,10);if(!Number.isInteger(e)||e<1||e>65535)throw Error(`Invalid --port '${r}'. Expected a TCP port between 1 and 65535.`);return e}import{existsSync as _u}from"node:fs";import{mkdir as zu,writeFile as Uu}from"node:fs/promises";import{join as Vu}from"node:path";class Ut{builder;config;environment;mergedConfig;serverEipLogicalIds=new Map;constructor(r){this.config=r.config,this.environment=r.environment,this.builder=new ia(`${this.config.project.name} - ${this.environment}`),this.mergedConfig=this.mergeEnvironmentConfig()}mergeEnvironmentConfig(){let e=this.config.environments[this.environment]?.infrastructure;if(!e)return this.config;return{...this.config,infrastructure:{...this.config.infrastructure,...e,compute:{...this.config.infrastructure?.compute,...e.compute},storage:{...this.config.infrastructure?.storage,...e.storage},functions:{...this.config.infrastructure?.functions,...e.functions},servers:{...this.config.infrastructure?.servers,...e.servers},databases:{...this.config.infrastructure?.databases,...e.databases},cdn:{...this.config.infrastructure?.cdn,...e.cdn},sftp:e.sftp??this.config.infrastructure?.sftp,queues:{...this.config.infrastructure?.queues,...e.queues},redirects:{...this.config.infrastructure?.redirects,...e.redirects},realtime:{...this.config.infrastructure?.realtime,...e.realtime},cache:{...this.config.infrastructure?.cache,...e.cache},fileSystem:{...this.config.infrastructure?.fileSystem,...e.fileSystem},email:{...this.config.infrastructure?.email,...e.email},search:{...this.config.infrastructure?.search,...e.search},ai:{...this.config.infrastructure?.ai,...e.ai}}}}shouldDeploy(r){if(r.environments&&!r.environments.includes(this.environment))return!1;if(r.requiresFeatures){let e=this.config.features||{};if(!r.requiresFeatures.every((n)=>e[n]===!0))return!1}if(r.regions){let e=this.config.environments[this.environment]?.region||this.config.project.region;if(!r.regions.includes(e))return!1}if(r.condition&&typeof r.condition==="function")return r.condition(this.config,this.environment);return!0}resolveApiOriginPort(){let r=this.mergedConfig.infrastructure?.api?.port??this.mergedConfig.ports?.api,e=Number(r||3008);return Number.isFinite(e)&&e>0?e: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(r,e){return r==="public"||e.routeCompute===!0}storageBucketName(r,e,t){return this.mergedConfig.infrastructure?.storage?.[t]?.bucket??`${r}-${e}-${t}`}fileSystemLogicalId(r,e,t){return sa(oa({slug:`${r}-${t}`,environment:e,resourceType:"efs"}))}resolveSftpStorage(r,e,t){let n=r.storage;if(n?.type==="efs"&&n.fileSystem){if(!this.mergedConfig.infrastructure?.fileSystem?.[n.fileSystem])throw Error(`sftp: storage.fileSystem "${n.fileSystem}" is not defined in infrastructure.fileSystem`);return{type:"efs",fileSystemId:{Ref:this.fileSystemLogicalId(e,t,n.fileSystem)},posixProfile:n.posixProfile}}if(n?.type==="s3"&&n.storageBucket){if(!this.mergedConfig.infrastructure?.storage?.[n.storageBucket])throw Error(`sftp: storage.storageBucket "${n.storageBucket}" is not defined in infrastructure.storage`);return{type:"s3",bucket:this.storageBucketName(e,t,n.storageBucket)}}return}resolveComputeCachePathPatterns(r){if(r&&r.length>0)return r;return this.defaultComputeCachePathPatterns()}createComputeCacheBehavior(r,e){return{PathPattern:r,TargetOriginId:e,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(r,e,t,n,i,o){let s=this.serverEipLogicalIds.get("app"),c=this.serverEipLogicalIds.get("appInstance");if(!s)return;let l=`EC2-${n}-${i}-api`,d=this.mergedConfig.infrastructure?.servers?.app?.region||this.mergedConfig.project?.region||"us-east-1",u=d==="us-east-1"?".compute-1.amazonaws.com":`.${d}.compute.amazonaws.com`,m={"Fn::Join":["",["ec2-",{"Fn::Join":["-",{"Fn::Split":[".",{Ref:s}]}]},u]]},f=this.resolveApiOriginPort();if(r.push({Id:l,DomainName:m,CustomOriginConfig:{HTTPPort:f,HTTPSPort:443,OriginProtocolPolicy:"http-only",OriginSSLProtocols:["TLSv1.2"]}}),t.push(s),c)t.push(c);for(let h of o)e.push(this.createComputeCacheBehavior(h,l))}buildCaddyfile(r){let e=r.filter(([,i])=>typeof i.domain==="string"&&i.domain&&typeof i.port==="number");if(e.length===0)return;let t=new Map;for(let[,i]of e){let o=t.get(i.domain)??[];o.push({port:i.port,path:i.path}),t.set(i.domain,o)}let n=[];for(let[i,o]of t){let c=[...o].sort((l,d)=>{let u=!l.path||l.path==="/",m=!d.path||d.path==="/";if(u&&!m)return 1;if(!u&&m)return-1;return(d.path?.length??0)-(l.path?.length??0)}).map((l)=>{let d=!l.path||l.path==="/",u=`reverse_proxy localhost:${l.port}`;return d?` handle {
88
+ ${u}
89
+ }`:` handle ${l.path} {
90
+ ${u}
91
+ }`});n.push(`${i} {
92
+ ${c.join(`
84
93
  `)}
85
- }`)}return Y.join(`
94
+ }`)}return n.join(`
86
95
 
87
- `)}normalizeMountPath(G){let $=G?.mountPath||G?.path;if(!$||$==="/")return;let J=`/${$}`.replace(/\/+/g,"/").replace(/\/$/,"");return J==="/"?void 0:J}storageBucketLogicalId(G,$,J){return`${G}-${$}-s3-${J}`.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((Y)=>Y.charAt(0).toUpperCase()+Y.slice(1)).join("")}pathMountRewriteFunctionCode(G,$="directory"){return`function handler(event) { var request = event.request; var prefix = ${JSON.stringify(G)}; var rewriteStyle = ${JSON.stringify($)}; 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 G=this.mergedConfig.project.slug,$=this.environment,J=!!(this.mergedConfig.infrastructure?.functions&&Object.keys(this.mergedConfig.infrastructure.functions).length>0||this.mergedConfig.infrastructure?.api),Y=!!(this.mergedConfig.infrastructure?.servers&&Object.keys(this.mergedConfig.infrastructure.servers).length>0),X=!!this.mergedConfig.infrastructure?.compute;if(!!((this.mergedConfig.mode||"server")==="serverless"&&this.mergedConfig.infrastructure?.containers&&Object.keys(this.mergedConfig.infrastructure.containers).length>0))this.generateNetworkInfrastructure(G,$),this.generateContainerInfrastructure(G,$);if(J)this.generateServerless(G,$);if(Y)this.generateServer(G,$);if(X)this.generateComputeApp(G,$);if(this.mergedConfig.infrastructure?.jumpBox)this.generateJumpBox(G,$);if(this.generateSharedInfrastructure(G,$),this.config.tags)this.applyGlobalTags(this.config.tags);return this}applyGlobalTags(G){}generateNetworkInfrastructure(G,$){if(this.builder.hasResource("VPC"))return;let Y=this.mergedConfig.infrastructure?.network?.cidr||"10.0.0.0/16",{vpc:X,logicalId:Q}=_4.createVpc({slug:G,environment:$,cidr:Y,enableDnsHostnames:!0,enableDnsSupport:!0});this.builder.addResource("VPC",X);let Z=`${G}${$}IGW`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Z,{Type:"AWS::EC2::InternetGateway",Properties:{Tags:[{Key:"Name",Value:`${G}-${$}-igw`},{Key:"Environment",Value:$}]}});let z=`${G}${$}IGWAttach`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(z,{Type:"AWS::EC2::VPCGatewayAttachment",Properties:{VpcId:{Ref:"VPC"},InternetGatewayId:{Ref:Z}}});let F=`${G}${$}PublicRT`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(F,{Type:"AWS::EC2::RouteTable",Properties:{VpcId:{Ref:"VPC"},Tags:[{Key:"Name",Value:`${G}-${$}-public-rt`},{Key:"Environment",Value:$}]}}),this.builder.addResource(`${G}${$}PublicRoute`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::EC2::Route",Properties:{RouteTableId:{Ref:F},DestinationCidrBlock:"0.0.0.0/0",GatewayId:{Ref:Z}},DependsOn:z});let U=this.mergedConfig.environments[$]?.region||this.mergedConfig.project.region||"us-east-1",M=["a","b"];for(let H=0;H<2;H++){let j=`PublicSubnet${H+1}`;this.builder.addResource(j,{Type:"AWS::EC2::Subnet",Properties:{VpcId:{Ref:"VPC"},CidrBlock:`10.0.${H}.0/24`,AvailabilityZone:`${U}${M[H]}`,MapPublicIpOnLaunch:!0,Tags:[{Key:"Name",Value:`${G}-${$}-public-${M[H]}`},{Key:"Environment",Value:$}]}}),this.builder.addResource(`${j}RTAssoc`,{Type:"AWS::EC2::SubnetRouteTableAssociation",Properties:{SubnetId:{Ref:j},RouteTableId:{Ref:F}}})}}generateContainerInfrastructure(G,$){let J=this.mergedConfig.infrastructure?.containers;if(!J)return;let Y=this.mergedConfig.infrastructure?.loadBalancer,X=this.mergedConfig.infrastructure?.ssl,Q=this.mergedConfig.infrastructure?.dns,Z;if(X?.enabled&&X.domains?.length)if(X.certificateArn);else{let M=X.domains[0],H=X.domains.slice(1).map((T)=>{if(T.includes(".")&&T.endsWith(M))return T.replace(`.${M}`,"");return T}),{certificate:j,logicalId:V}=S6.createCertificate({domain:M,subdomains:H,slug:G,environment:$,validationMethod:"DNS",hostedZoneId:Q?.hostedZoneId});Z=V,this.builder.addResource(V,j)}let z="ECSCluster";this.builder.addResource(z,{Type:"AWS::ECS::Cluster",Properties:{ClusterName:`${G}-${$}`,ClusterSettings:[{Name:"containerInsights",Value:"enabled"}],Tags:[{Key:"Name",Value:`${G}-${$}`},{Key:"Environment",Value:$}]}});let F=`${G}${$}ALBSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(F,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ALB security group for ${G}-${$}`,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:`${G}-${$}-alb-sg`},{Key:"Environment",Value:$}]}});let U=`${G}${$}ECSSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");for(let[M,H]of Object.entries(J)){let j=H.port||3000;this.builder.addResource(U,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ECS tasks security group for ${G}-${$}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:j,ToPort:j,SourceSecurityGroupId:{Ref:F},Description:"Allow traffic from ALB"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${G}-${$}-ecs-sg`},{Key:"Environment",Value:$}]}});let V=`${G}${$}TaskExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(V,{Type:"AWS::IAM::Role",Properties:{RoleName:`${G}-${$}-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 T=`${G}${$}TaskRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(T,{Type:"AWS::IAM::Role",Properties:{RoleName:`${G}-${$}-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 A=`${G}${$}${M}LogGroup`.replace(/[^a-zA-Z0-9]/g,""),q=`/ecs/${G}-${$}-${M}`;this.builder.addResource(A,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:q,RetentionInDays:30}});let O=`${G}${$}${M}TaskDef`.replace(/[^a-zA-Z0-9]/g,""),L=String(H.cpu||512),D=String(H.memory||1024);this.builder.addResource(O,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${G}-${$}-${M}`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:L,Memory:D,ExecutionRoleArn:{"Fn::GetAtt":[V,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[T,"Arn"]},ContainerDefinitions:[{Name:M,Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${G}:latest`},Essential:!0,PortMappings:[{ContainerPort:j,Protocol:"tcp"}],LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":q,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":M}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${j}${H.healthCheck||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${G}-${$}-${M}`},{Key:"Environment",Value:$}]},DependsOn:[V,T,A]});let P=`${G}${$}ALB`.replace(/[^a-zA-Z0-9]/g,"");if(Y?.enabled!==!1){this.builder.addResource(P,{Type:"AWS::ElasticLoadBalancingV2::LoadBalancer",Properties:{Name:`${G}-${$}-alb`,Scheme:"internet-facing",Type:"application",Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],SecurityGroups:[{Ref:F}],Tags:[{Key:"Name",Value:`${G}-${$}-alb`},{Key:"Environment",Value:$}]}});let b=`${G}${$}TargetGroup`.replace(/[^a-zA-Z0-9]/g,""),_=Y?.healthCheck?.path||H.healthCheck||"/health";this.builder.addResource(b,{Type:"AWS::ElasticLoadBalancingV2::TargetGroup",Properties:{Name:`${G}-${$}-tg`,Port:j,Protocol:"HTTP",VpcId:{Ref:"VPC"},TargetType:"ip",HealthCheckPath:_,HealthCheckIntervalSeconds:Y?.healthCheck?.interval||30,HealthyThresholdCount:Y?.healthCheck?.healthyThreshold||2,UnhealthyThresholdCount:Y?.healthCheck?.unhealthyThreshold||5,HealthCheckTimeoutSeconds:10,Tags:[{Key:"Name",Value:`${G}-${$}-tg`},{Key:"Environment",Value:$}]}});let S=`${G}${$}HTTPListener`.replace(/[^a-zA-Z0-9]/g,"");if(X?.enabled&&X?.redirectHttp)this.builder.addResource(S,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:P},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"redirect",RedirectConfig:{Protocol:"HTTPS",Port:"443",StatusCode:"HTTP_301"}}]}});else this.builder.addResource(S,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:P},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:b}}]}});if(X?.enabled){let d=X.certificateArn||(Z?{Ref:Z}:void 0);if(d){let r=`${G}${$}HTTPSListener`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(r,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:P},Port:443,Protocol:"HTTPS",Certificates:[{CertificateArn:d}],DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:b}}],SslPolicy:"ELBSecurityPolicy-TLS13-1-2-2021-06"}})}}let I=`${G}${$}${M}Service`.replace(/[^a-zA-Z0-9]/g,""),g=H.desiredCount||1;this.builder.addResource(I,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${G}-${$}-${M}`,Cluster:{Ref:z},TaskDefinition:{Ref:O},DesiredCount:g,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:U}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},LoadBalancers:[{ContainerName:M,ContainerPort:j,TargetGroupArn:{Ref:b}}],HealthCheckGracePeriodSeconds:120,Tags:[{Key:"Name",Value:`${G}-${$}-${M}`},{Key:"Environment",Value:$}]},DependsOn:[O,S,b]});let f=H.autoScaling;if(f){let d=`${G}${$}${M}ScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(d,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:f.max||10,MinCapacity:f.min||1,ResourceId:{"Fn::Sub":`service/\${${z}}/${G}-${$}-${M}`},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:I}),f.targetCpuUtilization)this.builder.addResource(`${G}${$}${M}CPUScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${G}-${$}-${M}-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:d},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:f.targetCpuUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}});if(f.targetMemoryUtilization)this.builder.addResource(`${G}${$}${M}MemoryScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${G}-${$}-${M}-memory-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:d},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageMemoryUtilization"},TargetValue:f.targetMemoryUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(Q?.domain&&Q?.hostedZoneId)this.builder.addResource(`${G}${$}ApiDnsRecord`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:Q.hostedZoneId,Name:`api.${Q.domain}`,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[P,"DNSName"]},HostedZoneId:{"Fn::GetAtt":[P,"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:`${G}-${$}-${M}`,Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-service-name"}}}),this.builder.addOutput("LoadBalancerDNS",{Description:"Application Load Balancer DNS Name",Value:{"Fn::GetAtt":[P,"DNSName"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-alb-dns"}}})}else{let b=`${G}${$}${M}Service`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(b,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${G}-${$}-${M}`,Cluster:{Ref:z},TaskDefinition:{Ref:O},DesiredCount:H.desiredCount||1,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:U}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},Tags:[{Key:"Name",Value:`${G}-${$}-${M}`},{Key:"Environment",Value:$}]},DependsOn:[O]})}break}}generateServerless(G,$){if(this.mergedConfig.infrastructure?.functions)for(let[J,Y]of Object.entries(this.mergedConfig.infrastructure.functions)){if(!this.shouldDeploy(Y))continue;let{role:X,logicalId:Q}=I4.createRole({slug:G,environment:$,roleName:`${G}-${$}-${J}-role`,servicePrincipal:"lambda.amazonaws.com",managedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]});this.builder.addResource(Q,X);let{lambdaFunction:Z,logicalId:z}=F$.createLambdaFunction({slug:G,environment:$,functionName:`${G}-${$}-${J}`,handler:Y.handler||"index.handler",runtime:Y.runtime||"nodejs20.x",code:{zipFile:Y.code||"export const handler = async () => ({ statusCode: 200 });"},role:Q,timeout:Y.timeout,memorySize:Y.memorySize});this.builder.addResource(z,Z)}if(this.mergedConfig.infrastructure?.api){let{restApi:J,logicalId:Y}=k4.createRestApi({slug:G,environment:$,apiName:`${G}-${$}-api`});this.builder.addResource(Y,J)}}generateServer(G,$){if(!this.mergedConfig.infrastructure?.servers)return;let J=this.mergedConfig.infrastructure.servers,Y=this.mergedConfig.infrastructure?.compute,X=this.mergedConfig.infrastructure?.ssl;if(Object.values(J).some((Z)=>!Z.privateNetwork||Z.privateNetwork==="create"))this.generateNetworkInfrastructure(G,$);for(let[Z,z]of Object.entries(J)){let F=z.instanceType||z.size||Y?.size,M=(F?F$.InstanceSize.specs[F]:void 0)?.instanceType||F||"t3.micro",H=z.type||"app",j=z.userData||z.startupScript;if(!j)j=F$.UserData.generateAppServerScript({runtime:"bun",runtimeVersion:z.bunVersion||"latest",webServer:"none",domain:z.domain,enableSsl:!!X?.enabled,sslEmail:X?.letsEncrypt?.email,installRedis:H==="cache",installDatabaseClients:!!z.database});let V=z.privateNetwork&&z.privateNetwork!=="create"?z.privateNetwork:{Ref:"VPC"},T=z.subnet||{Ref:"PublicSubnet1"},q=!!this.mergedConfig.infrastructure?.email?.server?.enabled,O=[22,80,443,this.resolveApiOriginPort()];if(q)O.push(25,465,587,143,993);let L=F$.createServerModeStack({slug:`${G}-${Z}`,environment:$,vpcId:V,subnetId:T,instanceType:M,keyName:z.keyName||`${G}-${$}`,domain:z.domain,userData:j,volumeSize:z.diskSize||20,imageId:z.image,allowedPorts:O});for(let[D,P]of Object.entries(L.resources))this.builder.addResource(D,P);this.serverEipLogicalIds.set(Z,L.outputs.eipLogicalId),this.serverEipLogicalIds.set(`${Z}Instance`,L.outputs.instanceLogicalId),this.builder.addOutput(`${Z}InstanceId`,{Value:{Ref:L.outputs.instanceLogicalId},Description:`Instance ID for ${Z} server`}),this.builder.addOutput(`${Z}PublicIp`,{Value:{Ref:L.outputs.eipLogicalId},Description:`Public IP for ${Z} server`})}}generateComputeApp(G,$){let J=this.mergedConfig.infrastructure?.compute;if(!J)return;let Y=this.mergedConfig.sites||{},X=Object.entries(Y),Z=this.mergedConfig.infrastructure?.dns?.domain,z=this.mergedConfig.infrastructure?.database,F=J.size,M=(F?F$.InstanceSize.specs[F]:void 0)?.instanceType||F||"t3.micro";if(!this.builder.hasResource("VPC"))this.generateNetworkInfrastructure(G,$);let H=this.buildCaddyfile(X),j=F$.UserData.generateBunAppScript({runtime:J.runtime||"bun",runtimeVersion:J.runtimeVersion||"latest",systemPackages:J.systemPackages,database:z,caddyfile:H}),V=X.map(([,P])=>P.port).filter((P)=>typeof P==="number"&&![80,443].includes(P)),T=this.resolveApiOriginPort(),A=`${G}${$}DeployBucket`.replace(/[^a-zA-Z0-9]/g,""),q=`${G}-${$}-deploy`;this.builder.addResource(A,{Type:"AWS::S3::Bucket",Properties:{BucketName:q,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:G},{Key:"Environment",Value:$},{Key:"ManagedBy",Value:"ts-cloud"}]}});let O=F$.createServerModeStack({slug:`${G}-app`,environment:$,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},instanceType:M,keyName:J.allowSsh?`${G}-${$}`:void 0,domain:Z,userData:j,volumeSize:J.disk?.size||20,imageId:J.image,allowedPorts:[...J.allowSsh?[22]:[],80,443,T,...V]}),L=O.resources[O.outputs.instanceLogicalId];if(L?.Properties){let P=L.Properties.Tags||[];L.Properties.Tags=[...P,{Key:"Project",Value:G},{Key:"Environment",Value:$},{Key:"Role",Value:"app"},{Key:"ManagedBy",Value:"ts-cloud"}]}let D=O.resources[O.outputs.roleLogicalId];if(D?.Properties)D.Properties.Policies=D.Properties.Policies||[],D.Properties.Policies.push({PolicyName:"DeployBucketRead",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["s3:GetObject","s3:ListBucket"],Resource:[`arn:aws:s3:::${q}`,`arn:aws:s3:::${q}/*`]}]}});for(let[P,b]of Object.entries(O.resources))this.builder.addResource(P,b);this.builder.addOutput("deployBucketName",{Value:{Ref:A},Description:"S3 bucket where release tarballs are uploaded"}),this.builder.addOutput("appInstanceId",{Value:{Ref:O.outputs.instanceLogicalId},Description:"EC2 instance ID for the app server"}),this.builder.addOutput("appPublicIp",{Value:{Ref:O.outputs.eipLogicalId},Description:"Public IP for the app server"}),this.serverEipLogicalIds.set("app",O.outputs.eipLogicalId),this.serverEipLogicalIds.set("appInstance",O.outputs.instanceLogicalId)}generateJumpBox(G,$){let J=this.mergedConfig.infrastructure?.jumpBox;if(!J)return;let Y=J===!0?{}:J;if(Y.enabled===!1)return;this.generateNetworkInfrastructure(G,$);let X=Y.size||"micro",Z=F$.InstanceSize.specs[X]?.instanceType||X||"t3.micro",z;if(Y.mountEfs)z={fileSystemId:typeof Y.mountEfs==="string"?Y.mountEfs:{Ref:"FileSystem"},mountPath:Y.mountPath||"/mnt/efs"};let F;if(Y.databaseTools)F=F$.JumpBox.withDatabaseTools({slug:G,environment:$,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:Y.keyName||`${G}-${$}`,allowedCidrs:Y.allowedCidrs});else if(z)F=F$.JumpBox.withEfsMount({slug:G,environment:$,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:Y.keyName||`${G}-${$}`,fileSystemId:z.fileSystemId,mountPath:z.mountPath,allowedCidrs:Y.allowedCidrs});else F=F$.createJumpBox({slug:G,environment:$,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:Y.keyName||`${G}-${$}`,instanceType:Z,allowedCidrs:Y.allowedCidrs,mountEfs:z});for(let[U,M]of Object.entries(F.resources))this.builder.addResource(U,M);this.builder.addOutput("JumpBoxInstanceId",{Value:{Ref:F.instanceLogicalId},Description:"The ID of the JumpBox EC2 instance (use with SSM Session Manager or SSH)"})}generateSharedInfrastructure(G,$){let J=this.mergedConfig.infrastructure?.ssl,Y=this.mergedConfig.infrastructure?.dns,X=Y?.domain,Q=Y?.hostedZoneId,Z=[],z,F;if(J?.certificateArn)F=J.certificateArn;else if(J?.enabled&&X&&Q){let A=J.domains||[X],q=A[0],O=A.slice(1).map((P)=>{if(P.includes(".")&&P.endsWith(q))return P.replace(`.${q}`,"");return P}),{certificate:L,logicalId:D}=S6.createCertificate({domain:q,subdomains:O,slug:G,environment:$,validationMethod:"DNS",hostedZoneId:Q});z=D,this.builder.addResource(D,L)}if(this.mergedConfig.infrastructure?.storage){let A=Object.entries(this.mergedConfig.infrastructure.storage).some(([,L])=>L.website),q;if(A&&(z||F))q=`${G}${$}CloudFrontOAC`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(q,{Type:"AWS::CloudFront::OriginAccessControl",Properties:{OriginAccessControlConfig:{Name:`${G}-${$}-s3-oac`,Description:`OAC for ${G} ${$} S3 website buckets`,OriginAccessControlOriginType:"s3",SigningBehavior:"always",SigningProtocol:"sigv4"}}});let O=Object.entries(this.mergedConfig.infrastructure.storage).map(([L,D])=>({name:L,config:D,mountPath:this.normalizeMountPath(D),rewriteStyle:D.pathRewriteStyle||"directory",logicalId:this.storageBucketLogicalId(G,$,L)})).filter((L)=>L.name!=="public"&&L.config.website&&L.mountPath);for(let[L,D]of Object.entries(this.mergedConfig.infrastructure.storage)){let P=!!(D.website&&q),b=this.storageBucketName(G,$,L),{bucket:_,logicalId:S}=P6.createBucket({slug:G,name:L,environment:$,bucketName:b,versioning:D.versioning,encryption:D.encryption,public:P?!1:D.public});if(P&&_.Properties)_.Properties.PublicAccessBlockConfiguration={BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!1,RestrictPublicBuckets:!1};if(this.builder.addResource(S,_),D.website){let g=typeof D.website==="object"?D.website:{},f=P6.enableWebsiteHosting(_,g.indexDocument||"index.html",g.errorDocument);this.builder.addResource(S,f)}let I=this.normalizeMountPath(D);if(P&&q&&X&&!(I&&L!=="public")){let g=`${G}${$}${L}CDN`.replace(/[^a-zA-Z0-9]/g,""),f=[];if(D.aliases&&D.aliases.length>0)f.push(...D.aliases);else if(L==="public"){if(f.push(X),J?.domains?.includes(`www.${X}`))f.push(`www.${X}`)}else if(L==="docs")f.push(`docs.${X}`);else if(L==="blog")f.push(`blog.${X}`);let d=typeof D.website==="object"?D.website:{},r=D.spa===!0,W$=d.errorDocument||(r?"index.html":"404.html"),l=[];if(r)l.push({ErrorCode:403,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300});else l.push({ErrorCode:403,ResponseCode:404,ResponsePagePath:`/${W$}`,ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:404,ResponsePagePath:`/${W$}`,ErrorCachingMinTTL:300});let s=F&&f.length>0?{AcmCertificateArn:F,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:z&&f.length>0?{AcmCertificateArn:{Ref:z},SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},e=`S3-${G}-${$}-${L}`,C=this.mergedConfig.project.region||"us-east-1",c;if(!r)c=`${G}${$}${L}UrlRewrite`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(c,{Type:"AWS::CloudFront::Function",Properties:{Name:`${G}-${$}-${L}-url-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`URL rewrite for ${G} ${$} ${L} - 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'; }
88
- else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`}});let z$=[{Id:e,DomainName:{"Fn::Sub":`\${${S}}.s3.${C}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:q}}],X$=[],h$=[];if(this.shouldRouteStorageBucketToCompute(L,D)){this.appendComputeAppOrigin(z$,X$,h$,G,$,this.resolveComputeCachePathPatterns(D.computeRoutes));for(let a of O){let i=`S3-${G}-${$}-${a.name}`,O$=`${G}${$}${a.name}PathMountRewrite`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(O$,{Type:"AWS::CloudFront::Function",Properties:{Name:`${G}-${$}-${a.name}-path-mount-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`Path mount rewrite for ${G} ${$} ${a.name} at ${a.mountPath}`,Runtime:"cloudfront-js-2.0"},FunctionCode:this.pathMountRewriteFunctionCode(a.mountPath,a.rewriteStyle)}}),z$.push({Id:i,DomainName:{"Fn::Sub":`\${${a.logicalId}}.s3.${C}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:q}}),h$.push(a.logicalId,O$);for(let y$ of[a.mountPath,`${a.mountPath}/*`])X$.push({PathPattern:y$,TargetOriginId:i,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":[O$,"FunctionARN"]}}]})}}let k0={Type:"AWS::CloudFront::Distribution",DependsOn:[S,q,...c?[c]:[],...h$],Properties:{DistributionConfig:{Enabled:!0,Comment:`${G} ${$} ${L} site`,DefaultRootObject:"index.html",Origins:z$,DefaultCacheBehavior:{TargetOriginId:e,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",...c?{FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[c,"FunctionARN"]}}]}:{}},...X$.length>0?{CacheBehaviors:X$}:{},...f.length>0?{Aliases:f}:{},ViewerCertificate:s,PriceClass:"PriceClass_100",HttpVersion:"http2and3",IPV6Enabled:!0,CustomErrorResponses:l}}};if(z)k0.DependsOn.push(z);this.builder.addResource(g,k0);let j$=`${S}CloudFrontPolicy`;if(this.builder.addResource(j$,{Type:"AWS::S3::BucketPolicy",DependsOn:[S,g],Properties:{Bucket:{Ref:S},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${S}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${g}}`}}}}]}}}),this.shouldRouteStorageBucketToCompute(L,D))for(let a of O){let i=`${a.logicalId}CloudFrontPolicy`;this.builder.addResource(i,{Type:"AWS::S3::BucketPolicy",DependsOn:[a.logicalId,g],Properties:{Bucket:{Ref:a.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:::\${${a.logicalId}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${g}}`}}}}]}}})}if(f.length>0)Z.push({name:L,bucketLogicalId:S,distLogicalId:g,oacLogicalId:q,aliases:f});this.builder.addOutput(`${L}CloudFrontDomain`,{Value:{"Fn::GetAtt":[g,"DomainName"]},Description:`CloudFront domain for ${L}`}),this.builder.addOutput(`${L}CloudFrontDistributionId`,{Value:{Ref:g},Description:`CloudFront distribution ID for ${L}`})}if(this.builder.addOutput(`${L}BucketName`,{Value:{Ref:S},Description:`S3 bucket name for ${L}`}),L==="public")this.builder.addOutput("FrontendBucketName",{Value:{Ref:S},Description:"Frontend S3 bucket name"});if(L==="docs")this.builder.addOutput("DocsBucketName",{Value:{Ref:S},Description:"Documentation S3 bucket name"});if(L==="blog")this.builder.addOutput("BlogBucketName",{Value:{Ref:S},Description:"Blog S3 bucket name"})}}if(this.mergedConfig.infrastructure?.sftp){let A=this.mergedConfig.infrastructure.sftp,q=b4.create({...A,storage:this.resolveSftpStorage(A,G,$)??A.storage,slug:G,environment:$});for(let[O,L]of Object.entries(q.resources))this.builder.addResource(O,L);this.builder.addOutput("SftpServerId",{Value:{Ref:q.serverLogicalId},Description:"AWS Transfer Family SFTP server ID"}),this.builder.addOutput("SftpEndpoint",{Value:{"Fn::Sub":`\${${q.serverLogicalId}}.server.transfer.\${AWS::Region}.amazonaws.com`},Description:"AWS Transfer Family SFTP endpoint"}),this.builder.addOutput("SftpStorageDomain",{Value:q.domain,Description:"Storage backing the SFTP server (EFS or S3)"})}if(Q&&Z.length>0)for(let{name:A,distLogicalId:q,aliases:O}of Z)for(let L of O){let D=L.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${D}ARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[q],Properties:{HostedZoneId:Q,Name:L,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[q,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}}),this.builder.addResource(`${D}AAAARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[q],Properties:{HostedZoneId:Q,Name:L,Type:"AAAA",AliasTarget:{DNSName:{"Fn::GetAtt":[q,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}})}if(this.mergedConfig.infrastructure?.databases){for(let[A,q]of Object.entries(this.mergedConfig.infrastructure.databases))if(q.engine==="dynamodb"){let{table:O,logicalId:L}=P1.createTable({slug:G,environment:$,tableName:`${G}-${$}-${A}`,partitionKey:q.partitionKey||{name:"id",type:"S"},sortKey:q.sortKey});this.builder.addResource(L,O)}else if(q.engine==="postgres"){let{dbInstance:O,logicalId:L}=P1.createPostgres({slug:G,environment:$,dbInstanceIdentifier:`${G}-${$}-${A}`,masterUsername:q.username||"admin",masterUserPassword:q.password||"changeme123",allocatedStorage:q.storage||20,dbInstanceClass:q.instanceClass||"db.t3.micro"});this.builder.addResource(L,O)}else if(q.engine==="mysql"){let{dbInstance:O,logicalId:L}=P1.createMysql({slug:G,environment:$,dbInstanceIdentifier:`${G}-${$}-${A}`,masterUsername:q.username||"admin",masterUserPassword:q.password||"changeme123",allocatedStorage:q.storage||20,dbInstanceClass:q.instanceClass||"db.t3.micro"});this.builder.addResource(L,O)}}if(this.mergedConfig.infrastructure?.cdn)for(let[A,q]of Object.entries(this.mergedConfig.infrastructure.cdn)){if(!q.origin)continue;let O=typeof q.customDomain==="string"?q.customDomain:q.customDomain?.domain||q.domain,L=typeof q.customDomain==="object"?q.customDomain?.certificateArn:q.certificateArn,D=L||F,P=`${G}${$}${A}CDN`.replace(/[^a-zA-Z0-9]/g,""),b=`S3-${G}-${$}-${A}-cdn`,_=[{Id:b,DomainName:q.origin,OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginShield:q.originShield?{Enabled:!0,OriginShieldRegion:q.originShieldRegion||this.mergedConfig.environments[$]?.region||this.mergedConfig.project.region||"us-east-1"}:{Enabled:!1}}],S=[],I=[];if(q.routeCompute)this.appendComputeAppOrigin(_,S,I,G,$,this.resolveComputeCachePathPatterns(q.computeRoutes));let g=D&&O?{AcmCertificateArn:z&&!L?{Ref:z}:D,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},f=[...I];if(z&&!L&&O)f.push(z);let d={Type:"AWS::CloudFront::Distribution",...f.length>0?{DependsOn:f}:{},Properties:{DistributionConfig:{Enabled:!0,Comment:`${G} ${$} ${A} CDN`,DefaultRootObject:"index.html",Origins:_,DefaultCacheBehavior:{TargetOriginId:b,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:q.compress!==!1,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6"},...S.length>0?{CacheBehaviors:S}:{},...O&&D?{Aliases:[O]}:{},ViewerCertificate:g,PriceClass:"PriceClass_100",HttpVersion:q.http3?"http2and3":"http2"}}};if(this.builder.addResource(P,d),Q&&O&&D){let r=O.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${r}CdnARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[P,...z&&!L?[z]:[]],Properties:{HostedZoneId:Q,Name:O,Type:"A",AliasTarget:{HostedZoneId:"Z2FDTNDATAQYW2",DNSName:{"Fn::GetAtt":[P,"DomainName"]}}}})}}if(this.mergedConfig.infrastructure?.queues)for(let[A,q]of Object.entries(this.mergedConfig.infrastructure.queues)){if(!this.shouldDeploy(q))continue;let{queue:O,logicalId:L}=_6.createQueue({slug:G,environment:$,name:`${G}-${$}-${A}`,fifo:q.fifo,visibilityTimeout:q.visibilityTimeout,messageRetentionPeriod:q.messageRetentionPeriod,delaySeconds:q.delaySeconds,maxMessageSize:q.maxMessageSize,receiveMessageWaitTime:q.receiveMessageWaitTime,contentBasedDeduplication:q.contentBasedDeduplication,encrypted:q.encrypted,kmsKeyId:q.kmsKeyId});this.builder.addResource(L,O);let D;if(q.deadLetterQueue){let{deadLetterQueue:P,updatedSourceQueue:b,deadLetterLogicalId:_}=_6.createDeadLetterQueue(L,{slug:G,environment:$,maxReceiveCount:q.maxReceiveCount});D=_,this.builder.addResource(_,P);let I=this.builder.getResources()[L];if(I?.Properties)I.Properties.RedrivePolicy=b.Properties?.RedrivePolicy}if(q.trigger){let P=q.trigger,b=`${G}${$}${P.functionName}`.replace(/[^a-zA-Z0-9]/g,""),_={Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:{"Fn::GetAtt":[L,"Arn"]},FunctionName:{Ref:b},BatchSize:P.batchSize||10,MaximumBatchingWindowInSeconds:P.batchWindow||0,Enabled:!0,...P.reportBatchItemFailures!==!1&&{FunctionResponseTypes:["ReportBatchItemFailures"]},...P.maxConcurrency&&{ScalingConfig:{MaximumConcurrency:P.maxConcurrency}},...P.filterPattern&&{FilterCriteria:{Filters:[{Pattern:JSON.stringify(P.filterPattern)}]}}},DependsOn:[L,b]};this.builder.addResource(`${L}Trigger`,_)}if(q.alarms?.enabled){let P=q.alarms,b=P.notificationTopicArn;if(!b&&P.notificationEmails?.length){let I=`${L}AlarmTopic`;this.builder.addResource(I,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${G}-${$}-${A}-alarms`,DisplayName:`${A} Queue Alarms`}}),P.notificationEmails.forEach((g,f)=>{this.builder.addResource(`${I}Sub${f}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:I},Protocol:"email",Endpoint:g}})}),b={Ref:I}}let _=P.queueDepthThreshold||1000;this.builder.addResource(`${L}DepthAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${G}-${$}-${A}-queue-depth`,AlarmDescription:`Queue ${A} depth exceeds ${_} messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:_,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[L,"QueueName"]}}],...b&&{AlarmActions:[b],OKActions:[b]}}});let S=P.messageAgeThreshold||3600;if(this.builder.addResource(`${L}AgeAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${G}-${$}-${A}-message-age`,AlarmDescription:`Queue ${A} oldest message exceeds ${S} seconds`,MetricName:"ApproximateAgeOfOldestMessage",Namespace:"AWS/SQS",Statistic:"Maximum",Period:300,EvaluationPeriods:2,Threshold:S,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[L,"QueueName"]}}],...b&&{AlarmActions:[b],OKActions:[b]}}}),D&&P.dlqAlarm!==!1)this.builder.addResource(`${D}Alarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${G}-${$}-${A}-dlq-messages`,AlarmDescription:`Dead letter queue for ${A} has messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Sum",Period:300,EvaluationPeriods:1,Threshold:0,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[D,"QueueName"]}}],...b&&{AlarmActions:[b]}}})}if(q.subscribe){let P=q.subscribe,b=P.topicArn;if(!b&&P.topicName)b={Ref:P.topicName};if(b){this.builder.addResource(`${L}SnsPolicy`,{Type:"AWS::SQS::QueuePolicy",Properties:{Queues:[{Ref:L}],PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"sqs:SendMessage",Resource:{"Fn::GetAtt":[L,"Arn"]},Condition:{ArnEquals:{"aws:SourceArn":b}}}]}}});let _={TopicArn:b,Protocol:"sqs",Endpoint:{"Fn::GetAtt":[L,"Arn"]},RawMessageDelivery:P.rawMessageDelivery||!1};if(P.filterPolicy)_.FilterPolicy=P.filterPolicy,_.FilterPolicyScope=P.filterPolicyScope||"MessageAttributes";this.builder.addResource(`${L}SnsSub`,{Type:"AWS::SNS::Subscription",Properties:_,DependsOn:`${L}SnsPolicy`})}}}if(this.mergedConfig.infrastructure?.realtime?.enabled)if((this.mergedConfig.infrastructure.realtime.mode||"serverless")==="server")this.generateRealtimeServerResources(G,$);else this.generateRealtimeResources(G,$);let U=this.mergedConfig.infrastructure?.redirects;if(U){let A=U.target||this.mergedConfig.infrastructure?.dns?.domain||"",q=U.protocol||"https";if(U.domains?.length&&A)for(let O of U.domains){let{bucket:L,bucketPolicy:D,logicalId:P,policyLogicalId:b}=S1.createDomainRedirectBucket({slug:G,environment:$,sourceDomain:O,targetDomain:A,protocol:q});this.builder.addResource(P,L),this.builder.addResource(b,D)}if(U.paths&&Object.keys(U.paths).length>0){let O=S1.fromMapping(U.paths,{statusCode:U.statusCode||301}),{function:L,logicalId:D}=S1.createPathRedirectFunction({slug:G,environment:$,rules:O});this.builder.addResource(D,L)}}if(this.mergedConfig.infrastructure?.monitoring?.alarms)for(let[A,q]of Object.entries(this.mergedConfig.infrastructure.monitoring.alarms)){let{alarm:O,logicalId:L}=h4.createAlarm({slug:G,environment:$,alarmName:`${G}-${$}-${A}`,metricName:q.metricName||"Errors",namespace:q.namespace||"AWS/Lambda",threshold:q.threshold||1,comparisonOperator:q.comparisonOperator||"GreaterThanThreshold"});this.builder.addResource(L,O)}let M=this.mergedConfig.infrastructure?.cache;if(M){let A=M.type||"redis";if(A==="redis"){this.generateNetworkInfrastructure(G,$);let q=M.redis||{},{replicationGroup:O,subnetGroup:L,logicalId:D,subnetGroupId:P}=b6.createRedis({slug:G,environment:$,nodeType:q.nodeType||M.nodeType||"cache.t3.micro",engineVersion:q.engineVersion||"7.1",port:q.port||6379,numCacheClusters:q.numCacheNodes||2,automaticFailover:q.automaticFailoverEnabled!==!1,atRestEncryption:!0,transitEncryption:!0,snapshotRetentionDays:q.snapshotRetentionLimit||7,snapshotWindow:q.snapshotWindow,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(D,O),L&&P)this.builder.addResource(P,L);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[D,"PrimaryEndPoint.Address"]},Description:"Redis primary endpoint address"}),this.builder.addOutput("CachePort",{Value:{"Fn::GetAtt":[D,"PrimaryEndPoint.Port"]},Description:"Redis primary endpoint port"})}else if(A==="memcached"){this.generateNetworkInfrastructure(G,$);let q=M.elasticache||{},{cluster:O,subnetGroup:L,logicalId:D,subnetGroupId:P}=b6.createMemcached({slug:G,environment:$,nodeType:q.nodeType||M.nodeType||"cache.t3.micro",engineVersion:q.engineVersion||"1.6.22",numCacheNodes:q.numCacheNodes||2,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(D,O),L&&P)this.builder.addResource(P,L);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[D,"ConfigurationEndpoint.Address"]},Description:"Memcached configuration endpoint address"})}}let H=this.mergedConfig.infrastructure?.email;if(H){let A=H.domain||this.mergedConfig.infrastructure?.dns?.domain;if(A){let{emailIdentity:q,logicalId:O}=H0.verifyDomain({domain:A,slug:G,environment:$,enableDkim:H.enableDkim!==!1,dkimKeyLength:H.dkimKeyLength||"RSA_2048_BIT"});if(this.builder.addResource(O,q),H.configurationSet!==!1){let{configurationSet:P,logicalId:b}=H0.createConfigurationSet({slug:G,environment:$});this.builder.addResource(b,P)}let L=H.hostedZoneId||this.mergedConfig.infrastructure?.dns?.hostedZoneId;if(L){if(H.enableDkim!==!1)for(let I=1;I<=3;I++){let g=`DkimRecord${I}${A.replace(/\./g,"")}`;this.builder.addResource(g,{Type:"AWS::Route53::RecordSet",DependsOn:[O],Properties:{HostedZoneId:L,Name:{"Fn::GetAtt":[O,`DkimDNSTokenName${I}`]},Type:"CNAME",TTL:1800,ResourceRecords:[{"Fn::GetAtt":[O,`DkimDNSTokenValue${I}`]}]}})}let{record:P,logicalId:b}=H0.createSpfRecord(A,L);this.builder.addResource(b,P);let{record:_,logicalId:S}=H0.createDmarcRecord(A,L,{policy:"none",reportingEmail:H.dmarcReportingEmail||`dmarc-reports@${A}`});this.builder.addResource(S,_)}this.builder.addOutput("EmailDomain",{Value:A,Description:"SES verified email domain"});let D=H.server;if(D?.enabled&&L){let P=this.mergedConfig.environments[$]?.region||this.mergedConfig.project.region||"us-east-1",b=`${G}-${$}-email`,{role:_,policy:S,roleLogicalId:I,policyLogicalId:g}=H0.createEmailLambdaRole({slug:G,environment:$,s3BucketArn:`arn:aws:s3:::${b}`,sesIdentityArn:`arn:aws:ses:${P}:*:identity/${A}`});this.builder.addResource(I,_),this.builder.addResource(g,S);let{function:f,permission:d,logicalId:r,permissionLogicalId:W$}=H0.createInboundEmailLambda({slug:G,environment:$,roleArn:{"Fn::GetAtt":[I,"Arn"]},s3BucketName:b,organizedPrefix:"mailboxes/"});this.builder.addResource(r,f),this.builder.addResource(W$,d);let l=H0.createInboundEmailSetup({slug:G,environment:$,domain:A,s3BucketName:b,s3KeyPrefix:"inbox/",region:P,hostedZoneId:L,lambdaFunctionArn:{"Fn::GetAtt":[r,"Arn"]}});for(let[e,C]of Object.entries(l.resources)){if(C.Type==="AWS::SES::ReceiptRule")C.DependsOn=[W$,r];this.builder.addResource(e,C)}this.builder.addOutput("InboundEmailLambda",{Value:{Ref:r},Description:"Inbound email processing Lambda function"}),this.builder.addOutput("EmailBucket",{Value:b,Description:"S3 bucket for email storage"});let s=this.serverEipLogicalIds.get("app");if(s&&L){let C=`${D?.subdomain||"mail"}.${A}`,c=C.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${c}ARecord`,{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:L,Name:C,Type:"A",TTL:"300",ResourceRecords:[{Ref:s}]}})}this.builder.addOutput("MailHost",{Value:`mail.${A}`,Description:"Mail server hostname for SMTP/IMAP clients"})}}}let j=this.mergedConfig.infrastructure?.search;if(j){let A=j.vpc?{subnetIds:[{Ref:"PublicSubnet1"}],securityGroupIds:[]}:void 0;if(A)this.generateNetworkInfrastructure(G,$);let{domain:q,logicalId:O}=y4.createDomain({slug:G,environment:$,engineVersion:j.engineVersion||"OpenSearch_2.11",instanceType:j.instanceType||"t3.small.search",instanceCount:j.instanceCount||1,volumeSize:j.volumeSize||10,volumeType:j.volumeType||"gp3",dedicatedMaster:j.dedicatedMaster||!1,dedicatedMasterType:j.dedicatedMasterType,dedicatedMasterCount:j.dedicatedMasterCount||3,multiAz:j.multiAz||!1,encryption:j.encryption||{atRest:!0,nodeToNode:!0},advancedSecurity:j.advancedSecurity,autoTune:j.autoTune!==!1,vpc:A});this.builder.addResource(O,q),this.builder.addOutput("SearchDomainEndpoint",{Value:{"Fn::GetAtt":[O,"DomainEndpoint"]},Description:"OpenSearch domain endpoint"}),this.builder.addOutput("SearchDomainArn",{Value:{"Fn::GetAtt":[O,"Arn"]},Description:"OpenSearch domain ARN"})}let V=this.mergedConfig.infrastructure?.fileSystem;if(V&&Object.keys(V).length>0){this.generateNetworkInfrastructure(G,$);for(let[A,q]of Object.entries(V)){let{fileSystem:O,logicalId:L}=N1.createFileSystem({slug:`${G}-${A}`,environment:$,encrypted:q.encrypted!==!1,performanceMode:q.performanceMode||"generalPurpose",throughputMode:q.throughputMode||"bursting",enableBackup:!0});this.builder.addResource(L,O);let{securityGroup:D,logicalId:P}=N1.createEfsSecurityGroup({slug:`${G}-${A}`,environment:$,vpcId:{Ref:"VPC"},sourceCidrBlocks:["10.0.0.0/16"]});this.builder.addResource(P,D);let{mountTargets:b,logicalIds:_}=N1.createMultiAzMountTargets(L,{slug:`${G}-${A}`,environment:$,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],securityGroupId:{Ref:P}});for(let S=0;S<b.length;S++)this.builder.addResource(_[S],b[S]);this.builder.addOutput(`${A}FileSystemId`,{Value:{Ref:L},Description:`EFS file system ID for ${A}`})}}let T=this.mergedConfig.infrastructure?.ai;if(T){let A=T.service||"ecs",q=T.models||["*"],O=T.allowStreaming!==!1,L;if(A==="ecs")L=y0.enableBedrockForEcs({slug:G,environment:$,models:q,allowStreaming:O});else if(A==="ec2")L=y0.enableBedrockForEc2({slug:G,environment:$,models:q,allowStreaming:O});else if(A==="lambda")L=y0.enableBedrockForLambda({slug:G,environment:$,models:q,allowStreaming:O});else L=y0.createBedrockRole(A,{slug:G,environment:$,models:q,allowStreaming:O});if(this.builder.addResource(L.logicalId,L.role),T.allowAsync){let{policy:D,logicalId:P}=y0.createBedrockPolicy({slug:G,environment:$,models:q,allowStreaming:O,allowAsync:!0});this.builder.addResource(P,D)}this.builder.addOutput("BedrockRoleArn",{Value:{"Fn::GetAtt":[L.logicalId,"Arn"]},Description:"IAM role ARN with Bedrock permissions"})}}generateRealtimeResources(G,$){let J=this.mergedConfig.infrastructure?.realtime;if(!J)return;let Y=J.name||`${G}-${$}-realtime`,X=J.scaling||{},Q=J.storage||{type:"dynamodb"},Z=X.handlerMemory||256,z=X.handlerTimeout||30,F=`${G}${$}RealtimeConnections`.replace(/[^a-zA-Z0-9]/g,""),U=`${G}${$}RealtimeChannels`.replace(/[^a-zA-Z0-9]/g,"");if(Q.type==="dynamodb"){let _=Q.dynamodb||{},S=_.billingMode||"PAY_PER_REQUEST";this.builder.addResource(F,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${G}-${$}-realtime-connections`,BillingMode:S,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"},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:_.readCapacity||5,WriteCapacityUnits:_.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:_.readCapacity||5,WriteCapacityUnits:_.writeCapacity||5}},..._.pointInTimeRecovery&&{PointInTimeRecoverySpecification:{PointInTimeRecoveryEnabled:!0}},Tags:[{Key:"Name",Value:`${G}-${$}-realtime-connections`},{Key:"Environment",Value:$}]}}),this.builder.addResource(U,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${G}-${$}-realtime-channels`,BillingMode:S,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"},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:_.readCapacity||5,WriteCapacityUnits:_.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...S==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:_.readCapacity||5,WriteCapacityUnits:_.writeCapacity||5}},Tags:[{Key:"Name",Value:`${G}-${$}-realtime-channels`},{Key:"Environment",Value:$}]}})}let M=`${G}${$}RealtimeRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(M,{Type:"AWS::IAM::Role",Properties:{RoleName:`${G}-${$}-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":[F,"Arn"]},{"Fn::Sub":`\${${F}.Arn}/index/*`},{"Fn::GetAtt":[U,"Arn"]},{"Fn::Sub":`\${${U}.Arn}/index/*`}]},{Effect:"Allow",Action:"execute-api:ManageConnections",Resource:{"Fn::Sub":"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"}}]}}]}});let H=`${G}${$}RealtimeConnect`.replace(/[^a-zA-Z0-9]/g,""),j=`${G}${$}RealtimeDisconnect`.replace(/[^a-zA-Z0-9]/g,""),V=`${G}${$}RealtimeMessage`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(H,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${G}-${$}-realtime-connect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[M,"Arn"]},MemorySize:Z,Timeout:z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:F},CHANNELS_TABLE:{Ref:U},ENVIRONMENT:$}},Code:{ZipFile:this.generateConnectHandlerCode()}},DependsOn:[M,F]}),this.builder.addResource(j,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${G}-${$}-realtime-disconnect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[M,"Arn"]},MemorySize:Z,Timeout:z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:F},CHANNELS_TABLE:{Ref:U},ENVIRONMENT:$}},Code:{ZipFile:this.generateDisconnectHandlerCode()}},DependsOn:[M,F,U]}),this.builder.addResource(V,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${G}-${$}-realtime-message`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[M,"Arn"]},MemorySize:Z,Timeout:z,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:F},CHANNELS_TABLE:{Ref:U},ENVIRONMENT:$}},Code:{ZipFile:this.generateMessageHandlerCode()}},DependsOn:[M,F,U]});let T=`${G}${$}RealtimeApi`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(T,{Type:"AWS::ApiGatewayV2::Api",Properties:{Name:Y,ProtocolType:"WEBSOCKET",RouteSelectionExpression:"$request.body.action",Tags:{Name:Y,Environment:$}}});let A=`${H}Permission`,q=`${j}Permission`,O=`${V}Permission`;this.builder.addResource(A,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:H},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${T}}/*/$connect`}}}),this.builder.addResource(q,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:j},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${T}}/*/$disconnect`}}}),this.builder.addResource(O,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:V},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${T}}/*/$default`}}});let L=`${T}ConnectInteg`,D=`${T}DisconnectInteg`,P=`${T}MessageInteg`;this.builder.addResource(L,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:T},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${H}.Arn}/invocations`}}}),this.builder.addResource(D,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:T},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${j}.Arn}/invocations`}}}),this.builder.addResource(P,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:T},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${V}.Arn}/invocations`}}}),this.builder.addResource(`${T}ConnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:T},RouteKey:"$connect",AuthorizationType:"NONE",Target:{"Fn::Sub":`integrations/\${${L}}`}}}),this.builder.addResource(`${T}DisconnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:T},RouteKey:"$disconnect",Target:{"Fn::Sub":`integrations/\${${D}}`}}}),this.builder.addResource(`${T}DefaultRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:T},RouteKey:"$default",Target:{"Fn::Sub":`integrations/\${${P}}`}}});let b=`${T}Stage`;if(this.builder.addResource(b,{Type:"AWS::ApiGatewayV2::Stage",Properties:{ApiId:{Ref:T},StageName:$,AutoDeploy:!0,DefaultRouteSettings:{ThrottlingBurstLimit:X.messagesPerSecond||1000,ThrottlingRateLimit:X.messagesPerSecond||1000}}}),J.monitoring?.enabled){let _=J.monitoring,S=_.notificationTopicArn;if(!S&&_.notificationEmails?.length){let I=`${T}AlarmTopic`;this.builder.addResource(I,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${G}-${$}-realtime-alarms`,DisplayName:"Realtime WebSocket Alarms"}}),_.notificationEmails.forEach((g,f)=>{this.builder.addResource(`${I}Sub${f}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:I},Protocol:"email",Endpoint:g}})}),S={Ref:I}}if(_.connectionThreshold)this.builder.addResource(`${T}ConnectionAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${G}-${$}-realtime-connections`,AlarmDescription:`WebSocket connections exceed ${_.connectionThreshold}`,MetricName:"ConnectCount",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:300,EvaluationPeriods:2,Threshold:_.connectionThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:T}}],...S&&{AlarmActions:[S]}}});if(_.errorThreshold)this.builder.addResource(`${T}ErrorAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${G}-${$}-realtime-errors`,AlarmDescription:`WebSocket errors exceed ${_.errorThreshold}/min`,MetricName:"ExecutionError",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:60,EvaluationPeriods:3,Threshold:_.errorThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:T}}],...S&&{AlarmActions:[S]}}})}this.builder.addOutput(`${T}Endpoint`,{Description:"WebSocket API endpoint URL",Value:{"Fn::Sub":`wss://\${${T}}.execute-api.\${AWS::Region}.amazonaws.com/${$}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}}),this.builder.addOutput(`${T}Id`,{Description:"WebSocket API ID",Value:{Ref:T},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-api-id"}}})}generateConnectHandlerCode(){return`
96
+ `)}normalizeMountPath(r){let e=r?.mountPath||r?.path;if(!e||e==="/")return;let t=`/${e}`.replace(/\/+/g,"/").replace(/\/$/,"");return t==="/"?void 0:t}storageBucketLogicalId(r,e,t){return`${r}-${e}-s3-${t}`.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((n)=>n.charAt(0).toUpperCase()+n.slice(1)).join("")}pathMountRewriteFunctionCode(r,e="directory"){return`function handler(event) { var request = event.request; var prefix = ${JSON.stringify(r)}; var rewriteStyle = ${JSON.stringify(e)}; 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 r=this.mergedConfig.project.slug,e=this.environment,t=!!(this.mergedConfig.infrastructure?.functions&&Object.keys(this.mergedConfig.infrastructure.functions).length>0||this.mergedConfig.infrastructure?.api),n=!!(this.mergedConfig.infrastructure?.servers&&Object.keys(this.mergedConfig.infrastructure.servers).length>0),i=!!this.mergedConfig.infrastructure?.compute;if(!!((this.mergedConfig.mode||"server")==="serverless"&&this.mergedConfig.infrastructure?.containers&&Object.keys(this.mergedConfig.infrastructure.containers).length>0))this.generateNetworkInfrastructure(r,e),this.generateContainerInfrastructure(r,e);if(t)this.generateServerless(r,e);if(n)this.generateServer(r,e);if(i)this.generateComputeApp(r,e);if(this.mergedConfig.infrastructure?.jumpBox)this.generateJumpBox(r,e);if(this.generateSharedInfrastructure(r,e),this.config.tags)this.applyGlobalTags(this.config.tags);return this}applyGlobalTags(r){}generateNetworkInfrastructure(r,e){if(this.builder.hasResource("VPC"))return;let n=this.mergedConfig.infrastructure?.network?.cidr||"10.0.0.0/16",{vpc:i,logicalId:o}=aa.createVpc({slug:r,environment:e,cidr:n,enableDnsHostnames:!0,enableDnsSupport:!0});this.builder.addResource("VPC",i);let s=`${r}${e}IGW`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(s,{Type:"AWS::EC2::InternetGateway",Properties:{Tags:[{Key:"Name",Value:`${r}-${e}-igw`},{Key:"Environment",Value:e}]}});let c=`${r}${e}IGWAttach`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(c,{Type:"AWS::EC2::VPCGatewayAttachment",Properties:{VpcId:{Ref:"VPC"},InternetGatewayId:{Ref:s}}});let l=`${r}${e}PublicRT`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(l,{Type:"AWS::EC2::RouteTable",Properties:{VpcId:{Ref:"VPC"},Tags:[{Key:"Name",Value:`${r}-${e}-public-rt`},{Key:"Environment",Value:e}]}}),this.builder.addResource(`${r}${e}PublicRoute`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::EC2::Route",Properties:{RouteTableId:{Ref:l},DestinationCidrBlock:"0.0.0.0/0",GatewayId:{Ref:s}},DependsOn:c});let d=this.mergedConfig.environments[e]?.region||this.mergedConfig.project.region||"us-east-1",u=["a","b"];for(let m=0;m<2;m++){let f=`PublicSubnet${m+1}`;this.builder.addResource(f,{Type:"AWS::EC2::Subnet",Properties:{VpcId:{Ref:"VPC"},CidrBlock:`10.0.${m}.0/24`,AvailabilityZone:`${d}${u[m]}`,MapPublicIpOnLaunch:!0,Tags:[{Key:"Name",Value:`${r}-${e}-public-${u[m]}`},{Key:"Environment",Value:e}]}}),this.builder.addResource(`${f}RTAssoc`,{Type:"AWS::EC2::SubnetRouteTableAssociation",Properties:{SubnetId:{Ref:f},RouteTableId:{Ref:l}}})}}generateContainerInfrastructure(r,e){let t=this.mergedConfig.infrastructure?.containers;if(!t)return;let n=this.mergedConfig.infrastructure?.loadBalancer,i=this.mergedConfig.infrastructure?.ssl,o=this.mergedConfig.infrastructure?.dns,s;if(i?.enabled&&i.domains?.length)if(i.certificateArn);else{let u=i.domains[0],m=i.domains.slice(1).map((S)=>{if(S.includes(".")&&S.endsWith(u))return S.replace(`.${u}`,"");return S}),{certificate:f,logicalId:h}=Br.createCertificate({domain:u,subdomains:m,slug:r,environment:e,validationMethod:"DNS",hostedZoneId:o?.hostedZoneId});s=h,this.builder.addResource(h,f)}let c="ECSCluster";this.builder.addResource(c,{Type:"AWS::ECS::Cluster",Properties:{ClusterName:`${r}-${e}`,ClusterSettings:[{Name:"containerInsights",Value:"enabled"}],Tags:[{Key:"Name",Value:`${r}-${e}`},{Key:"Environment",Value:e}]}});let l=`${r}${e}ALBSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(l,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ALB security group for ${r}-${e}`,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:`${r}-${e}-alb-sg`},{Key:"Environment",Value:e}]}});let d=`${r}${e}ECSSecurityGroup`.replace(/[^a-zA-Z0-9]/g,"");for(let[u,m]of Object.entries(t)){let f=m.port||3000;this.builder.addResource(d,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`ECS tasks security group for ${r}-${e}`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:f,ToPort:f,SourceSecurityGroupId:{Ref:l},Description:"Allow traffic from ALB"}],SecurityGroupEgress:[{IpProtocol:"-1",CidrIp:"0.0.0.0/0",Description:"Allow all outbound"}],Tags:[{Key:"Name",Value:`${r}-${e}-ecs-sg`},{Key:"Environment",Value:e}]}});let h=`${r}${e}TaskExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(h,{Type:"AWS::IAM::Role",Properties:{RoleName:`${r}-${e}-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 S=`${r}${e}TaskRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(S,{Type:"AWS::IAM::Role",Properties:{RoleName:`${r}-${e}-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 y=`${r}${e}${u}LogGroup`.replace(/[^a-zA-Z0-9]/g,""),v=`/ecs/${r}-${e}-${u}`;this.builder.addResource(y,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:v,RetentionInDays:30}});let R=`${r}${e}${u}TaskDef`.replace(/[^a-zA-Z0-9]/g,""),b=String(m.cpu||512),A=String(m.memory||1024);this.builder.addResource(R,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${r}-${e}-${u}`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:b,Memory:A,ExecutionRoleArn:{"Fn::GetAtt":[h,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[S,"Arn"]},ContainerDefinitions:[{Name:u,Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${r}:latest`},Essential:!0,PortMappings:[{ContainerPort:f,Protocol:"tcp"}],LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":v,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":u}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${f}${m.healthCheck||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${r}-${e}-${u}`},{Key:"Environment",Value:e}]},DependsOn:[h,S,y]});let k=`${r}${e}ALB`.replace(/[^a-zA-Z0-9]/g,"");if(n?.enabled!==!1){this.builder.addResource(k,{Type:"AWS::ElasticLoadBalancingV2::LoadBalancer",Properties:{Name:`${r}-${e}-alb`,Scheme:"internet-facing",Type:"application",Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],SecurityGroups:[{Ref:l}],Tags:[{Key:"Name",Value:`${r}-${e}-alb`},{Key:"Environment",Value:e}]}});let T=`${r}${e}TargetGroup`.replace(/[^a-zA-Z0-9]/g,""),D=n?.healthCheck?.path||m.healthCheck||"/health";this.builder.addResource(T,{Type:"AWS::ElasticLoadBalancingV2::TargetGroup",Properties:{Name:`${r}-${e}-tg`,Port:f,Protocol:"HTTP",VpcId:{Ref:"VPC"},TargetType:"ip",HealthCheckPath:D,HealthCheckIntervalSeconds:n?.healthCheck?.interval||30,HealthyThresholdCount:n?.healthCheck?.healthyThreshold||2,UnhealthyThresholdCount:n?.healthCheck?.unhealthyThreshold||5,HealthCheckTimeoutSeconds:10,Tags:[{Key:"Name",Value:`${r}-${e}-tg`},{Key:"Environment",Value:e}]}});let E=`${r}${e}HTTPListener`.replace(/[^a-zA-Z0-9]/g,"");if(i?.enabled&&i?.redirectHttp)this.builder.addResource(E,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:k},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"redirect",RedirectConfig:{Protocol:"HTTPS",Port:"443",StatusCode:"HTTP_301"}}]}});else this.builder.addResource(E,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:k},Port:80,Protocol:"HTTP",DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:T}}]}});if(i?.enabled){let V=i.certificateArn||(s?{Ref:s}:void 0);if(V){let K=`${r}${e}HTTPSListener`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(K,{Type:"AWS::ElasticLoadBalancingV2::Listener",Properties:{LoadBalancerArn:{Ref:k},Port:443,Protocol:"HTTPS",Certificates:[{CertificateArn:V}],DefaultActions:[{Type:"forward",TargetGroupArn:{Ref:T}}],SslPolicy:"ELBSecurityPolicy-TLS13-1-2-2021-06"}})}}let N=`${r}${e}${u}Service`.replace(/[^a-zA-Z0-9]/g,""),_=m.desiredCount||1;this.builder.addResource(N,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${r}-${e}-${u}`,Cluster:{Ref:c},TaskDefinition:{Ref:R},DesiredCount:_,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:d}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},LoadBalancers:[{ContainerName:u,ContainerPort:f,TargetGroupArn:{Ref:T}}],HealthCheckGracePeriodSeconds:120,Tags:[{Key:"Name",Value:`${r}-${e}-${u}`},{Key:"Environment",Value:e}]},DependsOn:[R,E,T]});let B=m.autoScaling;if(B){let V=`${r}${e}${u}ScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(V,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:B.max||10,MinCapacity:B.min||1,ResourceId:{"Fn::Sub":`service/\${${c}}/${r}-${e}-${u}`},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:N}),B.targetCpuUtilization)this.builder.addResource(`${r}${e}${u}CPUScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${r}-${e}-${u}-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:V},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:B.targetCpuUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}});if(B.targetMemoryUtilization)this.builder.addResource(`${r}${e}${u}MemoryScaling`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${r}-${e}-${u}-memory-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:V},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageMemoryUtilization"},TargetValue:B.targetMemoryUtilization,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(o?.domain&&o?.hostedZoneId)this.builder.addResource(`${r}${e}ApiDnsRecord`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:o.hostedZoneId,Name:`api.${o.domain}`,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[k,"DNSName"]},HostedZoneId:{"Fn::GetAtt":[k,"CanonicalHostedZoneID"]}}}});this.builder.addOutput("ECSClusterArn",{Description:"ECS Cluster ARN",Value:{"Fn::GetAtt":[c,"Arn"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-cluster-arn"}}}),this.builder.addOutput("ECSServiceName",{Description:"ECS Service Name",Value:`${r}-${e}-${u}`,Export:{Name:{"Fn::Sub":"${AWS::StackName}-ecs-service-name"}}}),this.builder.addOutput("LoadBalancerDNS",{Description:"Application Load Balancer DNS Name",Value:{"Fn::GetAtt":[k,"DNSName"]},Export:{Name:{"Fn::Sub":"${AWS::StackName}-alb-dns"}}})}else{let T=`${r}${e}${u}Service`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(T,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${r}-${e}-${u}`,Cluster:{Ref:c},TaskDefinition:{Ref:R},DesiredCount:m.desiredCount||1,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:d}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},Tags:[{Key:"Name",Value:`${r}-${e}-${u}`},{Key:"Environment",Value:e}]},DependsOn:[R]})}break}}generateServerless(r,e){if(this.mergedConfig.infrastructure?.functions)for(let[t,n]of Object.entries(this.mergedConfig.infrastructure.functions)){if(!this.shouldDeploy(n))continue;let{role:i,logicalId:o}=la.createRole({slug:r,environment:e,roleName:`${r}-${e}-${t}-role`,servicePrincipal:"lambda.amazonaws.com",managedPolicyArns:["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"]});this.builder.addResource(o,i);let{lambdaFunction:s,logicalId:c}=me.createLambdaFunction({slug:r,environment:e,functionName:`${r}-${e}-${t}`,handler:n.handler||"index.handler",runtime:n.runtime||"nodejs20.x",code:{zipFile:n.code||"export const handler = async () => ({ statusCode: 200 });"},role:o,timeout:n.timeout,memorySize:n.memorySize});this.builder.addResource(c,s)}if(this.mergedConfig.infrastructure?.api){let{restApi:t,logicalId:n}=da.createRestApi({slug:r,environment:e,apiName:`${r}-${e}-api`});this.builder.addResource(n,t)}}generateServer(r,e){if(!this.mergedConfig.infrastructure?.servers)return;let t=this.mergedConfig.infrastructure.servers,n=this.mergedConfig.infrastructure?.compute,i=this.mergedConfig.infrastructure?.ssl;if(Object.values(t).some((s)=>!s.privateNetwork||s.privateNetwork==="create"))this.generateNetworkInfrastructure(r,e);for(let[s,c]of Object.entries(t)){let l=c.instanceType||c.size||n?.size,u=(l?me.InstanceSize.specs[l]:void 0)?.instanceType||l||"t3.micro",m=c.type||"app",f=c.userData||c.startupScript;if(!f)f=me.UserData.generateAppServerScript({runtime:"bun",runtimeVersion:c.bunVersion||"latest",webServer:"none",domain:c.domain,enableSsl:!!i?.enabled,sslEmail:i?.letsEncrypt?.email,installRedis:m==="cache",installDatabaseClients:!!c.database});let h=c.privateNetwork&&c.privateNetwork!=="create"?c.privateNetwork:{Ref:"VPC"},S=c.subnet||{Ref:"PublicSubnet1"},v=!!this.mergedConfig.infrastructure?.email?.server?.enabled,R=[22,80,443,this.resolveApiOriginPort()];if(v)R.push(25,465,587,143,993);let b=me.createServerModeStack({slug:`${r}-${s}`,environment:e,vpcId:h,subnetId:S,instanceType:u,keyName:c.keyName||`${r}-${e}`,domain:c.domain,userData:f,volumeSize:c.diskSize||20,imageId:c.image,allowedPorts:R});for(let[A,k]of Object.entries(b.resources))this.builder.addResource(A,k);this.serverEipLogicalIds.set(s,b.outputs.eipLogicalId),this.serverEipLogicalIds.set(`${s}Instance`,b.outputs.instanceLogicalId),this.builder.addOutput(`${s}InstanceId`,{Value:{Ref:b.outputs.instanceLogicalId},Description:`Instance ID for ${s} server`}),this.builder.addOutput(`${s}PublicIp`,{Value:{Ref:b.outputs.eipLogicalId},Description:`Public IP for ${s} server`})}}generateComputeApp(r,e){let t=this.mergedConfig.infrastructure?.compute;if(!t)return;let n=this.mergedConfig.sites||{},i=Object.entries(n),s=this.mergedConfig.infrastructure?.dns?.domain,c=this.mergedConfig.infrastructure?.database,l=t.size,u=(l?me.InstanceSize.specs[l]:void 0)?.instanceType||l||"t3.micro";if(!this.builder.hasResource("VPC"))this.generateNetworkInfrastructure(r,e);let m=this.buildCaddyfile(i),f=me.UserData.generateBunAppScript({runtime:t.runtime||"bun",runtimeVersion:t.runtimeVersion||"latest",systemPackages:t.systemPackages,database:c,caddyfile:m}),h=i.map(([,k])=>k.port).filter((k)=>typeof k==="number"&&![80,443].includes(k)),S=this.resolveApiOriginPort(),y=`${r}${e}DeployBucket`.replace(/[^a-zA-Z0-9]/g,""),v=`${r}-${e}-deploy`;this.builder.addResource(y,{Type:"AWS::S3::Bucket",Properties:{BucketName:v,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:r},{Key:"Environment",Value:e},{Key:"ManagedBy",Value:"ts-cloud"}]}});let R=me.createServerModeStack({slug:`${r}-app`,environment:e,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},instanceType:u,keyName:t.allowSsh?`${r}-${e}`:void 0,domain:s,userData:f,volumeSize:t.disk?.size||20,imageId:t.image,allowedPorts:[...t.allowSsh?[22]:[],80,443,S,...h]}),b=R.resources[R.outputs.instanceLogicalId];if(b?.Properties){let k=b.Properties.Tags||[];b.Properties.Tags=[...k,{Key:"Project",Value:r},{Key:"Environment",Value:e},{Key:"Role",Value:"app"},{Key:"ManagedBy",Value:"ts-cloud"}]}let A=R.resources[R.outputs.roleLogicalId];if(A?.Properties)A.Properties.Policies=A.Properties.Policies||[],A.Properties.Policies.push({PolicyName:"DeployBucketRead",PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["s3:GetObject","s3:ListBucket"],Resource:[`arn:aws:s3:::${v}`,`arn:aws:s3:::${v}/*`]}]}});for(let[k,T]of Object.entries(R.resources))this.builder.addResource(k,T);this.builder.addOutput("deployBucketName",{Value:{Ref:y},Description:"S3 bucket where release tarballs are uploaded"}),this.builder.addOutput("appInstanceId",{Value:{Ref:R.outputs.instanceLogicalId},Description:"EC2 instance ID for the app server"}),this.builder.addOutput("appPublicIp",{Value:{Ref:R.outputs.eipLogicalId},Description:"Public IP for the app server"}),this.serverEipLogicalIds.set("app",R.outputs.eipLogicalId),this.serverEipLogicalIds.set("appInstance",R.outputs.instanceLogicalId)}generateJumpBox(r,e){let t=this.mergedConfig.infrastructure?.jumpBox;if(!t)return;let n=t===!0?{}:t;if(n.enabled===!1)return;this.generateNetworkInfrastructure(r,e);let i=n.size||"micro",s=me.InstanceSize.specs[i]?.instanceType||i||"t3.micro",c;if(n.mountEfs)c={fileSystemId:typeof n.mountEfs==="string"?n.mountEfs:{Ref:"FileSystem"},mountPath:n.mountPath||"/mnt/efs"};let l;if(n.databaseTools)l=me.JumpBox.withDatabaseTools({slug:r,environment:e,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:n.keyName||`${r}-${e}`,allowedCidrs:n.allowedCidrs});else if(c)l=me.JumpBox.withEfsMount({slug:r,environment:e,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:n.keyName||`${r}-${e}`,fileSystemId:c.fileSystemId,mountPath:c.mountPath,allowedCidrs:n.allowedCidrs});else l=me.createJumpBox({slug:r,environment:e,vpcId:{Ref:"VPC"},subnetId:{Ref:"PublicSubnet1"},keyName:n.keyName||`${r}-${e}`,instanceType:s,allowedCidrs:n.allowedCidrs,mountEfs:c});for(let[d,u]of Object.entries(l.resources))this.builder.addResource(d,u);this.builder.addOutput("JumpBoxInstanceId",{Value:{Ref:l.instanceLogicalId},Description:"The ID of the JumpBox EC2 instance (use with SSM Session Manager or SSH)"})}generateSharedInfrastructure(r,e){let t=this.mergedConfig.infrastructure?.ssl,n=this.mergedConfig.infrastructure?.dns,i=n?.domain,o=n?.hostedZoneId,s=[],c,l;if(t?.certificateArn)l=t.certificateArn;else if(t?.enabled&&i&&o){let y=t.domains||[i],v=y[0],R=y.slice(1).map((k)=>{if(k.includes(".")&&k.endsWith(v))return k.replace(`.${v}`,"");return k}),{certificate:b,logicalId:A}=Br.createCertificate({domain:v,subdomains:R,slug:r,environment:e,validationMethod:"DNS",hostedZoneId:o});c=A,this.builder.addResource(A,b)}if(this.mergedConfig.infrastructure?.storage){let y=Object.entries(this.mergedConfig.infrastructure.storage).some(([,b])=>b.website),v;if(y&&(c||l))v=`${r}${e}CloudFrontOAC`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(v,{Type:"AWS::CloudFront::OriginAccessControl",Properties:{OriginAccessControlConfig:{Name:`${r}-${e}-s3-oac`,Description:`OAC for ${r} ${e} S3 website buckets`,OriginAccessControlOriginType:"s3",SigningBehavior:"always",SigningProtocol:"sigv4"}}});let R=Object.entries(this.mergedConfig.infrastructure.storage).map(([b,A])=>({name:b,config:A,mountPath:this.normalizeMountPath(A),rewriteStyle:A.pathRewriteStyle||"directory",logicalId:this.storageBucketLogicalId(r,e,b)})).filter((b)=>b.name!=="public"&&b.config.website&&b.mountPath);for(let[b,A]of Object.entries(this.mergedConfig.infrastructure.storage)){let k=!!(A.website&&v),T=this.storageBucketName(r,e,b),{bucket:D,logicalId:E}=Mr.createBucket({slug:r,name:b,environment:e,bucketName:T,versioning:A.versioning,encryption:A.encryption,public:k?!1:A.public});if(k&&D.Properties)D.Properties.PublicAccessBlockConfiguration={BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!1,RestrictPublicBuckets:!1};if(this.builder.addResource(E,D),A.website){let _=typeof A.website==="object"?A.website:{},B=Mr.enableWebsiteHosting(D,_.indexDocument||"index.html",_.errorDocument);this.builder.addResource(E,B)}let N=this.normalizeMountPath(A);if(k&&v&&i&&!(N&&b!=="public")){let _=`${r}${e}${b}CDN`.replace(/[^a-zA-Z0-9]/g,""),B=[];if(A.aliases&&A.aliases.length>0)B.push(...A.aliases);else if(b==="public"){if(B.push(i),t?.domains?.includes(`www.${i}`))B.push(`www.${i}`)}else if(b==="docs")B.push(`docs.${i}`);else if(b==="blog")B.push(`blog.${i}`);let V=typeof A.website==="object"?A.website:{},K=A.spa===!0,oe=V.errorDocument||(K?"index.html":"404.html"),G=[];if(K)G.push({ErrorCode:403,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:200,ResponsePagePath:"/index.html",ErrorCachingMinTTL:300});else G.push({ErrorCode:403,ResponseCode:404,ResponsePagePath:`/${oe}`,ErrorCachingMinTTL:300},{ErrorCode:404,ResponseCode:404,ResponsePagePath:`/${oe}`,ErrorCachingMinTTL:300});let L=l&&B.length>0?{AcmCertificateArn:l,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:c&&B.length>0?{AcmCertificateArn:{Ref:c},SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},H=`S3-${r}-${e}-${b}`,j=this.mergedConfig.project.region||"us-east-1",W;if(!K)W=`${r}${e}${b}UrlRewrite`.replace(/[^a-zA-Z0-9]/g,""),this.builder.addResource(W,{Type:"AWS::CloudFront::Function",Properties:{Name:`${r}-${e}-${b}-url-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`URL rewrite for ${r} ${e} ${b} - 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'; }
97
+ else if (!uri.includes('.')) { request.uri += '.html'; } return request; }`}});let ue=[{Id:H,DomainName:{"Fn::Sub":`\${${E}}.s3.${j}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:v}}],ae=[],Fe=[];if(this.shouldRouteStorageBucketToCompute(b,A)){this.appendComputeAppOrigin(ue,ae,Fe,r,e,this.resolveComputeCachePathPatterns(A.computeRoutes));for(let Y of R){let X=`S3-${r}-${e}-${Y.name}`,ke=`${r}${e}${Y.name}PathMountRewrite`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(ke,{Type:"AWS::CloudFront::Function",Properties:{Name:`${r}-${e}-${Y.name}-path-mount-rewrite`,AutoPublish:!0,FunctionConfig:{Comment:`Path mount rewrite for ${r} ${e} ${Y.name} at ${Y.mountPath}`,Runtime:"cloudfront-js-2.0"},FunctionCode:this.pathMountRewriteFunctionCode(Y.mountPath,Y.rewriteStyle)}}),ue.push({Id:X,DomainName:{"Fn::Sub":`\${${Y.logicalId}}.s3.${j}.amazonaws.com`},OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginAccessControlId:{Ref:v}}),Fe.push(Y.logicalId,ke);for(let Me of[Y.mountPath,`${Y.mountPath}/*`])ae.push({PathPattern:Me,TargetOriginId:X,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":[ke,"FunctionARN"]}}]})}}let jt={Type:"AWS::CloudFront::Distribution",DependsOn:[E,v,...W?[W]:[],...Fe],Properties:{DistributionConfig:{Enabled:!0,Comment:`${r} ${e} ${b} site`,DefaultRootObject:"index.html",Origins:ue,DefaultCacheBehavior:{TargetOriginId:H,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:!0,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6",...W?{FunctionAssociations:[{EventType:"viewer-request",FunctionARN:{"Fn::GetAtt":[W,"FunctionARN"]}}]}:{}},...ae.length>0?{CacheBehaviors:ae}:{},...B.length>0?{Aliases:B}:{},ViewerCertificate:L,PriceClass:"PriceClass_100",HttpVersion:"http2and3",IPV6Enabled:!0,CustomErrorResponses:G}}};if(c)jt.DependsOn.push(c);this.builder.addResource(_,jt);let ye=`${E}CloudFrontPolicy`;if(this.builder.addResource(ye,{Type:"AWS::S3::BucketPolicy",DependsOn:[E,_],Properties:{Bucket:{Ref:E},PolicyDocument:{Version:"2012-10-17",Statement:[{Sid:"AllowCloudFrontServicePrincipal",Effect:"Allow",Principal:{Service:"cloudfront.amazonaws.com"},Action:"s3:GetObject",Resource:{"Fn::Sub":`arn:aws:s3:::\${${E}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${_}}`}}}}]}}}),this.shouldRouteStorageBucketToCompute(b,A))for(let Y of R){let X=`${Y.logicalId}CloudFrontPolicy`;this.builder.addResource(X,{Type:"AWS::S3::BucketPolicy",DependsOn:[Y.logicalId,_],Properties:{Bucket:{Ref:Y.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:::\${${Y.logicalId}}/*`},Condition:{StringEquals:{"AWS:SourceArn":{"Fn::Sub":`arn:aws:cloudfront::\${AWS::AccountId}:distribution/\${${_}}`}}}}]}}})}if(B.length>0)s.push({name:b,bucketLogicalId:E,distLogicalId:_,oacLogicalId:v,aliases:B});this.builder.addOutput(`${b}CloudFrontDomain`,{Value:{"Fn::GetAtt":[_,"DomainName"]},Description:`CloudFront domain for ${b}`}),this.builder.addOutput(`${b}CloudFrontDistributionId`,{Value:{Ref:_},Description:`CloudFront distribution ID for ${b}`})}if(this.builder.addOutput(`${b}BucketName`,{Value:{Ref:E},Description:`S3 bucket name for ${b}`}),b==="public")this.builder.addOutput("FrontendBucketName",{Value:{Ref:E},Description:"Frontend S3 bucket name"});if(b==="docs")this.builder.addOutput("DocsBucketName",{Value:{Ref:E},Description:"Documentation S3 bucket name"});if(b==="blog")this.builder.addOutput("BlogBucketName",{Value:{Ref:E},Description:"Blog S3 bucket name"})}}if(this.mergedConfig.infrastructure?.sftp){let y=this.mergedConfig.infrastructure.sftp,v=ca.create({...y,storage:this.resolveSftpStorage(y,r,e)??y.storage,slug:r,environment:e});for(let[R,b]of Object.entries(v.resources))this.builder.addResource(R,b);this.builder.addOutput("SftpServerId",{Value:{Ref:v.serverLogicalId},Description:"AWS Transfer Family SFTP server ID"}),this.builder.addOutput("SftpEndpoint",{Value:{"Fn::Sub":`\${${v.serverLogicalId}}.server.transfer.\${AWS::Region}.amazonaws.com`},Description:"AWS Transfer Family SFTP endpoint"}),this.builder.addOutput("SftpStorageDomain",{Value:v.domain,Description:"Storage backing the SFTP server (EFS or S3)"})}if(o&&s.length>0)for(let{name:y,distLogicalId:v,aliases:R}of s)for(let b of R){let A=b.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${A}ARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[v],Properties:{HostedZoneId:o,Name:b,Type:"A",AliasTarget:{DNSName:{"Fn::GetAtt":[v,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}}),this.builder.addResource(`${A}AAAARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[v],Properties:{HostedZoneId:o,Name:b,Type:"AAAA",AliasTarget:{DNSName:{"Fn::GetAtt":[v,"DomainName"]},HostedZoneId:"Z2FDTNDATAQYW2",EvaluateTargetHealth:!1}}})}if(this.mergedConfig.infrastructure?.databases){for(let[y,v]of Object.entries(this.mergedConfig.infrastructure.databases))if(v.engine==="dynamodb"){let{table:R,logicalId:b}=_n.createTable({slug:r,environment:e,tableName:`${r}-${e}-${y}`,partitionKey:v.partitionKey||{name:"id",type:"S"},sortKey:v.sortKey});this.builder.addResource(b,R)}else if(v.engine==="postgres"){let{dbInstance:R,logicalId:b}=_n.createPostgres({slug:r,environment:e,dbInstanceIdentifier:`${r}-${e}-${y}`,masterUsername:v.username||"admin",masterUserPassword:v.password||"changeme123",allocatedStorage:v.storage||20,dbInstanceClass:v.instanceClass||"db.t3.micro"});this.builder.addResource(b,R)}else if(v.engine==="mysql"){let{dbInstance:R,logicalId:b}=_n.createMysql({slug:r,environment:e,dbInstanceIdentifier:`${r}-${e}-${y}`,masterUsername:v.username||"admin",masterUserPassword:v.password||"changeme123",allocatedStorage:v.storage||20,dbInstanceClass:v.instanceClass||"db.t3.micro"});this.builder.addResource(b,R)}}if(this.mergedConfig.infrastructure?.cdn)for(let[y,v]of Object.entries(this.mergedConfig.infrastructure.cdn)){if(!v.origin)continue;let R=typeof v.customDomain==="string"?v.customDomain:v.customDomain?.domain||v.domain,b=typeof v.customDomain==="object"?v.customDomain?.certificateArn:v.certificateArn,A=b||l,k=`${r}${e}${y}CDN`.replace(/[^a-zA-Z0-9]/g,""),T=`S3-${r}-${e}-${y}-cdn`,D=[{Id:T,DomainName:v.origin,OriginPath:"",S3OriginConfig:{OriginAccessIdentity:""},OriginShield:v.originShield?{Enabled:!0,OriginShieldRegion:v.originShieldRegion||this.mergedConfig.environments[e]?.region||this.mergedConfig.project.region||"us-east-1"}:{Enabled:!1}}],E=[],N=[];if(v.routeCompute)this.appendComputeAppOrigin(D,E,N,r,e,this.resolveComputeCachePathPatterns(v.computeRoutes));let _=A&&R?{AcmCertificateArn:c&&!b?{Ref:c}:A,SslSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"}:{CloudFrontDefaultCertificate:!0},B=[...N];if(c&&!b&&R)B.push(c);let V={Type:"AWS::CloudFront::Distribution",...B.length>0?{DependsOn:B}:{},Properties:{DistributionConfig:{Enabled:!0,Comment:`${r} ${e} ${y} CDN`,DefaultRootObject:"index.html",Origins:D,DefaultCacheBehavior:{TargetOriginId:T,ViewerProtocolPolicy:"redirect-to-https",AllowedMethods:["GET","HEAD","OPTIONS"],CachedMethods:["GET","HEAD","OPTIONS"],Compress:v.compress!==!1,CachePolicyId:"658327ea-f89d-4fab-a63d-7e88639e58f6"},...E.length>0?{CacheBehaviors:E}:{},...R&&A?{Aliases:[R]}:{},ViewerCertificate:_,PriceClass:"PriceClass_100",HttpVersion:v.http3?"http2and3":"http2"}}};if(this.builder.addResource(k,V),o&&R&&A){let K=R.replace(/\./g,"").replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${K}CdnARecord`,{Type:"AWS::Route53::RecordSet",DependsOn:[k,...c&&!b?[c]:[]],Properties:{HostedZoneId:o,Name:R,Type:"A",AliasTarget:{HostedZoneId:"Z2FDTNDATAQYW2",DNSName:{"Fn::GetAtt":[k,"DomainName"]}}}})}}if(this.mergedConfig.infrastructure?.queues)for(let[y,v]of Object.entries(this.mergedConfig.infrastructure.queues)){if(!this.shouldDeploy(v))continue;let{queue:R,logicalId:b}=_r.createQueue({slug:r,environment:e,name:`${r}-${e}-${y}`,fifo:v.fifo,visibilityTimeout:v.visibilityTimeout,messageRetentionPeriod:v.messageRetentionPeriod,delaySeconds:v.delaySeconds,maxMessageSize:v.maxMessageSize,receiveMessageWaitTime:v.receiveMessageWaitTime,contentBasedDeduplication:v.contentBasedDeduplication,encrypted:v.encrypted,kmsKeyId:v.kmsKeyId});this.builder.addResource(b,R);let A;if(v.deadLetterQueue){let{deadLetterQueue:k,updatedSourceQueue:T,deadLetterLogicalId:D}=_r.createDeadLetterQueue(b,{slug:r,environment:e,maxReceiveCount:v.maxReceiveCount});A=D,this.builder.addResource(D,k);let N=this.builder.getResources()[b];if(N?.Properties)N.Properties.RedrivePolicy=T.Properties?.RedrivePolicy}if(v.trigger){let k=v.trigger,T=`${r}${e}${k.functionName}`.replace(/[^a-zA-Z0-9]/g,""),D={Type:"AWS::Lambda::EventSourceMapping",Properties:{EventSourceArn:{"Fn::GetAtt":[b,"Arn"]},FunctionName:{Ref:T},BatchSize:k.batchSize||10,MaximumBatchingWindowInSeconds:k.batchWindow||0,Enabled:!0,...k.reportBatchItemFailures!==!1&&{FunctionResponseTypes:["ReportBatchItemFailures"]},...k.maxConcurrency&&{ScalingConfig:{MaximumConcurrency:k.maxConcurrency}},...k.filterPattern&&{FilterCriteria:{Filters:[{Pattern:JSON.stringify(k.filterPattern)}]}}},DependsOn:[b,T]};this.builder.addResource(`${b}Trigger`,D)}if(v.alarms?.enabled){let k=v.alarms,T=k.notificationTopicArn;if(!T&&k.notificationEmails?.length){let N=`${b}AlarmTopic`;this.builder.addResource(N,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${r}-${e}-${y}-alarms`,DisplayName:`${y} Queue Alarms`}}),k.notificationEmails.forEach((_,B)=>{this.builder.addResource(`${N}Sub${B}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:N},Protocol:"email",Endpoint:_}})}),T={Ref:N}}let D=k.queueDepthThreshold||1000;this.builder.addResource(`${b}DepthAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${r}-${e}-${y}-queue-depth`,AlarmDescription:`Queue ${y} depth exceeds ${D} messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Average",Period:300,EvaluationPeriods:2,Threshold:D,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[b,"QueueName"]}}],...T&&{AlarmActions:[T],OKActions:[T]}}});let E=k.messageAgeThreshold||3600;if(this.builder.addResource(`${b}AgeAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${r}-${e}-${y}-message-age`,AlarmDescription:`Queue ${y} oldest message exceeds ${E} seconds`,MetricName:"ApproximateAgeOfOldestMessage",Namespace:"AWS/SQS",Statistic:"Maximum",Period:300,EvaluationPeriods:2,Threshold:E,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[b,"QueueName"]}}],...T&&{AlarmActions:[T],OKActions:[T]}}}),A&&k.dlqAlarm!==!1)this.builder.addResource(`${A}Alarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${r}-${e}-${y}-dlq-messages`,AlarmDescription:`Dead letter queue for ${y} has messages`,MetricName:"ApproximateNumberOfMessagesVisible",Namespace:"AWS/SQS",Statistic:"Sum",Period:300,EvaluationPeriods:1,Threshold:0,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"QueueName",Value:{"Fn::GetAtt":[A,"QueueName"]}}],...T&&{AlarmActions:[T]}}})}if(v.subscribe){let k=v.subscribe,T=k.topicArn;if(!T&&k.topicName)T={Ref:k.topicName};if(T){this.builder.addResource(`${b}SnsPolicy`,{Type:"AWS::SQS::QueuePolicy",Properties:{Queues:[{Ref:b}],PolicyDocument:{Version:"2012-10-17",Statement:[{Effect:"Allow",Principal:{Service:"sns.amazonaws.com"},Action:"sqs:SendMessage",Resource:{"Fn::GetAtt":[b,"Arn"]},Condition:{ArnEquals:{"aws:SourceArn":T}}}]}}});let D={TopicArn:T,Protocol:"sqs",Endpoint:{"Fn::GetAtt":[b,"Arn"]},RawMessageDelivery:k.rawMessageDelivery||!1};if(k.filterPolicy)D.FilterPolicy=k.filterPolicy,D.FilterPolicyScope=k.filterPolicyScope||"MessageAttributes";this.builder.addResource(`${b}SnsSub`,{Type:"AWS::SNS::Subscription",Properties:D,DependsOn:`${b}SnsPolicy`})}}}if(this.mergedConfig.infrastructure?.realtime?.enabled)if((this.mergedConfig.infrastructure.realtime.mode||"serverless")==="server")this.generateRealtimeServerResources(r,e);else this.generateRealtimeResources(r,e);let d=this.mergedConfig.infrastructure?.redirects;if(d){let y=d.target||this.mergedConfig.infrastructure?.dns?.domain||"",v=d.protocol||"https";if(d.domains?.length&&y)for(let R of d.domains){let{bucket:b,bucketPolicy:A,logicalId:k,policyLogicalId:T}=zn.createDomainRedirectBucket({slug:r,environment:e,sourceDomain:R,targetDomain:y,protocol:v});this.builder.addResource(k,b),this.builder.addResource(T,A)}if(d.paths&&Object.keys(d.paths).length>0){let R=zn.fromMapping(d.paths,{statusCode:d.statusCode||301}),{function:b,logicalId:A}=zn.createPathRedirectFunction({slug:r,environment:e,rules:R});this.builder.addResource(A,b)}}if(this.mergedConfig.infrastructure?.monitoring?.alarms)for(let[y,v]of Object.entries(this.mergedConfig.infrastructure.monitoring.alarms)){let{alarm:R,logicalId:b}=ua.createAlarm({slug:r,environment:e,alarmName:`${r}-${e}-${y}`,metricName:v.metricName||"Errors",namespace:v.namespace||"AWS/Lambda",threshold:v.threshold||1,comparisonOperator:v.comparisonOperator||"GreaterThanThreshold"});this.builder.addResource(b,R)}let u=this.mergedConfig.infrastructure?.cache;if(u){let y=u.type||"redis";if(y==="redis"){this.generateNetworkInfrastructure(r,e);let v=u.redis||{},{replicationGroup:R,subnetGroup:b,logicalId:A,subnetGroupId:k}=zr.createRedis({slug:r,environment:e,nodeType:v.nodeType||u.nodeType||"cache.t3.micro",engineVersion:v.engineVersion||"7.1",port:v.port||6379,numCacheClusters:v.numCacheNodes||2,automaticFailover:v.automaticFailoverEnabled!==!1,atRestEncryption:!0,transitEncryption:!0,snapshotRetentionDays:v.snapshotRetentionLimit||7,snapshotWindow:v.snapshotWindow,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(A,R),b&&k)this.builder.addResource(k,b);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[A,"PrimaryEndPoint.Address"]},Description:"Redis primary endpoint address"}),this.builder.addOutput("CachePort",{Value:{"Fn::GetAtt":[A,"PrimaryEndPoint.Port"]},Description:"Redis primary endpoint port"})}else if(y==="memcached"){this.generateNetworkInfrastructure(r,e);let v=u.elasticache||{},{cluster:R,subnetGroup:b,logicalId:A,subnetGroupId:k}=zr.createMemcached({slug:r,environment:e,nodeType:v.nodeType||u.nodeType||"cache.t3.micro",engineVersion:v.engineVersion||"1.6.22",numCacheNodes:v.numCacheNodes||2,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]});if(this.builder.addResource(A,R),b&&k)this.builder.addResource(k,b);this.builder.addOutput("CacheEndpoint",{Value:{"Fn::GetAtt":[A,"ConfigurationEndpoint.Address"]},Description:"Memcached configuration endpoint address"})}}let m=this.mergedConfig.infrastructure?.email;if(m){let y=m.domain||this.mergedConfig.infrastructure?.dns?.domain;if(y){let{emailIdentity:v,logicalId:R}=ft.verifyDomain({domain:y,slug:r,environment:e,enableDkim:m.enableDkim!==!1,dkimKeyLength:m.dkimKeyLength||"RSA_2048_BIT"});if(this.builder.addResource(R,v),m.configurationSet!==!1){let{configurationSet:k,logicalId:T}=ft.createConfigurationSet({slug:r,environment:e});this.builder.addResource(T,k)}let b=m.hostedZoneId||this.mergedConfig.infrastructure?.dns?.hostedZoneId;if(b){if(m.enableDkim!==!1)for(let N=1;N<=3;N++){let _=`DkimRecord${N}${y.replace(/\./g,"")}`;this.builder.addResource(_,{Type:"AWS::Route53::RecordSet",DependsOn:[R],Properties:{HostedZoneId:b,Name:{"Fn::GetAtt":[R,`DkimDNSTokenName${N}`]},Type:"CNAME",TTL:1800,ResourceRecords:[{"Fn::GetAtt":[R,`DkimDNSTokenValue${N}`]}]}})}let{record:k,logicalId:T}=ft.createSpfRecord(y,b);this.builder.addResource(T,k);let{record:D,logicalId:E}=ft.createDmarcRecord(y,b,{policy:"none",reportingEmail:m.dmarcReportingEmail||`dmarc-reports@${y}`});this.builder.addResource(E,D)}this.builder.addOutput("EmailDomain",{Value:y,Description:"SES verified email domain"});let A=m.server;if(A?.enabled&&b){let k=this.mergedConfig.environments[e]?.region||this.mergedConfig.project.region||"us-east-1",T=`${r}-${e}-email`,{role:D,policy:E,roleLogicalId:N,policyLogicalId:_}=ft.createEmailLambdaRole({slug:r,environment:e,s3BucketArn:`arn:aws:s3:::${T}`,sesIdentityArn:`arn:aws:ses:${k}:*:identity/${y}`});this.builder.addResource(N,D),this.builder.addResource(_,E);let{function:B,permission:V,logicalId:K,permissionLogicalId:oe}=ft.createInboundEmailLambda({slug:r,environment:e,roleArn:{"Fn::GetAtt":[N,"Arn"]},s3BucketName:T,organizedPrefix:"mailboxes/"});this.builder.addResource(K,B),this.builder.addResource(oe,V);let G=ft.createInboundEmailSetup({slug:r,environment:e,domain:y,s3BucketName:T,s3KeyPrefix:"inbox/",region:k,hostedZoneId:b,lambdaFunctionArn:{"Fn::GetAtt":[K,"Arn"]}});for(let[H,j]of Object.entries(G.resources)){if(j.Type==="AWS::SES::ReceiptRule")j.DependsOn=[oe,K];this.builder.addResource(H,j)}this.builder.addOutput("InboundEmailLambda",{Value:{Ref:K},Description:"Inbound email processing Lambda function"}),this.builder.addOutput("EmailBucket",{Value:T,Description:"S3 bucket for email storage"});let L=this.serverEipLogicalIds.get("app");if(L&&b){let j=`${A?.subdomain||"mail"}.${y}`,W=j.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(`${W}ARecord`,{Type:"AWS::Route53::RecordSet",Properties:{HostedZoneId:b,Name:j,Type:"A",TTL:"300",ResourceRecords:[{Ref:L}]}})}this.builder.addOutput("MailHost",{Value:`mail.${y}`,Description:"Mail server hostname for SMTP/IMAP clients"})}}}let f=this.mergedConfig.infrastructure?.search;if(f){let y=f.vpc?{subnetIds:[{Ref:"PublicSubnet1"}],securityGroupIds:[]}:void 0;if(y)this.generateNetworkInfrastructure(r,e);let{domain:v,logicalId:R}=ma.createDomain({slug:r,environment:e,engineVersion:f.engineVersion||"OpenSearch_2.11",instanceType:f.instanceType||"t3.small.search",instanceCount:f.instanceCount||1,volumeSize:f.volumeSize||10,volumeType:f.volumeType||"gp3",dedicatedMaster:f.dedicatedMaster||!1,dedicatedMasterType:f.dedicatedMasterType,dedicatedMasterCount:f.dedicatedMasterCount||3,multiAz:f.multiAz||!1,encryption:f.encryption||{atRest:!0,nodeToNode:!0},advancedSecurity:f.advancedSecurity,autoTune:f.autoTune!==!1,vpc:y});this.builder.addResource(R,v),this.builder.addOutput("SearchDomainEndpoint",{Value:{"Fn::GetAtt":[R,"DomainEndpoint"]},Description:"OpenSearch domain endpoint"}),this.builder.addOutput("SearchDomainArn",{Value:{"Fn::GetAtt":[R,"Arn"]},Description:"OpenSearch domain ARN"})}let h=this.mergedConfig.infrastructure?.fileSystem;if(h&&Object.keys(h).length>0){this.generateNetworkInfrastructure(r,e);for(let[y,v]of Object.entries(h)){let{fileSystem:R,logicalId:b}=Bn.createFileSystem({slug:`${r}-${y}`,environment:e,encrypted:v.encrypted!==!1,performanceMode:v.performanceMode||"generalPurpose",throughputMode:v.throughputMode||"bursting",enableBackup:!0});this.builder.addResource(b,R);let{securityGroup:A,logicalId:k}=Bn.createEfsSecurityGroup({slug:`${r}-${y}`,environment:e,vpcId:{Ref:"VPC"},sourceCidrBlocks:["10.0.0.0/16"]});this.builder.addResource(k,A);let{mountTargets:T,logicalIds:D}=Bn.createMultiAzMountTargets(b,{slug:`${r}-${y}`,environment:e,subnetIds:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}],securityGroupId:{Ref:k}});for(let E=0;E<T.length;E++)this.builder.addResource(D[E],T[E]);this.builder.addOutput(`${y}FileSystemId`,{Value:{Ref:b},Description:`EFS file system ID for ${y}`})}}let S=this.mergedConfig.infrastructure?.ai;if(S){let y=S.service||"ecs",v=S.models||["*"],R=S.allowStreaming!==!1,b;if(y==="ecs")b=zt.enableBedrockForEcs({slug:r,environment:e,models:v,allowStreaming:R});else if(y==="ec2")b=zt.enableBedrockForEc2({slug:r,environment:e,models:v,allowStreaming:R});else if(y==="lambda")b=zt.enableBedrockForLambda({slug:r,environment:e,models:v,allowStreaming:R});else b=zt.createBedrockRole(y,{slug:r,environment:e,models:v,allowStreaming:R});if(this.builder.addResource(b.logicalId,b.role),S.allowAsync){let{policy:A,logicalId:k}=zt.createBedrockPolicy({slug:r,environment:e,models:v,allowStreaming:R,allowAsync:!0});this.builder.addResource(k,A)}this.builder.addOutput("BedrockRoleArn",{Value:{"Fn::GetAtt":[b.logicalId,"Arn"]},Description:"IAM role ARN with Bedrock permissions"})}}generateRealtimeResources(r,e){let t=this.mergedConfig.infrastructure?.realtime;if(!t)return;let n=t.name||`${r}-${e}-realtime`,i=t.scaling||{},o=t.storage||{type:"dynamodb"},s=i.handlerMemory||256,c=i.handlerTimeout||30,l=`${r}${e}RealtimeConnections`.replace(/[^a-zA-Z0-9]/g,""),d=`${r}${e}RealtimeChannels`.replace(/[^a-zA-Z0-9]/g,"");if(o.type==="dynamodb"){let D=o.dynamodb||{},E=D.billingMode||"PAY_PER_REQUEST";this.builder.addResource(l,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${r}-${e}-realtime-connections`,BillingMode:E,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"},...E==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:D.readCapacity||5,WriteCapacityUnits:D.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...E==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:D.readCapacity||5,WriteCapacityUnits:D.writeCapacity||5}},...D.pointInTimeRecovery&&{PointInTimeRecoverySpecification:{PointInTimeRecoveryEnabled:!0}},Tags:[{Key:"Name",Value:`${r}-${e}-realtime-connections`},{Key:"Environment",Value:e}]}}),this.builder.addResource(d,{Type:"AWS::DynamoDB::Table",Properties:{TableName:`${r}-${e}-realtime-channels`,BillingMode:E,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"},...E==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:D.readCapacity||5,WriteCapacityUnits:D.writeCapacity||5}}}],TimeToLiveSpecification:{AttributeName:"ttl",Enabled:!0},...E==="PROVISIONED"&&{ProvisionedThroughput:{ReadCapacityUnits:D.readCapacity||5,WriteCapacityUnits:D.writeCapacity||5}},Tags:[{Key:"Name",Value:`${r}-${e}-realtime-channels`},{Key:"Environment",Value:e}]}})}let u=`${r}${e}RealtimeRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(u,{Type:"AWS::IAM::Role",Properties:{RoleName:`${r}-${e}-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":[l,"Arn"]},{"Fn::Sub":`\${${l}.Arn}/index/*`},{"Fn::GetAtt":[d,"Arn"]},{"Fn::Sub":`\${${d}.Arn}/index/*`}]},{Effect:"Allow",Action:"execute-api:ManageConnections",Resource:{"Fn::Sub":"arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*"}}]}}]}});let m=`${r}${e}RealtimeConnect`.replace(/[^a-zA-Z0-9]/g,""),f=`${r}${e}RealtimeDisconnect`.replace(/[^a-zA-Z0-9]/g,""),h=`${r}${e}RealtimeMessage`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(m,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${r}-${e}-realtime-connect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[u,"Arn"]},MemorySize:s,Timeout:c,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:l},CHANNELS_TABLE:{Ref:d},ENVIRONMENT:e}},Code:{ZipFile:this.generateConnectHandlerCode()}},DependsOn:[u,l]}),this.builder.addResource(f,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${r}-${e}-realtime-disconnect`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[u,"Arn"]},MemorySize:s,Timeout:c,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:l},CHANNELS_TABLE:{Ref:d},ENVIRONMENT:e}},Code:{ZipFile:this.generateDisconnectHandlerCode()}},DependsOn:[u,l,d]}),this.builder.addResource(h,{Type:"AWS::Lambda::Function",Properties:{FunctionName:`${r}-${e}-realtime-message`,Runtime:"nodejs20.x",Handler:"index.handler",Role:{"Fn::GetAtt":[u,"Arn"]},MemorySize:s,Timeout:c,Environment:{Variables:{CONNECTIONS_TABLE:{Ref:l},CHANNELS_TABLE:{Ref:d},ENVIRONMENT:e}},Code:{ZipFile:this.generateMessageHandlerCode()}},DependsOn:[u,l,d]});let S=`${r}${e}RealtimeApi`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(S,{Type:"AWS::ApiGatewayV2::Api",Properties:{Name:n,ProtocolType:"WEBSOCKET",RouteSelectionExpression:"$request.body.action",Tags:{Name:n,Environment:e}}});let y=`${m}Permission`,v=`${f}Permission`,R=`${h}Permission`;this.builder.addResource(y,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:m},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${S}}/*/$connect`}}}),this.builder.addResource(v,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:f},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${S}}/*/$disconnect`}}}),this.builder.addResource(R,{Type:"AWS::Lambda::Permission",Properties:{FunctionName:{Ref:h},Action:"lambda:InvokeFunction",Principal:"apigateway.amazonaws.com",SourceArn:{"Fn::Sub":`arn:aws:execute-api:\${AWS::Region}:\${AWS::AccountId}:\${${S}}/*/$default`}}});let b=`${S}ConnectInteg`,A=`${S}DisconnectInteg`,k=`${S}MessageInteg`;this.builder.addResource(b,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:S},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${m}.Arn}/invocations`}}}),this.builder.addResource(A,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:S},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${f}.Arn}/invocations`}}}),this.builder.addResource(k,{Type:"AWS::ApiGatewayV2::Integration",Properties:{ApiId:{Ref:S},IntegrationType:"AWS_PROXY",IntegrationUri:{"Fn::Sub":`arn:aws:apigateway:\${AWS::Region}:lambda:path/2015-03-31/functions/\${${h}.Arn}/invocations`}}}),this.builder.addResource(`${S}ConnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:S},RouteKey:"$connect",AuthorizationType:"NONE",Target:{"Fn::Sub":`integrations/\${${b}}`}}}),this.builder.addResource(`${S}DisconnectRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:S},RouteKey:"$disconnect",Target:{"Fn::Sub":`integrations/\${${A}}`}}}),this.builder.addResource(`${S}DefaultRoute`,{Type:"AWS::ApiGatewayV2::Route",Properties:{ApiId:{Ref:S},RouteKey:"$default",Target:{"Fn::Sub":`integrations/\${${k}}`}}});let T=`${S}Stage`;if(this.builder.addResource(T,{Type:"AWS::ApiGatewayV2::Stage",Properties:{ApiId:{Ref:S},StageName:e,AutoDeploy:!0,DefaultRouteSettings:{ThrottlingBurstLimit:i.messagesPerSecond||1000,ThrottlingRateLimit:i.messagesPerSecond||1000}}}),t.monitoring?.enabled){let D=t.monitoring,E=D.notificationTopicArn;if(!E&&D.notificationEmails?.length){let N=`${S}AlarmTopic`;this.builder.addResource(N,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${r}-${e}-realtime-alarms`,DisplayName:"Realtime WebSocket Alarms"}}),D.notificationEmails.forEach((_,B)=>{this.builder.addResource(`${N}Sub${B}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:N},Protocol:"email",Endpoint:_}})}),E={Ref:N}}if(D.connectionThreshold)this.builder.addResource(`${S}ConnectionAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${r}-${e}-realtime-connections`,AlarmDescription:`WebSocket connections exceed ${D.connectionThreshold}`,MetricName:"ConnectCount",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:300,EvaluationPeriods:2,Threshold:D.connectionThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:S}}],...E&&{AlarmActions:[E]}}});if(D.errorThreshold)this.builder.addResource(`${S}ErrorAlarm`,{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${r}-${e}-realtime-errors`,AlarmDescription:`WebSocket errors exceed ${D.errorThreshold}/min`,MetricName:"ExecutionError",Namespace:"AWS/ApiGateway",Statistic:"Sum",Period:60,EvaluationPeriods:3,Threshold:D.errorThreshold,ComparisonOperator:"GreaterThanThreshold",Dimensions:[{Name:"ApiId",Value:{Ref:S}}],...E&&{AlarmActions:[E]}}})}this.builder.addOutput(`${S}Endpoint`,{Description:"WebSocket API endpoint URL",Value:{"Fn::Sub":`wss://\${${S}}.execute-api.\${AWS::Region}.amazonaws.com/${e}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}}),this.builder.addOutput(`${S}Id`,{Description:"WebSocket API ID",Value:{Ref:S},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-api-id"}}})}generateConnectHandlerCode(){return`
89
98
  const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
90
99
  const { DynamoDBDocumentClient, PutCommand } = require('@aws-sdk/lib-dynamodb');
91
100
 
@@ -244,206 +253,207 @@ catch (error) {
244
253
  return { statusCode: 500, body: JSON.stringify({ error: 'Internal error' }) };
245
254
  }
246
255
  };
247
- `.trim()}generateRealtimeServerResources(G,$){let J=this.mergedConfig.infrastructure?.realtime;if(!J)return;let Y=J.server||{},X=Y.port||6001,Q=Y.instances||1,Z=`${G}${$}RealtimeSG`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(Z,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`Security group for ${G} realtime WebSocket server`,GroupName:`${G}-${$}-realtime-sg`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:X,ToPort:X,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:`${G}-${$}-realtime-sg`},{Key:"Environment",Value:$}]}});let z=`${G}${$}RealtimeTaskDef`.replace(/[^a-zA-Z0-9]/g,""),F=`${G}${$}RealtimeTaskRole`.replace(/[^a-zA-Z0-9]/g,""),U=`${G}${$}RealtimeExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(U,{Type:"AWS::IAM::Role",Properties:{RoleName:`${G}-${$}-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(F,{Type:"AWS::IAM::Role",Properties:{RoleName:`${G}-${$}-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:"*"},...Y.redis?.enabled?[{Effect:"Allow",Action:["elasticache:DescribeCacheClusters","elasticache:DescribeReplicationGroups"],Resource:"*"}]:[]]}}]}});let M=[{Name:"BROADCAST_HOST",Value:Y.host||"0.0.0.0"},{Name:"BROADCAST_PORT",Value:String(X)},{Name:"NODE_ENV",Value:$==="production"?"production":"development"}];if(Y.redis?.enabled){if(Y.redis.useElastiCache)M.push({Name:"REDIS_HOST",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Address"]}}),M.push({Name:"REDIS_PORT",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Port"]}});else M.push({Name:"REDIS_HOST",Value:Y.redis.host||"localhost"}),M.push({Name:"REDIS_PORT",Value:String(Y.redis.port||6379)});if(Y.redis.keyPrefix)M.push({Name:"REDIS_KEY_PREFIX",Value:Y.redis.keyPrefix})}this.builder.addResource(z,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${G}-${$}-realtime`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:"512",Memory:"1024",ExecutionRoleArn:{"Fn::GetAtt":[U,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[F,"Arn"]},ContainerDefinitions:[{Name:"realtime",Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${G}-realtime:latest`},Essential:!0,PortMappings:[{ContainerPort:X,Protocol:"tcp"}],Environment:M,LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":`/ecs/${G}-${$}-realtime`,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":"realtime"}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${X}${Y.healthCheckPath||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${G}-${$}-realtime`},{Key:"Environment",Value:$}]},DependsOn:[U,F]});let H=`${G}${$}RealtimeLogs`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(H,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:`/ecs/${G}-${$}-realtime`,RetentionInDays:30}});let j=`${G}${$}RealtimeService`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(j,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${G}-${$}-realtime`,Cluster:{Ref:"ECSCluster"},TaskDefinition:{Ref:z},DesiredCount:Q,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:Z}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},HealthCheckGracePeriodSeconds:60,Tags:[{Key:"Name",Value:`${G}-${$}-realtime`},{Key:"Environment",Value:$}]},DependsOn:[z,Z,H]}),Y.autoScaling){let V=Y.autoScaling,T=`${G}${$}RealtimeScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(T,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:V.max||10,MinCapacity:V.min||1,ResourceId:{"Fn::Sub":`service/\${ECSCluster}/${G}-${$}-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:j}),V.targetCPU)this.builder.addResource(`${G}${$}RealtimeCPUPolicy`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${G}-${$}-realtime-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:T},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:V.targetCPU,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(J.monitoring?.enabled){let V=J.monitoring,T=V.notificationTopicArn;if(!T&&V.notificationEmails?.length){let A=`${G}${$}RealtimeAlarmTopic`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(A,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${G}-${$}-realtime-alarms`,DisplayName:"Realtime Server Alarms"}}),V.notificationEmails.forEach((q,O)=>{this.builder.addResource(`${A}Sub${O}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:A},Protocol:"email",Endpoint:q}})}),T={Ref:A}}this.builder.addResource(`${G}${$}RealtimeCPUAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${G}-${$}-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:`${G}-${$}-realtime`}],...T&&{AlarmActions:[T]}},DependsOn:j}),this.builder.addResource(`${G}${$}RealtimeMemoryAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${G}-${$}-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:`${G}-${$}-realtime`}],...T&&{AlarmActions:[T]}},DependsOn:j})}this.builder.addOutput(`${G}${$}RealtimeEndpoint`.replace(/[^a-zA-Z0-9]/g,""),{Description:"Realtime WebSocket server endpoint",Value:{"Fn::Sub":`wss://${G}-${$}-realtime.\${AWS::Region}.elb.amazonaws.com:${X}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}})}generateBroadcastConfig(){let G=this.mergedConfig.infrastructure?.realtime;if(!G||G.mode!=="server")return"";let $=G.server||{};return`import type { BroadcastConfig } from 'ts-broadcasting'
256
+ `.trim()}generateRealtimeServerResources(r,e){let t=this.mergedConfig.infrastructure?.realtime;if(!t)return;let n=t.server||{},i=n.port||6001,o=n.instances||1,s=`${r}${e}RealtimeSG`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(s,{Type:"AWS::EC2::SecurityGroup",Properties:{GroupDescription:`Security group for ${r} realtime WebSocket server`,GroupName:`${r}-${e}-realtime-sg`,VpcId:{Ref:"VPC"},SecurityGroupIngress:[{IpProtocol:"tcp",FromPort:i,ToPort:i,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:`${r}-${e}-realtime-sg`},{Key:"Environment",Value:e}]}});let c=`${r}${e}RealtimeTaskDef`.replace(/[^a-zA-Z0-9]/g,""),l=`${r}${e}RealtimeTaskRole`.replace(/[^a-zA-Z0-9]/g,""),d=`${r}${e}RealtimeExecRole`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(d,{Type:"AWS::IAM::Role",Properties:{RoleName:`${r}-${e}-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(l,{Type:"AWS::IAM::Role",Properties:{RoleName:`${r}-${e}-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:"*"},...n.redis?.enabled?[{Effect:"Allow",Action:["elasticache:DescribeCacheClusters","elasticache:DescribeReplicationGroups"],Resource:"*"}]:[]]}}]}});let u=[{Name:"BROADCAST_HOST",Value:n.host||"0.0.0.0"},{Name:"BROADCAST_PORT",Value:String(i)},{Name:"NODE_ENV",Value:e==="production"?"production":"development"}];if(n.redis?.enabled){if(n.redis.useElastiCache)u.push({Name:"REDIS_HOST",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Address"]}}),u.push({Name:"REDIS_PORT",Value:{"Fn::GetAtt":["CacheCluster","RedisEndpoint.Port"]}});else u.push({Name:"REDIS_HOST",Value:n.redis.host||"localhost"}),u.push({Name:"REDIS_PORT",Value:String(n.redis.port||6379)});if(n.redis.keyPrefix)u.push({Name:"REDIS_KEY_PREFIX",Value:n.redis.keyPrefix})}this.builder.addResource(c,{Type:"AWS::ECS::TaskDefinition",Properties:{Family:`${r}-${e}-realtime`,NetworkMode:"awsvpc",RequiresCompatibilities:["FARGATE"],Cpu:"512",Memory:"1024",ExecutionRoleArn:{"Fn::GetAtt":[d,"Arn"]},TaskRoleArn:{"Fn::GetAtt":[l,"Arn"]},ContainerDefinitions:[{Name:"realtime",Image:{"Fn::Sub":`\${AWS::AccountId}.dkr.ecr.\${AWS::Region}.amazonaws.com/${r}-realtime:latest`},Essential:!0,PortMappings:[{ContainerPort:i,Protocol:"tcp"}],Environment:u,LogConfiguration:{LogDriver:"awslogs",Options:{"awslogs-group":`/ecs/${r}-${e}-realtime`,"awslogs-region":{Ref:"AWS::Region"},"awslogs-stream-prefix":"realtime"}},HealthCheck:{Command:["CMD-SHELL",`curl -f http://localhost:${i}${n.healthCheckPath||"/health"} || exit 1`],Interval:30,Timeout:5,Retries:3,StartPeriod:60}}],Tags:[{Key:"Name",Value:`${r}-${e}-realtime`},{Key:"Environment",Value:e}]},DependsOn:[d,l]});let m=`${r}${e}RealtimeLogs`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(m,{Type:"AWS::Logs::LogGroup",Properties:{LogGroupName:`/ecs/${r}-${e}-realtime`,RetentionInDays:30}});let f=`${r}${e}RealtimeService`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(f,{Type:"AWS::ECS::Service",Properties:{ServiceName:`${r}-${e}-realtime`,Cluster:{Ref:"ECSCluster"},TaskDefinition:{Ref:c},DesiredCount:o,LaunchType:"FARGATE",NetworkConfiguration:{AwsvpcConfiguration:{AssignPublicIp:"ENABLED",SecurityGroups:[{Ref:s}],Subnets:[{Ref:"PublicSubnet1"},{Ref:"PublicSubnet2"}]}},HealthCheckGracePeriodSeconds:60,Tags:[{Key:"Name",Value:`${r}-${e}-realtime`},{Key:"Environment",Value:e}]},DependsOn:[c,s,m]}),n.autoScaling){let h=n.autoScaling,S=`${r}${e}RealtimeScalableTarget`.replace(/[^a-zA-Z0-9]/g,"");if(this.builder.addResource(S,{Type:"AWS::ApplicationAutoScaling::ScalableTarget",Properties:{MaxCapacity:h.max||10,MinCapacity:h.min||1,ResourceId:{"Fn::Sub":`service/\${ECSCluster}/${r}-${e}-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:f}),h.targetCPU)this.builder.addResource(`${r}${e}RealtimeCPUPolicy`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::ApplicationAutoScaling::ScalingPolicy",Properties:{PolicyName:`${r}-${e}-realtime-cpu-scaling`,PolicyType:"TargetTrackingScaling",ScalingTargetId:{Ref:S},TargetTrackingScalingPolicyConfiguration:{PredefinedMetricSpecification:{PredefinedMetricType:"ECSServiceAverageCPUUtilization"},TargetValue:h.targetCPU,ScaleInCooldown:300,ScaleOutCooldown:60}}})}if(t.monitoring?.enabled){let h=t.monitoring,S=h.notificationTopicArn;if(!S&&h.notificationEmails?.length){let y=`${r}${e}RealtimeAlarmTopic`.replace(/[^a-zA-Z0-9]/g,"");this.builder.addResource(y,{Type:"AWS::SNS::Topic",Properties:{TopicName:`${r}-${e}-realtime-alarms`,DisplayName:"Realtime Server Alarms"}}),h.notificationEmails.forEach((v,R)=>{this.builder.addResource(`${y}Sub${R}`,{Type:"AWS::SNS::Subscription",Properties:{TopicArn:{Ref:y},Protocol:"email",Endpoint:v}})}),S={Ref:y}}this.builder.addResource(`${r}${e}RealtimeCPUAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${r}-${e}-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:`${r}-${e}-realtime`}],...S&&{AlarmActions:[S]}},DependsOn:f}),this.builder.addResource(`${r}${e}RealtimeMemoryAlarm`.replace(/[^a-zA-Z0-9]/g,""),{Type:"AWS::CloudWatch::Alarm",Properties:{AlarmName:`${r}-${e}-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:`${r}-${e}-realtime`}],...S&&{AlarmActions:[S]}},DependsOn:f})}this.builder.addOutput(`${r}${e}RealtimeEndpoint`.replace(/[^a-zA-Z0-9]/g,""),{Description:"Realtime WebSocket server endpoint",Value:{"Fn::Sub":`wss://${r}-${e}-realtime.\${AWS::Region}.elb.amazonaws.com:${i}`},Export:{Name:{"Fn::Sub":"${AWS::StackName}-realtime-endpoint"}}})}generateBroadcastConfig(){let r=this.mergedConfig.infrastructure?.realtime;if(!r||r.mode!=="server")return"";let e=r.server||{};return`import type { BroadcastConfig } from 'ts-broadcasting'
248
257
 
249
258
  export default {
250
259
  verbose: ${this.environment!=="production"},
251
- driver: '${$.driver||"bun"}',
260
+ driver: '${e.driver||"bun"}',
252
261
  default: 'bun',
253
262
 
254
263
  connections: {
255
264
  bun: {
256
265
  driver: 'bun',
257
- host: process.env.BROADCAST_HOST || '${$.host||"0.0.0.0"}',
258
- port: Number(process.env.BROADCAST_PORT) || ${$.port||6001},
259
- scheme: '${$.scheme||"wss"}',
266
+ host: process.env.BROADCAST_HOST || '${e.host||"0.0.0.0"}',
267
+ port: Number(process.env.BROADCAST_PORT) || ${e.port||6001},
268
+ scheme: '${e.scheme||"wss"}',
260
269
  options: {
261
- idleTimeout: ${$.idleTimeout||120},
262
- maxPayloadLength: ${$.maxPayloadLength||16777216},
263
- backpressureLimit: ${$.backpressureLimit||1048576},
264
- closeOnBackpressureLimit: ${$.closeOnBackpressureLimit||!1},
265
- sendPings: ${$.sendPings!==!1},
266
- perMessageDeflate: ${$.perMessageDeflate!==!1},
270
+ idleTimeout: ${e.idleTimeout||120},
271
+ maxPayloadLength: ${e.maxPayloadLength||16777216},
272
+ backpressureLimit: ${e.backpressureLimit||1048576},
273
+ closeOnBackpressureLimit: ${e.closeOnBackpressureLimit||!1},
274
+ sendPings: ${e.sendPings!==!1},
275
+ perMessageDeflate: ${e.perMessageDeflate!==!1},
267
276
  },
268
277
  },
269
278
  },
270
- ${$.redis?.enabled?`
279
+ ${e.redis?.enabled?`
271
280
  redis: {
272
- host: process.env.REDIS_HOST || '${$.redis.host||"localhost"}',
273
- port: Number(process.env.REDIS_PORT) || ${$.redis.port||6379},
274
- ${$.redis.password?`password: process.env.REDIS_PASSWORD || '${$.redis.password}',`:""}
275
- database: ${$.redis.database||0},
276
- keyPrefix: '${$.redis.keyPrefix||"broadcasting:"}',
281
+ host: process.env.REDIS_HOST || '${e.redis.host||"localhost"}',
282
+ port: Number(process.env.REDIS_PORT) || ${e.redis.port||6379},
283
+ ${e.redis.password?`password: process.env.REDIS_PASSWORD || '${e.redis.password}',`:""}
284
+ database: ${e.redis.database||0},
285
+ keyPrefix: '${e.redis.keyPrefix||"broadcasting:"}',
277
286
  },
278
287
  `:""}
279
- ${$.rateLimit?.enabled?`
288
+ ${e.rateLimit?.enabled?`
280
289
  rateLimit: {
281
- max: ${$.rateLimit.max||100},
282
- window: ${$.rateLimit.window||60000},
283
- perChannel: ${$.rateLimit.perChannel!==!1},
284
- perUser: ${$.rateLimit.perUser!==!1},
290
+ max: ${e.rateLimit.max||100},
291
+ window: ${e.rateLimit.window||60000},
292
+ perChannel: ${e.rateLimit.perChannel!==!1},
293
+ perUser: ${e.rateLimit.perUser!==!1},
285
294
  },
286
295
  `:""}
287
- ${$.loadManagement?.enabled?`
296
+ ${e.loadManagement?.enabled?`
288
297
  loadManagement: {
289
298
  enabled: true,
290
- maxConnections: ${$.loadManagement.maxConnections||1e4},
291
- maxSubscriptionsPerConnection: ${$.loadManagement.maxSubscriptionsPerConnection||100},
292
- shedLoadThreshold: ${$.loadManagement.shedLoadThreshold||0.8},
299
+ maxConnections: ${e.loadManagement.maxConnections||1e4},
300
+ maxSubscriptionsPerConnection: ${e.loadManagement.maxSubscriptionsPerConnection||100},
301
+ shedLoadThreshold: ${e.loadManagement.shedLoadThreshold||0.8},
293
302
  },
294
303
  `:""}
295
304
  } satisfies BroadcastConfig
296
- `}toYAML(){return this.builder.toYAML()}toJSON(){return this.builder.toJSON()}getBuilder(){return this.builder}}function s0(G){let $=[],J=[];if(!G.AWSTemplateFormatVersion)$.push({path:"AWSTemplateFormatVersion",message:"AWSTemplateFormatVersion is required",severity:"error"});else if(G.AWSTemplateFormatVersion!=="2010-09-09")J.push({path:"AWSTemplateFormatVersion",message:'AWSTemplateFormatVersion should be "2010-09-09"',severity:"warning"});if(!G.Resources||Object.keys(G.Resources).length===0)$.push({path:"Resources",message:"At least one resource is required",severity:"error"});else for(let[Y,X]of Object.entries(G.Resources))$G(Y,X,$,J);if(G.Resources){let Y=GG(G.Resources);if(Y.length>0)$.push({path:"Resources",message:`Circular dependencies detected: ${Y.join(" -> ")}`,severity:"error"})}if(G.Parameters)for(let[Y,X]of Object.entries(G.Parameters))JG(Y,X,$,J);if(G.Outputs)for(let[Y,X]of Object.entries(G.Outputs))YG(Y,X,$,J);return{valid:$.length===0,errors:$,warnings:J}}function $G(G,$,J,Y){if(!$.Type)J.push({path:`Resources.${G}.Type`,message:"Resource Type is required",severity:"error"});if(!/^[a-zA-Z0-9]+$/.test(G))Y.push({path:`Resources.${G}`,message:"Logical ID should only contain alphanumeric characters",severity:"warning"});if($.Type&&!$.Type.startsWith("AWS::")&&!$.Type.startsWith("Custom::"))J.push({path:`Resources.${G}.Type`,message:'Resource Type must start with "AWS::" or "Custom::"',severity:"error"});if($.Properties){for(let[X,Q]of Object.entries($.Properties))if(Q===void 0||Q===null)Y.push({path:`Resources.${G}.Properties.${X}`,message:"Property has undefined or null value",severity:"warning"})}if($.DependsOn){if(typeof $.DependsOn==="string"){if($.DependsOn===G)J.push({path:`Resources.${G}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}else if(Array.isArray($.DependsOn)){if($.DependsOn.includes(G))J.push({path:`Resources.${G}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}}}function JG(G,$,J,Y){if(!$.Type)J.push({path:`Parameters.${G}.Type`,message:"Parameter Type is required",severity:"error"});let X=["String","Number","List<Number>","CommaDelimitedList","AWS::SSM::Parameter::Value<String>"];if($.Type&&!X.includes($.Type)&&!$.Type.startsWith("AWS::"))Y.push({path:`Parameters.${G}.Type`,message:`Uncommon parameter type: ${$.Type}`,severity:"warning"});if($.NoEcho&&$.Default)Y.push({path:`Parameters.${G}`,message:"NoEcho parameters should not have default values",severity:"warning"})}function YG(G,$,J,Y){if(!$.Value)J.push({path:`Outputs.${G}.Value`,message:"Output Value is required",severity:"error"})}function GG(G){let $={};for(let[Z,z]of Object.entries(G)){if($[Z]=[],z.DependsOn){if(typeof z.DependsOn==="string")$[Z].push(z.DependsOn);else if(Array.isArray(z.DependsOn))$[Z].push(...z.DependsOn)}let F=I6(z);$[Z].push(...F)}let J=new Set,Y=new Set,X=[];function Q(Z,z){J.add(Z),Y.add(Z),z.push(Z);let F=$[Z]||[];for(let U of F)if(!J.has(U)){if(Q(U,z))return!0}else if(Y.has(U)){let M=z.indexOf(U);return X.push(...z.slice(M),U),!0}return z.pop(),Y.delete(Z),!1}for(let Z of Object.keys($))if(!J.has(Z)){if(Q(Z,[]))return X}return[]}function I6(G,$=[]){if(typeof G!=="object"||G===null)return $;if(Array.isArray(G)){for(let J of G)I6(J,$);return $}if(G.Ref&&typeof G.Ref==="string"&&!G.Ref.startsWith("AWS::"))$.push(G.Ref);if(G["Fn::GetAtt"]){let J=G["Fn::GetAtt"];if(Array.isArray(J)&&J.length>0)$.push(J[0])}for(let J of Object.values(G))I6(J,$);return $}function i0(G){let $=[],J=[],Y=new TextEncoder().encode(G).length,X=51200,Q=460800;if(Y>51200)if(Y>460800)$.push({path:"template",message:`Template size (${Y} bytes) exceeds maximum allowed size of 460800 bytes`,severity:"error"});else J.push({path:"template",message:`Template size (${Y} bytes) exceeds direct upload limit (51200 bytes). You must upload to S3 first.`,severity:"warning"});return{valid:$.length===0,errors:$,warnings:J}}function t0(G){let $=[],J=[],Y=G.Resources?Object.keys(G.Resources).length:0,X=G.Parameters?Object.keys(G.Parameters).length:0,Q=G.Outputs?Object.keys(G.Outputs).length:0;if(Y>500)$.push({path:"Resources",message:`Template has ${Y} resources, exceeding the limit of 500`,severity:"error"});else if(Y>200)J.push({path:"Resources",message:`Template has ${Y} resources. Consider using nested stacks for better organization.`,severity:"warning"});if(X>200)$.push({path:"Parameters",message:`Template has ${X} parameters, exceeding the limit of 200`,severity:"error"});if(Q>200)$.push({path:"Outputs",message:`Template has ${Q} outputs, exceeding the limit of 200`,severity:"error"});return{valid:$.length===0,errors:$,warnings:J}}function V$(G,$={}){let J={ok:!1,code:"unsupported_capability",exitCode:2,command:G,provider:$.provider,target:$.target,retryable:!1,nextAction:$.nextAction??"Run `cloud capabilities` and choose a supported driver operation.",message:$.message??`${G} is not supported by the selected provider driver.`};if(K(`${J.message} ${J.nextAction}`),$.setProcessExitCode!==!1)process.exitCode=J.exitCode;return J}function k6(G){G.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($)=>{R("Generating CloudFormation Templates");let J=new E("Loading configuration...");J.start();try{let Y=await x();J.succeed("Configuration loaded");let X=$?.output||"cloudformation",Q=$?.format||"json",Z=$?.env||"production";if(!XG(X))await QG(X,{recursive:!0});y("Generating infrastructure...");let z=new C0({config:Y,environment:Z,modules:$?.module?[$.module]:void 0}),F=new E("Generating CloudFormation template...");F.start(),z.generate();let U=Q==="yaml"?z.toYAML():z.toJSON();F.succeed("Template generated"),y("Validating template...");let M=JSON.parse(z.toJSON()),H=s0(M),j=i0(U),V=t0(M),T=[...H.errors,...j.errors,...V.errors];if(T.length>0){K("Template validation failed:");for(let _ of T)K(` - ${_.path}: ${_.message}`)}else B("Template validated successfully");let A=[...H.warnings,...j.warnings,...V.warnings];if(A.length>0)for(let _ of A)N(` - ${_.path}: ${_.message}`);let q=WG(X,`${Z}.${Q}`);await ZG(q,U),B(`Generated ${q}`);let O=z.getBuilder(),L=Object.keys(O.getResources()).length;W(`
297
- Generated ${L} resources:`);let D=O.getResources(),P={};for(let _ of Object.values(D)){let S=_.Type;P[S]=(P[S]||0)+1}let b=Object.entries(P).sort((_,S)=>S[1]-_[1]);for(let[_,S]of b)W(` - ${_}: ${S}`);W(`
305
+ `}toYAML(){return this.builder.toYAML()}toJSON(){return this.builder.toJSON()}getBuilder(){return this.builder}}function on(r){let e=[],t=[];if(!r.AWSTemplateFormatVersion)e.push({path:"AWSTemplateFormatVersion",message:"AWSTemplateFormatVersion is required",severity:"error"});else if(r.AWSTemplateFormatVersion!=="2010-09-09")t.push({path:"AWSTemplateFormatVersion",message:'AWSTemplateFormatVersion should be "2010-09-09"',severity:"warning"});if(!r.Resources||Object.keys(r.Resources).length===0)e.push({path:"Resources",message:"At least one resource is required",severity:"error"});else for(let[n,i]of Object.entries(r.Resources))ju(n,i,e,t);if(r.Resources){let n=Bu(r.Resources);if(n.length>0)e.push({path:"Resources",message:`Circular dependencies detected: ${n.join(" -> ")}`,severity:"error"})}if(r.Parameters)for(let[n,i]of Object.entries(r.Parameters))Fu(n,i,e,t);if(r.Outputs)for(let[n,i]of Object.entries(r.Outputs))Mu(n,i,e,t);return{valid:e.length===0,errors:e,warnings:t}}function ju(r,e,t,n){if(!e.Type)t.push({path:`Resources.${r}.Type`,message:"Resource Type is required",severity:"error"});if(!/^[a-zA-Z0-9]+$/.test(r))n.push({path:`Resources.${r}`,message:"Logical ID should only contain alphanumeric characters",severity:"warning"});if(e.Type&&!e.Type.startsWith("AWS::")&&!e.Type.startsWith("Custom::"))t.push({path:`Resources.${r}.Type`,message:'Resource Type must start with "AWS::" or "Custom::"',severity:"error"});if(e.Properties){for(let[i,o]of Object.entries(e.Properties))if(o===void 0||o===null)n.push({path:`Resources.${r}.Properties.${i}`,message:"Property has undefined or null value",severity:"warning"})}if(e.DependsOn){if(typeof e.DependsOn==="string"){if(e.DependsOn===r)t.push({path:`Resources.${r}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}else if(Array.isArray(e.DependsOn)){if(e.DependsOn.includes(r))t.push({path:`Resources.${r}.DependsOn`,message:"Resource cannot depend on itself",severity:"error"})}}}function Fu(r,e,t,n){if(!e.Type)t.push({path:`Parameters.${r}.Type`,message:"Parameter Type is required",severity:"error"});let i=["String","Number","List<Number>","CommaDelimitedList","AWS::SSM::Parameter::Value<String>"];if(e.Type&&!i.includes(e.Type)&&!e.Type.startsWith("AWS::"))n.push({path:`Parameters.${r}.Type`,message:`Uncommon parameter type: ${e.Type}`,severity:"warning"});if(e.NoEcho&&e.Default)n.push({path:`Parameters.${r}`,message:"NoEcho parameters should not have default values",severity:"warning"})}function Mu(r,e,t,n){if(!e.Value)t.push({path:`Outputs.${r}.Value`,message:"Output Value is required",severity:"error"})}function Bu(r){let e={};for(let[s,c]of Object.entries(r)){if(e[s]=[],c.DependsOn){if(typeof c.DependsOn==="string")e[s].push(c.DependsOn);else if(Array.isArray(c.DependsOn))e[s].push(...c.DependsOn)}let l=Ur(c);e[s].push(...l)}let t=new Set,n=new Set,i=[];function o(s,c){t.add(s),n.add(s),c.push(s);let l=e[s]||[];for(let d of l)if(!t.has(d)){if(o(d,c))return!0}else if(n.has(d)){let u=c.indexOf(d);return i.push(...c.slice(u),d),!0}return c.pop(),n.delete(s),!1}for(let s of Object.keys(e))if(!t.has(s)){if(o(s,[]))return i}return[]}function Ur(r,e=[]){if(typeof r!=="object"||r===null)return e;if(Array.isArray(r)){for(let t of r)Ur(t,e);return e}if(r.Ref&&typeof r.Ref==="string"&&!r.Ref.startsWith("AWS::"))e.push(r.Ref);if(r["Fn::GetAtt"]){let t=r["Fn::GetAtt"];if(Array.isArray(t)&&t.length>0)e.push(t[0])}for(let t of Object.values(r))Ur(t,e);return e}function sn(r){let e=[],t=[],n=new TextEncoder().encode(r).length,i=51200,o=460800;if(n>51200)if(n>460800)e.push({path:"template",message:`Template size (${n} bytes) exceeds maximum allowed size of 460800 bytes`,severity:"error"});else t.push({path:"template",message:`Template size (${n} bytes) exceeds direct upload limit (51200 bytes). You must upload to S3 first.`,severity:"warning"});return{valid:e.length===0,errors:e,warnings:t}}function an(r){let e=[],t=[],n=r.Resources?Object.keys(r.Resources).length:0,i=r.Parameters?Object.keys(r.Parameters).length:0,o=r.Outputs?Object.keys(r.Outputs).length:0;if(n>500)e.push({path:"Resources",message:`Template has ${n} resources, exceeding the limit of 500`,severity:"error"});else if(n>200)t.push({path:"Resources",message:`Template has ${n} resources. Consider using nested stacks for better organization.`,severity:"warning"});if(i>200)e.push({path:"Parameters",message:`Template has ${i} parameters, exceeding the limit of 200`,severity:"error"});if(o>200)e.push({path:"Outputs",message:`Template has ${o} outputs, exceeding the limit of 200`,severity:"error"});return{valid:e.length===0,errors:e,warnings:t}}function be(r,e={}){let t={ok:!1,code:"unsupported_capability",exitCode:2,command:r,provider:e.provider,target:e.target,retryable:!1,nextAction:e.nextAction??"Run `cloud capabilities` and choose a supported driver operation.",message:e.message??`${r} is not supported by the selected provider driver.`};if(p(`${t.message} ${t.nextAction}`),e.setProcessExitCode!==!1)process.exitCode=t.exitCode;return t}function Vr(r){r.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(e)=>{w("Generating CloudFormation Templates");let t=new C("Loading configuration...");t.start();try{let n=await P();t.succeed("Configuration loaded");let i=e?.output||"cloudformation",o=e?.format||"json",s=e?.env||"production";if(!_u(i))await zu(i,{recursive:!0});M("Generating infrastructure...");let c=new Ut({config:n,environment:s,modules:e?.module?[e.module]:void 0}),l=new C("Generating CloudFormation template...");l.start(),c.generate();let d=o==="yaml"?c.toYAML():c.toJSON();l.succeed("Template generated"),M("Validating template...");let u=JSON.parse(c.toJSON()),m=on(u),f=sn(d),h=an(u),S=[...m.errors,...f.errors,...h.errors];if(S.length>0){p("Template validation failed:");for(let D of S)p(` - ${D.path}: ${D.message}`)}else g("Template validated successfully");let y=[...m.warnings,...f.warnings,...h.warnings];if(y.length>0)for(let D of y)I(` - ${D.path}: ${D.message}`);let v=Vu(i,`${s}.${o}`);await Uu(v,d),g(`Generated ${v}`);let R=c.getBuilder(),b=Object.keys(R.getResources()).length;a(`
306
+ Generated ${b} resources:`);let A=R.getResources(),k={};for(let D of Object.values(A)){let E=D.Type;k[E]=(k[E]||0)+1}let T=Object.entries(k).sort((D,E)=>E[1]-D[1]);for(let[D,E]of T)a(` - ${D}: ${E}`);a(`
298
307
  Next steps:
299
- 1. Review the generated templates in ${X}/
300
- 2. Run 'cloud deploy' to deploy your infrastructure`)}catch(Y){J.fail("Failed to generate templates"),K(Y instanceof Error?Y.message:"Unknown error")}}),G.command("generate:preview","Preview what will be generated").action(async()=>{V$("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."})}),G.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($)=>{R("Infrastructure Diff");try{let J=await x(),Y=$?.env||"production",{resolveProjectStackName:X}=await import("./chunk-5n9fmabb.js"),Q=$?.stack||X(J,Y),Z=J.project.region||"us-east-1";W(`Stack: ${Q}`),W(`Region: ${Z}`),W(`Environment: ${Y}`),y("Generating template from configuration...");let z=new C0({config:J,environment:Y});z.generate();let F=z.toJSON(),U=JSON.parse(F);y("Fetching deployed template...");let M=new Y$(Z),H=null;try{let D=await M.getTemplate(Q);if(D.TemplateBody)H=JSON.parse(D.TemplateBody)}catch(D){if(D.message?.includes("does not exist")){N(`Stack "${Q}" does not exist yet`),W(`
301
- This will be a new deployment with the following resources:`);let P=Object.keys(U.Resources||{}).length;W(`
302
- Resources to create: ${P}`);let b={};for(let _ of Object.values(U.Resources||{})){let S=_.Type;b[S]=(b[S]||0)+1}for(let[_,S]of Object.entries(b).sort((I,g)=>g[1]-I[1]))W(` + ${_}: ${S}`);W("\nRun `cloud deploy` to create this stack");return}throw D}y("Comparing templates...");let j=H.Resources||{},V=U.Resources||{},T=new Set(Object.keys(j)),A=new Set(Object.keys(V)),q=[];for(let D of A)if(!T.has(D))q.push(D);let O=[];for(let D of T)if(!A.has(D))O.push(D);let L=[];for(let D of A)if(T.has(D)){let P=JSON.stringify(j[D]),b=JSON.stringify(V[D]);if(P!==b)L.push(D)}if(q.length===0&&O.length===0&&L.length===0){B(`
303
- No changes detected - infrastructure is up to date`);return}if(W(`
308
+ 1. Review the generated templates in ${i}/
309
+ 2. Run 'cloud deploy' to deploy your infrastructure`)}catch(n){t.fail("Failed to generate templates"),p(n instanceof Error?n.message:"Unknown error")}}),r.command("generate:preview","Preview what will be generated").action(async()=>{be("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."})}),r.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(e)=>{w("Infrastructure Diff");try{let t=await P(),n=e?.env||"production",{resolveProjectStackName:i}=await import("./chunk-ezftw2q4.js"),o=e?.stack||i(t,n),s=t.project.region||"us-east-1";a(`Stack: ${o}`),a(`Region: ${s}`),a(`Environment: ${n}`),M("Generating template from configuration...");let c=new Ut({config:t,environment:n});c.generate();let l=c.toJSON(),d=JSON.parse(l);M("Fetching deployed template...");let u=new ie(s),m=null;try{let A=await u.getTemplate(o);if(A.TemplateBody)m=JSON.parse(A.TemplateBody)}catch(A){if(A.message?.includes("does not exist")){I(`Stack "${o}" does not exist yet`),a(`
310
+ This will be a new deployment with the following resources:`);let k=Object.keys(d.Resources||{}).length;a(`
311
+ Resources to create: ${k}`);let T={};for(let D of Object.values(d.Resources||{})){let E=D.Type;T[E]=(T[E]||0)+1}for(let[D,E]of Object.entries(T).sort((N,_)=>_[1]-N[1]))a(` + ${D}: ${E}`);a("\nRun `cloud deploy` to create this stack");return}throw A}M("Comparing templates...");let f=m.Resources||{},h=d.Resources||{},S=new Set(Object.keys(f)),y=new Set(Object.keys(h)),v=[];for(let A of y)if(!S.has(A))v.push(A);let R=[];for(let A of S)if(!y.has(A))R.push(A);let b=[];for(let A of y)if(S.has(A)){let k=JSON.stringify(f[A]),T=JSON.stringify(h[A]);if(k!==T)b.push(A)}if(v.length===0&&R.length===0&&b.length===0){g(`
312
+ No changes detected - infrastructure is up to date`);return}if(a(`
304
313
  Changes detected:
305
- `),q.length>0){B(`Resources to add (${q.length}):`);for(let D of q){let P=V[D].Type;W(` + ${D} (${P})`)}console.log()}if(O.length>0){K(`Resources to remove (${O.length}):`);for(let D of O){let P=j[D].Type;W(` - ${D} (${P})`)}console.log()}if(L.length>0){N(`Resources to modify (${L.length}):`);for(let D of L){let P=V[D].Type;W(` ~ ${D} (${P})`)}console.log()}W("Summary:"),W(` - Add: ${q.length}`),W(` - Remove: ${O.length}`),W(` - Modify: ${L.length}`),W("\nRun `cloud deploy` to apply these changes")}catch(J){K(`Diff failed: ${J.message}`)}})}function K0(G,$=0){let J=Number(G);return Number.isFinite(J)?J:$}function h6(G){return K0(G)*1024}function zG(G){let $=G?.date?.trim(),J=G?.time?.trim();if(!$||!J)return null;let Y=new Date(`${$}T${J}Z`);return Number.isFinite(Y.getTime())?Y.toISOString():null}function FG(G,$,J){let Y;try{Y=JSON.parse(G)}catch{return[]}let X=(Y?.sysstat?.hosts??[]).flatMap((Z)=>Z.statistics??[]),Q=[];for(let Z of X){let z=zG(Z.timestamp);if(!z)continue;let F=new Date(z).getTime();if(F<$.getTime()||F>J.getTime())continue;let U=Z["cpu-load"]?.find((H)=>H.cpu==="all")??Z["cpu-load"]?.[0],M=K0(U?.idle,100);Q.push({timestamp:z,cpuUsedPercent:Math.max(0,Math.min(100,100-M)),cpuUserPercent:K0(U?.user),cpuSystemPercent:K0(U?.system),cpuIowaitPercent:K0(U?.iowait),memoryUsedPercent:K0(Z.memory?.["memused-percent"]),memoryUsedBytes:h6(Z.memory?.memused),memoryAvailableBytes:h6(Z.memory?.avail),swapUsedPercent:K0(Z.memory?.["swpused-percent"]),swapUsedBytes:h6(Z.memory?.swpused)})}return Q.sort((Z,z)=>Z.timestamp.localeCompare(z.timestamp))}function UG(G){return Object.fromEntries(Object.entries(G.time_series??{}).map(([$,J])=>[$,(J.values??[]).map(([Y,X])=>({timestamp:new Date(Number(Y)*1000).toISOString(),value:K0(X,Number.NaN)})).filter((Y)=>Number.isFinite(Y.value))]))}function C4(G){return G.length?G.reduce(($,J)=>$+J,0)/G.length:null}function y6(G){return G.length?Math.max(...G):null}function MG(G){return G.length?Math.min(...G):null}async function f4(G){let[$,J]=await Promise.all([G.client.getServerMetrics(G.serverId,{types:["cpu","disk","network"],start:G.from,end:G.to,step:G.step}),C6(G.remote.host,"LC_ALL=C sadf -j -- -u -r -S",G.remote).catch(()=>"")]),Y=FG(J,G.from,G.to),X=UG($),Z=(X.cpu??[]).map((H)=>Math.max(0,Math.min(100,H.value/Math.max(1,G.cores)))),z=Y.map((H)=>H.cpuUsedPercent),F=Z.length?Z:z,U=Y.map((H)=>H.memoryUsedPercent),M=Y.map((H)=>H.swapUsedPercent);return{from:G.from.toISOString(),to:G.to.toISOString(),cores:G.cores,host:Y,provider:X,summary:{cpuAveragePercent:C4(F),cpuPeakPercent:y6(F),memoryAveragePercent:C4(U),memoryPeakPercent:y6(U),memoryMinimumAvailableBytes:MG(Y.map((H)=>H.memoryAvailableBytes)),swapPeakPercent:y6(M)}}}async function HG(){let G=await x(),$=u(process.cwd(),G),J=new O0($.store),Y=new i4(J,[new _1("aws"),new _1("hetzner"),new _1("ssh")]);return{controlPlane:$,store:J,service:Y}}async function s$(G){let $=await HG();try{return await G($)}finally{$.controlPlane.store.close()}}var T0=(G,$)=>{let J=G.store.list(G.controlPlane.project.id).find((Y)=>Y.id===$||Y.name===$);if(!J)throw Error(`Server ${$} was not found.`);return J},A$=(G)=>{K(G instanceof Error?G.message:String(G)),process.exitCode=1};function m6(G,$){let J=Number.parseInt(G??"",10);return Number.isFinite(J)&&J>=0?J:$}function KG(G="3h"){let $=G.match(/^(\d+)(m|h)$/);if(!$)throw Error("--range must use minutes or hours, for example 30m or 3h.");let J=Number($[1])*($[2]==="h"?3600000:60000);if(J<300000||J>86400000)throw Error("--range must be between 5 minutes and 24 hours.");return J}function e0(G,$="%"){return G==null?"unavailable":`${G.toFixed(1)}${$}`}async function jG(G,$){let J=await x();if(J.cloud?.provider!=="hetzner")throw Error("server:monitoring currently requires the Hetzner provider.");let Y=$.env??process.env.CLOUD_ENV??"production",X=i$(J,Y),Q=await I1(X);if(!Q?.serverId)throw Error(`No pinned Hetzner server was found for ${X}.`);if(![Q.serverName,String(Q.serverId),Q.stackName].includes(G))throw Error(`Refusing to read ${G}: project state is pinned to ${Q.serverName}.`);let Z=b1(J),z=new o0({apiToken:$1(Z.apiToken,J)}),F=await z.getServer(Q.serverId),U=Q.publicIp??F.public_net.ipv4?.ip;if(!U)throw Error(`Server ${F.name} has no public IPv4 address.`);let M=new Date,H=new Date(M.getTime()-KG($.range)),j=Math.min(3600,Math.max(60,m6($.step,60))),V=await f4({client:z,serverId:F.id,cores:Math.max(1,Number(F.server_type.cores)||1),from:H,to:M,step:j,remote:{host:U,user:Q.sshUser??Z.sshUser,identityFile:Z.sshPrivateKeyPath,connectTimeoutSec:15}});if($.json){console.log(JSON.stringify({schemaVersion:1,server:F.name,result:V},null,2));return}w(["Server","Window","CPU average / peak","RAM average / peak","Minimum available","Swap peak"],[[F.name,`${V.from} to ${V.to}`,`${e0(V.summary.cpuAveragePercent)} / ${e0(V.summary.cpuPeakPercent)}`,`${e0(V.summary.memoryAveragePercent)} / ${e0(V.summary.memoryPeakPercent)}`,V.summary.memoryMinimumAvailableBytes==null?"unavailable":`${(V.summary.memoryMinimumAvailableBytes/1073741824).toFixed(2)} GB`,e0(V.summary.swapPeakPercent)]])}function t4(G){let $=G.targetType.prices?.find((J)=>J.location===G.location);return{serverId:G.serverId,serverName:G.serverName,location:G.location,from:G.currentType.name,to:G.targetType.name,capacityAvailable:G.capacityAvailable,alreadyComplete:G.alreadyComplete,upgradeDisk:G.upgradeDisk,target:{cores:G.targetType.cores,memoryGb:G.targetType.memory,diskGb:G.targetType.disk,architecture:G.targetType.architecture,cpuType:G.targetType.cpu_type},price:$?{hourlyNet:$.price_hourly.net,hourlyGross:$.price_hourly.gross,monthlyNet:$.price_monthly.net,monthlyGross:$.price_monthly.gross}:void 0}}function BG(G){let $=t4(G);w(["Server","Location","Resize","Capacity","CPU / RAM / disk","Monthly"],[[`${$.serverName} (${$.serverId})`,$.location,`${$.from} -> ${$.to}`,$.capacityAvailable?"available":"waiting",`${$.target.cores??0} / ${$.target.memoryGb??0} GB / ${$.target.diskGb??0} GB`,$.price?.monthlyGross??$.price?.monthlyNet??"not reported"]])}async function VG(G,$,J){let Y=await x();if(Y.cloud?.provider!=="hetzner")throw Error("server:resize currently requires the Hetzner provider.");let X=J.env??process.env.CLOUD_ENV??"production",Q=i$(Y,X),Z=await I1(Q),z=b1(Y),F=new o0({apiToken:$1(z.apiToken,Y)}),M=Z&&(G===Z.serverName||G===String(Z.serverId)||G===Z.stackName)?await F.getServer(Z.serverId):(await F.listServers()).find((_)=>_.name===G||String(_.id)===G);if(!M)throw Error(`Hetzner server ${G} was not found.`);if(Z?.serverId&&Z.serverId!==M.id)throw Error(`Refusing to resize ${G}: project state is pinned to server ${Z.serverId}.`);let H=l4($),j=J.upgradeDisk!==!1,V=await g6(F,M.id,H,j);if(J.json)console.log(JSON.stringify({schemaVersion:1,plan:t4(V)},null,2));else BG(V);if(!J.apply){if(!J.json)W(`Preview only. Re-run with --apply --confirm ${M.name} to execute.`);return}if(J.confirm!==M.name)throw Error(`Resize requires --confirm ${M.name}.`);let T=Z?.publicIp??M.public_net.ipv4?.ip;if(!T)throw Error(`Server ${M.name} has no public IPv4 address for verification.`);let A={host:T,user:Z?.sshUser??z.sshUser,identityFile:z.sshPrivateKeyPath,connectTimeoutSec:15,sshTimeoutMs:600000,routeTimeoutMs:20000},q=await n4(Q),O=new Date().toISOString(),L={schemaVersion:1,operationId:q?.targetType===H&&q.serverId===M.id?q.operationId:crypto.randomUUID(),stackName:Q,serverId:M.id,serverName:M.name,sourceType:V.currentType.name,targetType:H,upgradeDisk:j,phase:"planning",status:"running",attempts:q?.targetType===H?q.attempts:0,startedAt:q?.targetType===H?q.startedAt:O,updatedAt:O,manifest:q?.targetType===H?q.manifest:void 0};await j0(L);let D=m6(J.pollSeconds,900)*1000,P=m6(J.maxWaitMinutes,0)*60000,b=Date.now();for(;;){let _=await g6(F,M.id,H,j);if(!_.capacityAvailable&&!_.alreadyComplete){if(L={...L,phase:"waiting-capacity",status:"waiting-capacity",updatedAt:new Date().toISOString()},await j0(L),!J.wait){if(!J.json)W(`${H} has no capacity in ${_.location}. The server remains running and unchanged. Use --wait to poll safely.`);return}if(P>0&&Date.now()-b>=P)throw Error(`Timed out waiting for ${H} capacity in ${_.location}.`);if(!J.json)W(`Waiting ${Math.round(D/1000)} seconds for ${H} capacity...`);await Bun.sleep(D);continue}let S=await s4(Q);try{let I=await(async()=>{try{return L={...L,status:"running",attempts:L.attempts+1,updatedAt:new Date().toISOString()},await j0(L),await r4({client:F,serverId:M.id,targetType:H,upgradeDisk:j,resumeFromOff:L.phase==="shutting-down"||L.phase==="resizing"||L.phase==="powering-on"||L.phase==="verifying",manifest:L.manifest,hooks:{preflight:async()=>{let g=await a4(A);return L={...L,manifest:g,updatedAt:new Date().toISOString()},await j0(L),g},afterBoot:async(g,f,d,r)=>p4(A,f,d,r),onPhase:async(g,f)=>{if(L={...L,phase:g,status:g==="waiting-capacity"?"waiting-capacity":g==="complete"?"completed":"running",updatedAt:new Date().toISOString()},await j0(L),!J.json)W(`${g}${f?` ${JSON.stringify(f)}`:""}`)}}})}finally{await S()}})();if(L={...L,status:I.status==="completed"||I.status==="already-complete"?"completed":I.status==="recovered"?"recovered":"waiting-capacity",lastError:I.status==="recovered"?I.error:void 0,updatedAt:new Date().toISOString()},await j0(L),J.json)console.log(JSON.stringify({schemaVersion:1,result:I},null,2));else if(I.status==="completed"||I.status==="already-complete")B(`${M.name} is on ${H} and passed full post-boot verification.`);else if(I.status==="recovered"&&I.retryable&&J.wait){W(`Capacity race recovered safely. Waiting ${Math.round(D/1000)} seconds before retry.`),await Bun.sleep(D);continue}else if(I.status==="recovered")throw Error(`Resize did not complete and the original server was recovered: ${I.error}`);return}catch(I){throw L={...L,status:"failed",lastError:I instanceof Error?I.message:String(I),updatedAt:new Date().toISOString()},await j0(L),I}}}async function AG(G,$){let J=await x();if(J.cloud?.provider!=="hetzner")throw Error("server:optimize currently requires the Hetzner provider.");let Y=$.env??process.env.CLOUD_ENV??"production",X=i$(J,Y),Q=await I1(X);if(!Q?.serverId)throw Error(`No pinned Hetzner server was found for ${X}.`);if(![Q.serverName,String(Q.serverId),Q.stackName].includes(G))throw Error(`Refusing to optimize ${G}: project state is pinned to ${Q.serverName}.`);let Z=b1(J),z=new o0({apiToken:$1(Z.apiToken,J)}),F=await z.getServer(Q.serverId),U=Q.publicIp??F.public_net.ipv4?.ip;if(!U)throw Error(`Server ${F.name} has no public IPv4 address.`);let M=m4(J);if($.json)console.log(JSON.stringify({schemaVersion:1,server:F.name,plan:M},null,2));else w(["Server","Firewall ports","Monitoring","Auto updates","Swap","SSH passwords"],[[F.name,M.firewallPorts.join(","),M.monitoring?"on":"off",M.autoUpdates?"on":"off",`${M.swapGb} GB`,"disabled"]]);if(!$.apply){if(!$.json)W(`Preview only. Re-run with --apply --confirm ${F.name} to execute.`);return}if($.confirm!==F.name)throw Error(`Optimization requires --confirm ${F.name}.`);let H={host:U,user:Q.sshUser??Z.sshUser,identityFile:Z.sshPrivateKeyPath,connectTimeoutSec:15},j=await v6(H),V=j.routeProbes.filter((d)=>!d.ok),T=J.infrastructure?.compute,A=f6(T)?new Set(["nginx.service","bun-gateway.service","ts-cloud-nginx.service"]):new Set,q=j.failedUnits.filter((d)=>!A.has(d));if(q.length>0)throw Error(`Preflight found failed services: ${q.join(", ")}`);if(V.length>0)throw Error(`Preflight found unhealthy routes: ${V.map((d)=>d.domain).join(", ")}`);let O=f6(T)?[]:Object.values(J.sites??{}).flatMap((d)=>typeof d?.port==="number"&&![80,443].includes(d.port)?[d.port]:[]),L=g4({allowSsh:T?.allowSsh!==!1,sitePorts:O,allowedPorts:T?.firewall?.allowedPorts}),D=(Q.firewallId?(await z.listFirewalls()).find((d)=>d.id===Q.firewallId):void 0)??(await z.listFirewalls()).find((d)=>d.name===`${J.project.slug}-${Y}-app-fw`);if(!D)throw Error(`Managed firewall for ${F.name} was not found.`);let P=await z.setFirewallRules(D.id,L);await Promise.all(P.map((d)=>z.waitForAction(d.id))),await d4(J,H);let[b,_]=await Promise.all([v6(H),c4(H)]),S=u4(M,_),I=v4(j,b),g=b.routeProbes.filter((d)=>!d.ok);if(I.stoppedServices.length>0)S.push(`services stopped: ${I.stoppedServices.join(", ")}`);if(I.changedRouteFragments.length>0)S.push(`route fragments changed: ${I.changedRouteFragments.join(", ")}`);if(I.missingRouteIds.length>0)S.push(`routes disappeared: ${I.missingRouteIds.join(", ")}`);if(I.changedReleaseLinks.length>0)S.push(`release links changed: ${I.changedReleaseLinks.join(", ")}`);if(I.missingData.length>0)S.push(`databases or volumes disappeared: ${I.missingData.join(", ")}`);if(g.length>0)S.push(`unhealthy routes: ${g.map((d)=>d.domain).join(", ")}`);if(S.length>0)throw Error(`Host optimization verification failed: ${S.join("; ")}`);let f={serverId:F.id,serverName:F.name,services:b.runningServices.length,routes:b.routeIds.length,healthyDomains:b.routeProbes.length,releaseLinks:b.releaseLinks.length,databasesAndVolumes:b.dataCatalog.length,report:_};if($.json)console.log(JSON.stringify({schemaVersion:1,result:f},null,2));else B(`${F.name} passed full host, rpx route, service, release, and data verification.`)}function d6(G){G.command("capabilities [server]","Show provider and target operation support").option("--json","Print structured JSON").action(async($,J)=>{try{await s$(async(Y)=>{let X=$?[T0(Y,$)]:Y.store.list(Y.controlPlane.project.id),Q=X.flatMap((Z)=>Object.entries(Z.capabilities).map(([z,F])=>({server:Z.name,provider:Z.provider,action:z,...F})));if(J.json)console.log(JSON.stringify({schemaVersion:1,targets:Q},null,2));else w(["Server","Provider","Action","Support","Explanation"],Q.map((Z)=>[Z.server,Z.provider,Z.action,Z.supported?"supported":"unsupported",Z.reason??"—"]))})}catch(Y){A$(Y)}}),G.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($,J,Y)=>{try{await VG($,J,Y)}catch(X){A$(X)}}),G.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($,J)=>{try{await AG($,J)}catch(Y){A$(Y)}}),G.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($,J)=>{try{await jG($,J)}catch(Y){A$(Y)}}),G.command("server:list","List fleet servers").option("--json","Print structured JSON").action(async($)=>{try{await s$(async(J)=>{let Y=J.store.list(J.controlPlane.project.id);if($.json)console.log(JSON.stringify(Y,null,2));else w(["Name","Provider ID","Provider / region","Status / trust","Roles","CPU / memory","Heartbeat"],Y.map((X)=>[X.name,X.providerId??"—",`${X.provider} / ${X.region??"external"}`,`${X.status} / ${X.trustState}`,X.roles.join(","),`${X.capacity.cpu??0} / ${X.capacity.memoryBytes??0}`,X.heartbeatAt??"never"]))})}catch(J){A$(J)}}),G.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($,J)=>{try{await s$(async(Y)=>{let X=J.provider;if(!["aws","hetzner","ssh"].includes(X)||!J.endpoint)throw Error("A supported --provider and --endpoint are required; provisioning and enrollment are separate operations.");let Q=Y.service.enroll({organizationId:Y.controlPlane.organization.id,projectId:Y.controlPlane.project.id,name:$,provider:X,providerId:J.providerId,endpoint:J.endpoint,sshUser:J.user??"deploy",credentialRef:J.credentialRef??"secret://fleet/agent",region:J.region,roles:String(J.roles??"application").split(","),labels:Object.fromEntries(String(J.labels??"").split(",").map((Z)=>Z.split("=")).filter((Z)=>Z[0]&&Z[1]))});B(`Enrolled ${Q.name} as ${Q.id}; no remote mutation was performed.`)})}catch(Y){A$(Y)}}),G.command("server:validate <name>","Pin trust and validate a server").option("--accept-host-key <fingerprint>","Accept reviewed rotation").option("--json","Print JSON").action(async($,J)=>{try{await s$(async(Y)=>{let X=T0(Y,$);if(X=await Y.service.test(X.id),X.trustState==="rotation_pending"){if(!J.acceptHostKey)throw Error(`Host key changed to ${X.pendingHostKey}; review it first.`);X=Y.service.reviewHostKey(X.id,J.acceptHostKey)}let Q=await Y.service.validate(X.id);if(J.json)console.log(JSON.stringify(Q.validation,null,2));else w(["Severity","Code","Finding","Remediation"],(Q.validation?.findings??[]).map((Z)=>[Z.severity,Z.code,Z.message,Z.remediation??"—"]));if(!Q.validation?.valid)process.exitCode=1})}catch(Y){A$(Y)}}),G.command("server:bootstrap <name>","Preview or queue bootstrap").option("--apply","Apply reviewed plan").action(async($,J)=>{try{await s$(async(Y)=>{let X=Y.service.bootstrap(T0(Y,$).id,!!J.apply);if(X.preview)w(["Step"],X.steps.map((Q)=>[Q]));else B(`Bootstrap queued: ${X.operation?.id}`)})}catch(Y){A$(Y)}}),G.command("server:drain <name>","Drain without terminating").option("--complete","Mark movement complete").action(async($,J)=>{try{await s$(async(Y)=>B(Y.service.drain(T0(Y,$).id,!!J.complete).status))}catch(Y){A$(Y)}}),G.command("server:uncordon <name>","Return a server to scheduling").action(async($)=>{try{await s$(async(J)=>{J.service.uncordon(T0(J,$).id),B(`Uncordoned ${$}.`)})}catch(J){A$(J)}}),G.command("server:archive <name>","Archive inventory without termination").option("--confirm <name>","Exact name").action(async($,J)=>{try{await s$(async(Y)=>{Y.service.archive(T0(Y,$).id,J.confirm??""),B(`Archived ${$}; provider infrastructure was not terminated.`)})}catch(Y){A$(Y)}}),G.command("server:ssh <name>","Open strict SSH using the enrolled endpoint").action(async($)=>{try{await s$(async(J)=>{let Y=T0(J,$);if(Y.trustState!=="pinned")throw Error("Validate and pin the server host key first.");let X=Bun.spawn(["ssh","-p",String(Y.sshPort),"-o","StrictHostKeyChecking=yes",`${Y.sshUser}@${Y.endpoint}`],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}),Q=await X.exited;if(Q)process.exitCode=Q})}catch(J){A$(J)}});for(let[$,J]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"]])G.command($,J).action(async(...Y)=>V$($.split(" ")[0],{target:String(Y[0]??""),nextAction:"Use the dashboard capability view or the corresponding runtime, release, firewall, backup, or maintenance service."}))}async function qG(){let G=await x(),$=u(process.cwd(),G),J=new o4($.store),Y=new $9(J,new O0($.store));return{controlPlane:$,store:J,service:Y}}async function J1(G){let $=await qG();try{await G($)}catch(J){K(J instanceof Error?J.message:String(J)),process.exitCode=1}finally{$.controlPlane.store.close()}}var k1=(G)=>Object.fromEntries(String(G??"").split(",").map(($)=>$.split("=")).filter(($)=>$[0]&&$[1])),e4=(G)=>Object.fromEntries(Object.entries(k1(G)).map(([$,J])=>[$,Number(J)]));function c6(G){G.command("pool:list","List capacity pools and reservations").option("--json","Print structured JSON").action(async($)=>J1((J)=>{let Y=J.store.listPools(J.controlPlane.project.id);if($.json)console.log(JSON.stringify(Y,null,2));else w(["Name","Purpose","Backend","Region / arch","Status","Capacity","Concurrency"],Y.map((X)=>[X.name,X.purpose,X.backend,`${X.region??"any"} / ${X.architecture??"any"}`,X.status,JSON.stringify(X.capacity),String(X.concurrency)]))})),G.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($,J)=>J1((Y)=>{let{purpose:X,backend:Q}=J;if(!["application","build","worker","monitoring","backup"].includes(X)||!["server","ecs","asg"].includes(Q))throw Error("Unsupported pool purpose or backend.");let Z=Y.store.createPool({organizationId:Y.controlPlane.organization.id,projectId:Y.controlPlane.project.id,name:$,purpose:X,backend:Q,region:J.region,architecture:J.arch,labels:k1(J.labels),requiredServerLabels:k1(J.selector),toleratedTaints:[],capacity:{cpu:0,memoryBytes:0,diskBytes:0,gpu:0,...e4(J.capacity)},maxWorkloads:Number(J.max),costWeight:1,concurrency:Number(J.concurrency),ephemeralWorkspaces:!0,allowProductionSecrets:!1,status:"active"});B(`Created ${Z.name} (${Z.id}).`)})),G.command("pool:add-server <pool> <server>","Attach an enrolled server to a pool").action(async($,J)=>J1((Y)=>{let X=Y.store.listPools(Y.controlPlane.project.id).find((Z)=>Z.id===$||Z.name===$),Q=new O0(Y.controlPlane.store).list(Y.controlPlane.project.id).find((Z)=>Z.id===J||Z.name===J);if(!X||!Q)throw Error("Pool or server was not found.");Y.store.addMember(X.id,Q.id),B(`Attached ${Q.name} to ${X.name}.`)})),G.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($)=>J1((J)=>{let Y=J.service.explain(J.controlPlane.project.id,{purpose:$.purpose,resources:e4($.resources),region:$.region,architecture:$.arch,labels:k1($.labels)});if($.json)console.log(JSON.stringify(Y,null,2));else w(["Pool","Server","Eligible","Available","Fit / spread / cost","Reasons"],Y.map((X)=>[X.poolName,X.serverId??"provider-managed",X.eligible?"yes":"no",JSON.stringify(X.available),`${X.score.fit.toFixed(2)} / ${X.score.spread} / ${X.score.cost}`,X.reasons.join("; ")||"eligible"]))})),G.command("pool:drain <pool>","Move stateless placements and block stateful placements").action(async($)=>J1((J)=>{let Y=J.store.listPools(J.controlPlane.project.id).find((Q)=>Q.id===$||Q.name===$);if(!Y)throw Error("Pool was not found.");let X=J.service.drainPool(Y.id);B(`Draining ${Y.name}: moved ${X.moved.length}, blocked ${X.blocked.length} stateful placement(s).`)}))}async function RG(){let G=await x(),$=u(process.cwd(),G),J=new J9($.store),Y=new Y9(J);return{controlPlane:$,store:J,service:Y}}async function w0(G){let $=await RG();try{await G($)}catch(J){K(J instanceof Error?J.message:String(J)),process.exitCode=1}finally{$.controlPlane.store.close()}}var Y1=(G,$)=>{let J=G.store.list(G.controlPlane.project.id).find((Y)=>Y.id===$||Y.name===$);if(!J)throw Error("Regional topology was not found.");return J},LG=(G)=>G.split(",").map(($,J)=>{let[Y,X="aws"]=$.split(":");return{region:String(Y),provider:X,role:J===0?"primary":"secondary"}}),EG=(G)=>{let $=new Set(["s3","dynamodb","secrets"]),J=G.split(",").map((X)=>X.trim()),Y=J.find((X)=>!$.has(X));if(Y)throw Error(`Unsupported replication kind: ${Y}`);return J};function u6(G){G.command("region:list","List regional topology, traffic, and replication health").option("--json","Print structured JSON").action(async($)=>w0((J)=>{let Y=J.store.list(J.controlPlane.project.id).map((X)=>({topology:X,targets:J.store.targets(X.id),channels:J.store.channels(X.id),route:J.store.route(X.id),executions:J.store.executions(X.id)}));if($.json)console.log(JSON.stringify(Y,null,2));else w(["Name","Hostname","Active","Status","Revision","Replication"],Y.map((X)=>[X.topology.name,X.topology.hostname,X.topology.activeRegion,X.topology.status,X.topology.revision??"—",`${X.channels.filter((Q)=>Q.status==="in_sync").length}/${X.channels.length} in sync`]))})),G.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($,J)=>w0((Y)=>{if(!J.hostname||!J.regions)throw Error("--hostname and --regions are required.");let X=Y.service.create({organizationId:Y.controlPlane.organization.id,projectId:Y.controlPlane.project.id,name:$,hostname:J.hostname,regions:LG(J.regions),trafficPolicy:"active_passive",dataPolicy:{replicate:EG(String(J.replicate)),maxLagSeconds:Number(J.maxLag),retainOnDestroy:!0},dnsProvider:"route53",cdnEnabled:!!J.cdn,wafEnabled:!!J.waf});B(`Created ${X.name} (${X.id}); run region:rollout with an immutable manifest.`)})),G.command("region:plan <name> <operation>","Preview exact regional execution steps").option("--target <region>","Failover or failback region").action(async($,J,Y)=>w0((X)=>w(["Step"],X.service.plan(Y1(X,$).id,J,Y.target).map((Q)=>[Q])))),G.command("region:rollout <name> <revision>","Queue secondary-first regional stack rollout").option("--manifest <file>","Regional stack manifest JSON").action(async($,J,Y)=>w0(async(X)=>{let Q=Y.manifest?await Bun.file(Y.manifest).json():{},Z=X.service.enqueue({topologyId:Y1(X,$).id,kind:"rollout",revision:J,manifest:Q});B(`Regional rollout queued: ${Z.operation.id}`)})),G.command("region:failover <name> <region>","Queue health and RPO-gated failover").action(async($,J)=>w0((Y)=>{let X=Y.service.enqueue({topologyId:Y1(Y,$).id,kind:"failover",targetRegion:J});B(`Failover queued: ${X.operation.id}`)})),G.command("region:failback <name> <region>","Queue health and RPO-gated failback").action(async($,J)=>w0((Y)=>{let X=Y.service.enqueue({topologyId:Y1(Y,$).id,kind:"failback",targetRegion:J});B(`Failback queued: ${X.operation.id}`)})),G.command("region:destroy <name>","Drain traffic and delete regional stacks while retaining data").option("--confirm <name>","Exact topology name").action(async($,J)=>w0((Y)=>{let X=Y.service.enqueue({topologyId:Y1(Y,$).id,kind:"destroy",confirmation:J.confirm});B(`Regional teardown queued: ${X.operation.id}; replicated data remains retained.`)}))}var G1="0.9.4";async function OG(){let G=await x(),$=u(process.cwd(),G),J=new G9($.store),Y=new X9(J),X=["releases","artifacts","operations","logs","previews","snapshots","orphaned_resources"].map((Q)=>new Q9(Q,$.store,$.project.id,$.organization.id));return{controlPlane:$,store:J,service:Y,cleanup:X}}async function t$(G){let $=await OG();try{await G($)}catch(J){K(J instanceof Error?J.message:String(J)),process.exitCode=1}finally{$.controlPlane.store.close()}}function l6(G){G.command("maintenance:list","List updates, campaigns, cleanup plans, and DR drills").option("--json","Print structured JSON").action(async($)=>t$((J)=>{let Y={manifests:J.store.manifests(J.controlPlane.organization.id),windows:J.store.windows(J.controlPlane.project.id),campaigns:J.store.campaigns(J.controlPlane.project.id),cleanups:J.store.cleanups(J.controlPlane.project.id),drills:J.store.drills(J.controlPlane.project.id)},X=[...Y.manifests.map((Q)=>["update",Q.version,Q.compatibility.compatible?"compatible":"blocked",Q.compatibility.reasons.join("; ")||Q.channel]),...Y.campaigns.map((Q)=>["upgrade",Q.id,Q.status,Q.currentStage??Q.fromVersion]),...Y.cleanups.map((Q)=>["cleanup",Q.kind,Q.status,`${Q.candidates.length} items / ${Q.estimatedBytes} bytes`]),...Y.drills.map((Q)=>["DR drill",Q.scenario,Q.status,Q.isolatedTarget])];if($.json)console.log(JSON.stringify(Y,null,2));else w(["Area","Name / version","Status","Detail"],X)})),G.command("maintenance:key:add <name> <pem>","Trust an Ed25519 update signing key").action(async($,J)=>t$(async(Y)=>{let X=Y.store.addTrustedKey({organizationId:Y.controlPlane.organization.id,name:$,publicKeyPem:await Bun.file(J).text()});B(`Trusted ${X.name}: ${X.fingerprint}`)})),G.command("maintenance:manifest <document> <signature> <key>","Verify and import a signed update manifest").action(async($,J,Y)=>t$(async(X)=>{let Q=await Bun.file($).json(),Z=(await Bun.file(J).text()).trim(),z=X.store.importManifest({organizationId:X.controlPlane.organization.id,keyId:Y,document:Q,signature:Z,current:{schemaVersion:X.controlPlane.store.health().schemaVersion,currentVersion:G1,platform:process.platform,architecture:process.arch}});B(`Verified ${z.version}; ${z.compatibility.compatible?"compatible":"blocked: "+z.compatibility.reasons.join("; ")}`)})),G.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($,J,Y)=>t$((X)=>{let Q=X.store.createWindow({projectId:X.controlPlane.project.id,name:$,schedule:J,timezone:Y.timezone,durationMinutes:Number(Y.minutes),allowedOperations:["platform.upgrade"],requireApproval:!0});B(`Created ${Q.name} (${Q.id}).`)})),G.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($,J,Y)=>t$((X)=>{let Q=new O0(X.controlPlane.store).list(X.controlPlane.project.id).filter((F)=>F.status==="ready").map((F)=>({serverId:F.id,target:F.name,previousVersion:F.bootstrapVersion??G1}));if(!Q.length)Q.push({target:"control-plane",previousVersion:G1});let Z=X.store.createCampaign({projectId:X.controlPlane.project.id,manifestId:$,fromVersion:G1,strategy:Y.strategy==="rolling"?"rolling":"canary",batchSize:Number(Y.batch),healthGate:{kind:"platform"},backupId:J,targets:Q}),z=X.service.enqueueCampaign(Z.id);B(`Staged upgrade queued: ${z.operation.id}`)})),G.command("cleanup:preview <kind>","Discover a bounded cleanup candidate set without deleting").option("--older-than <days>","Minimum age",{default:"30"}).action(async($,J)=>t$(async(Y)=>{let X=await Y.service.previewCleanup({projectId:Y.controlPlane.project.id,kind:$,criteria:{olderThanDays:Number(J.olderThan)}},Y.cleanup);w(["ID","Candidate","Bytes"],X.candidates.map((Q)=>[Q.id,Q.label,String(Q.bytes)])),W(`Apply with: cloud cleanup:apply ${X.id} --confirm "DELETE ${X.candidates.length} ${X.kind}"`)})),G.command("cleanup:apply <plan>","Apply an unchanged cleanup preview").option("--confirm <text>","Exact preview confirmation").action(async($,J)=>t$((Y)=>{let X=Y.service.enqueueCleanup($,String(J.confirm??""));B(`Cleanup queued: ${X.operation.id}`)})),G.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($,J,Y)=>t$((X)=>{let Q=X.store.createDrill({projectId:X.controlPlane.project.id,scenario:$,isolatedTarget:J,expectedRpoMinutes:Number(Y.rpo),expectedRtoMinutes:Number(Y.rto),recoveryPointId:Y.recoveryPoint,topologyId:Y.topology});B(`Planned isolated drill ${Q.id}.`)})),G.command("dr:run <drill>","Queue an isolated disaster-recovery drill").action(async($)=>t$((J)=>{let Y=J.service.enqueueDrill($);B(`DR drill queued: ${Y.operation.id}`)}))}async function r6(){try{return(await x()).project.region||process.env.AWS_REGION||"us-east-1"}catch{return process.env.AWS_REGION||"us-east-1"}}function a6(G){G.command("function:list","List all Lambda functions").option("--region <region>","AWS region").action(async($)=>{R("Lambda Functions");let J=$?.region||await r6(),Y=new e$(J);try{let{Functions:X=[]}=await Y.listFunctions({MaxItems:50});if(!X.length){W("No functions found");return}w(["Name","Runtime","Memory","Timeout","Last Modified"],X.map((Q)=>[Q.FunctionName??"-",Q.Runtime??"-",Q.MemorySize?`${Q.MemorySize} MB`:"-",Q.Timeout?`${Q.Timeout}s`:"-",Q.LastModified??"-"]))}catch(X){K(`Failed to list functions: ${X.message}`),process.exitCode=1}}),G.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($,J)=>{R(`Invoking ${$}`);let Y=J?.region||await r6(),X=new e$(Y),Q=new E("Invoking function...");Q.start();try{let Z=await X.invoke({FunctionName:$,InvocationType:J?.async?"Event":"RequestResponse",Payload:J?.payload||"{}",LogType:J?.async?"None":"Tail"});if(Z.FunctionError){if(Q.fail(`Function returned an error (${Z.FunctionError})`),Z.Payload)W(Z.Payload);process.exitCode=1;return}if(Q.succeed(`Invoked (status ${Z.StatusCode})`),Z.Payload)W(`
306
- Response:`),W(Z.Payload);if(Z.LogResult)W(`
307
- Logs:`),W(Buffer.from(Z.LogResult,"base64").toString("utf-8"))}catch(Z){Q.fail(`Invocation failed: ${Z.message}`),process.exitCode=1}}),G.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($,J)=>{R(`Logs for ${$}`);let Y=J?.region||await r6(),X=new o$(Y),Q=$.startsWith("/aws/lambda/")?$:`/aws/lambda/${$}`,Z=Number(J?.since??15),z=(F)=>{for(let U of F){let M=U.timestamp?new Date(U.timestamp).toISOString():"";W(`${M} ${(U.message??"").trimEnd()}`)}};try{let F=Date.now()-Z*60000,{events:U=[]}=await X.filterLogEvents({logGroupName:Q,startTime:F,filterPattern:J?.filter,limit:200});if(z(U),U.length)F=(U[U.length-1].timestamp??F)+1;if(J?.tail){W(`
308
- (streaming — Ctrl+C to stop)`);for(;;){await new Promise((H)=>setTimeout(H,3000));let{events:M=[]}=await X.filterLogEvents({logGroupName:Q,startTime:F,filterPattern:J?.filter,limit:200});if(M.length)z(M),F=(M[M.length-1].timestamp??F)+1}}}catch(F){K(`Failed to read logs: ${F.message}`),process.exitCode=1}})}async function Z9(G){let $=Bun.spawn(["docker",...G],{stdin:"inherit",stdout:"inherit",stderr:"pipe"}),[J,Y]=await Promise.all([$.exited,new Response($.stderr).text()]);return{ok:J===0,exitCode:J,command:["docker",...G].join(" "),stderr:Y.trim()}}function p6(G){G.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($)=>{let J=["build","--file",$.file??"Dockerfile","--tag",$.tag??"latest","."];if($.dryRun){console.log(`docker ${J.join(" ")}`);return}let Y=await Z9(J);if($.json)console.log(JSON.stringify(Y));if(!Y.ok)K(Y.stderr||`docker exited ${Y.exitCode}`),process.exitCode=Y.exitCode;else B(`Built ${$.tag??"latest"}.`)}),G.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($)=>{if(!$.image){K("--image with a full registry reference is required; credentials must come from docker credential helpers."),process.exitCode=2;return}if($.dryRun){console.log(`docker push ${$.image}`);return}let J=await Z9(["push",$.image]);if($.json)console.log(JSON.stringify(J));if(!J.ok)K(J.stderr),process.exitCode=J.exitCode;else B(`Pushed ${$.image}.`)}),G.command("container:deploy","Deploy a container image").action(async()=>V$("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 s6(G){G.command("domain:list","List all domains").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async($)=>{R("Domains");try{let J=C$($?.provider),Y=U$($?.provider)?.provider||"unknown",X=new E(`Fetching domains from ${Y}...`);X.start();let Q=await J.listDomains();if(X.succeed(`Found ${Q.length} domain(s)`),Q.length===0){W("No domains found in this provider");return}let Z=Q.map((z)=>[z,"Active","-",Y.charAt(0).toUpperCase()+Y.slice(1)]);w(["Domain","Status","SSL","Provider"],Z)}catch(J){K(`Failed to list domains: ${J instanceof Error?J.message:String(J)}`)}}),G.command("domain:add <domain>","Add a new domain").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async($,J)=>{R(`Adding Domain: ${$}`);try{let Y=C$(J?.provider),X=U$(J?.provider)?.provider||"unknown",Q=new E(`Checking if ${X} can manage ${$}...`);if(Q.start(),await Y.canManageDomain($))Q.succeed(`Domain ${$} is already available in ${X}`),W(`
309
- The domain is ready to use. You can now:`),W(` - Add DNS records: cloud dns:add ${$} A 192.168.1.1`),W(` - Generate SSL: cloud domain:ssl ${$}`);else if(Q.warn(`Domain ${$} is not available in ${X}`),W(`
310
- To add this domain:`),X==="route53")W(" - Create a hosted zone in Route53 for this domain"),W(" - Update nameservers at your registrar to point to Route53");else W(` - Ensure the domain is registered with ${X}`),W(" - Enable API access for the domain in your provider dashboard")}catch(Y){K(`Failed to check domain: ${Y instanceof Error?Y.message:String(Y)}`)}}),G.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($,J)=>{R(`Generating SSL Certificate for ${$}`);try{let Y=U$(J?.provider);if(!Y)throw Error("No DNS provider configured");let X=J?.region||"us-east-1",Q=Y.provider;W(`DNS Provider: ${Q}`),W(`ACM Region: ${X}`);let Z=new n6(Y,X),z=new E("Requesting certificate and creating validation records...");z.start();let F=await Z.findOrCreateCertificate({domainName:$,subjectAlternativeNames:[`*.${$}`],waitForValidation:J?.wait??!0,maxWaitMinutes:10});if(F.isNew)z.succeed("Certificate requested and validation records created");else z.succeed("Found existing valid certificate");if(W(`Certificate ARN: ${F.certificateArn}`),W(`Status: ${F.status}`),F.status==="issued")B(`
311
- SSL Certificate is ready!`),W(`
312
- You can now use this certificate with:`),W(" - CloudFront distributions"),W(" - Application Load Balancers"),W(" - API Gateway custom domains");else if(F.status==="pending")W(`
313
- DNS validation records have been created.`),W("Certificate validation may take a few more minutes."),W(`
314
- Check status with: cloud domain:verify ${$}`);else K(`
315
- Certificate validation failed. Check ACM console for details.`)}catch(Y){K(`Failed to generate SSL: ${Y instanceof Error?Y.message:String(Y)}`)}}),G.command("domain:verify <domain>","Verify domain ownership and SSL status").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async($,J)=>{R(`Verifying Domain: ${$}`);try{let Y=C$(J?.provider),X=U$(J?.provider)?.provider||"unknown",Q=new E("Checking domain ownership...");if(Q.start(),!await Y.canManageDomain($)){Q.fail("Domain not found in provider"),K(`Domain ${$} is not available in ${X}`);return}Q.succeed("Domain ownership verified"),Q.text="Fetching DNS records...",Q.start();let F=(await Y.listRecords($)).records||[];Q.succeed(`Found ${F.length} DNS record(s)`),Q.text="Checking SSL certificate status...",Q.start();let U=new B0("us-east-1"),M="Not found",H="";try{let T=(await U.listCertificates()).CertificateSummaryList.find((A)=>A.DomainName===$||A.DomainName===`*.${$}`);if(T)H=T.CertificateArn||"",M=(await U.describeCertificate({CertificateArn:H})).Status||"Unknown"}catch{}if(Q.succeed("SSL check complete"),W(`
316
- Verification details:`),W(` - Provider: ${X}`),W(` - DNS records found: ${F.length}`),W(" - Domain managed: Yes"),W(` - SSL certificate: ${M}`),H)W(` - Certificate ARN: ${H}`);let j=new Map;for(let V of F){let T=j.get(V.type)||0;j.set(V.type,T+1)}if(j.size>0){W(`
317
- Record summary:`);for(let[V,T]of j)W(` - ${V}: ${T}`)}}catch(Y){K(`Failed to verify domain: ${Y instanceof Error?Y.message:String(Y)}`)}}),G.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($,J)=>{R(`DNS Records for ${$}`);try{let Y=C$(J?.provider),X=U$(J?.provider)?.provider||"unknown",Q=new E(`Fetching records from ${X}...`);Q.start();let z=(await Y.listRecords($)).records||[];if(Q.succeed(`Found ${z.length} record(s)`),J?.type){let U=J.type.toUpperCase();z=z.filter((M)=>M.type.toUpperCase()===U),W(`Filtered to ${z.length} ${U} record(s)`)}if(z.length===0){W("No records found");return}let F=z.map((U)=>[U.type,U.name||"@",U.content.length>50?`${U.content.substring(0,47)}...`:U.content,String(U.ttl||300)]);w(["Type","Name","Value","TTL"],F)}catch(Y){K(`Failed to list records: ${Y instanceof Error?Y.message:String(Y)}`)}}),G.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($,J,Y,X)=>{R("Adding DNS Record");try{let Q=C$(X?.provider),Z=U$(X?.provider)?.provider||"unknown",z=X?.name||"@",F=Number.parseInt(X?.ttl||"300",10),U=J.toUpperCase();W(`Provider: ${Z}`),W(`Domain: ${$}`),W(`Type: ${U}`),W(`Name: ${z}`),W(`Value: ${Y}`),W(`TTL: ${F}`);let M=new E(`Adding record via ${Z}...`);M.start();let H=await Q.createRecord($,{type:U,name:z==="@"?"":z,content:Y,ttl:F});if(!H.success){M.fail("Failed to add DNS record"),K(H.message||"DNS provider rejected the record");return}M.succeed("DNS record added successfully"),B(`
318
- Record created!`),W(`
319
- Note: DNS changes may take a few minutes to propagate`)}catch(Q){K(`Failed to add record: ${Q instanceof Error?Q.message:String(Q)}`)}}),G.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($,J,Y)=>{R("Deleting DNS Record");try{let X=C$(Y?.provider),Q=U$(Y?.provider)?.provider||"unknown",Z=Y?.name||"@",z=J.toUpperCase();W(`Provider: ${Q}`),W(`Domain: ${$}`),W(`Type: ${z}`),W(`Name: ${Z}`);let F=new E("Finding record...");F.start();let H=((await X.listRecords($)).records||[]).filter((T)=>T.type.toUpperCase()===z&&(T.name===Z||T.name===""&&Z==="@"));if(H.length===0){F.fail("No matching record found");return}if(H.length>1&&!Y?.value){F.warn("Multiple records found"),W(`
320
- Please specify --value to identify which record to delete:`);for(let T of H)W(` - ${T.content}`);return}let j=Y?.value?H.find((T)=>T.content===Y.value)||H[0]:H[0];if(W(`Value: ${j.content}`),!await k("Delete this record?",!1)){W("Deletion cancelled");return}F.text=`Deleting record via ${Q}...`,F.start(),await X.deleteRecord($,j),F.succeed("DNS record deleted successfully"),B(`
321
- Record deleted!`)}catch(X){K(`Failed to delete record: ${X instanceof Error?X.message:String(X)}`)}})}var W9=["postgres","mysql","mariadb","redis","mongodb","libsql"],z9=["aws_rds","aws_aurora","aws_elasticache","server","container","external"],F9=["observe","connect","backup","restore","restart","resize","version","rotate","expose","delete","logs","slow_queries","users","databases"];async function wG(G,$){let J=await x(),Y=u(process.cwd(),J),X=new y1(Y.store),Q=!G&&$?X.get($)??X.list(Y.project.id).find((A)=>A.name===$):void 0,Z=Q?.environmentId?Y.store.listEnvironments(Y.project.id).find((A)=>A.id===Q.environmentId)?.slug:void 0,z=G??Z??Object.keys(J.environments??{})[0]??"production",F=Y.environments.get(z);if(!F)throw Y.store.close(),Error(`Environment ${z} was not found.`);let U=new v$(Y.store,{workerId:`cli:${process.pid}`}),M=new C1(Y.store,Q$(process.cwd())),H=new j9(X,U,M),j=Y.store.getActorByExternalId("system","cli")??Y.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"}),V=J.project?.region??"us-east-1",T={aws_rds:new B9(new L9(new h1(V))),aws_aurora:new V9(new E9(new h1(V))),aws_elasticache:new A9(new T9(new m$(V))),server:new q9(new o6),container:new R9(new o6)};return{config:J,controlPlane:Y,environmentRecord:F,env:z,store:X,queue:U,secrets:M,lifecycle:H,actor:j,resolveAdapter:(A)=>T[A.provider]}}async function D$(G,$,J){let Y=await wG(G,J);try{return await $(Y)}finally{Y.controlPlane.store.close()}}async function q$(G){try{await G()}catch($){K($ instanceof Error?$.message:String($))}}function i6(G){if(!W9.includes(G))throw Error(`--engine must be ${W9.join(", ")}.`);return G}function t6(G){if(!z9.includes(G))throw Error(`--provider must be ${z9.join(", ")}.`);return G}function D0(G,$){let J=G.store.list(G.controlPlane.project.id,G.environmentRecord.id),Y=G.store.get($)??J.find((X)=>X.name===$);if(!Y||Y.environmentId!==G.environmentRecord.id)throw Error(`Data service ${$} was not found in ${G.env}.`);return Y}function U9(G){return G.map(($)=>[$.id,$.name,$.engine,$.provider,$.status,$.plan,$.publicExposure?"public allowlist":"private",$.managementEnabled?"managed":"read-only"])}function DG(G,$){return G!==null&&typeof G==="object"&&!Array.isArray(G)&&G.serviceId===$}function xG(G){let $={};if(G.plan)$.plan=G.plan;if(G.storage!=null)$.storageGb=Number(G.storage);if(G.engineVersion)$.engineVersion=G.engineVersion;if(G.retention)$.retention=G.retention;if(G.confirm)$.confirm=G.confirm;if(G.backupId)$.backupId=G.backupId;if(G.compatibilityReviewed)$.compatibilityReviewed=!0;if(G.minCapacity!=null)$.minCapacity=Number(G.minCapacity);if(G.maxCapacity!=null)$.maxCapacity=Number(G.maxCapacity);if(G.public!=null)$.publicExposure=G.public;if(G.cidrs)$.allowedCidrs=G.cidrs.split(",").map((J)=>J.trim()).filter(Boolean);return $}function M9(G){let $=typeof G.observedState.endpoint==="string"?G.observedState.endpoint:G.placement,J=Number(G.observedState.port)||(G.engine==="postgres"?5432:G.engine==="redis"?6379:G.engine==="mongodb"?27017:G.engine==="libsql"?8080:3306),Y=typeof G.observedState.database==="string"?G.observedState.database:void 0;return[{type:G.publicExposure?"external":"internal",host:$,port:J,database:Y,tls:typeof G.observedState.tls==="boolean"?G.observedState.tls:G.provider.startsWith("aws_")},{type:"tunnel",host:$,port:J,database:Y,tls:typeof G.observedState.tls==="boolean"?G.observedState.tls:G.provider.startsWith("aws_")}]}function H9(G){G.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 $J(G){G.command("data:list","List first-class databases and caches").option("--env <environment>","Target environment").option("--json","Print structured JSON").action((Y)=>q$(()=>D$(Y.env,async(X)=>{let Q=X.store.list(X.controlPlane.project.id,X.environmentRecord.id);if(Y.json)W(JSON.stringify(Q,null,2));else w(["ID","Name","Engine","Provider","Status","Plan","Exposure","Management"],U9(Q))}))),G.command("data:capabilities <engine> <provider>","Explain lifecycle support before taking action").action((Y,X)=>q$(async()=>{W(JSON.stringify(K9(i6(Y),t6(X)),null,2))}));let $=G.command("data:create <name>","Provision an encrypted, private-by-default data service");H9($),$.action((Y,X)=>q$(()=>D$(X.env,async(Q)=>{let Z=i6(X.engine),z=t6(X.provider),F=Q.resolveAdapter({provider:z});if(!F||!F.engines.includes(Z))throw Error(`No ${z}/${Z} provisioning adapter is configured. Use data:adopt for read-only inventory.`);let U={...X.database?{database:X.database}:{},...X.subnetGroup?{subnetGroup:X.subnetGroup}:{},...X.securityGroups?{securityGroupIds:X.securityGroups.split(",").map((H)=>H.trim()).filter(Boolean)}:{}},M=await Q.lifecycle.create({organizationId:Q.controlPlane.organization.id,projectId:Q.controlPlane.project.id,environmentId:Q.environmentRecord.id,name:Y,engine:Z,provider:z,placement:X.placement??Y,engineVersion:X.engineVersion,plan:X.plan??"db.t4g.micro",storageGb:Number(X.storage)||void 0,highAvailability:X.highAvailability===!0,publicExposure:X.public===!0,allowedCidrs:X.cidrs?X.cidrs.split(",").map((H)=>H.trim()).filter(Boolean):[],desiredState:U,observedState:{},origin:"managed",managementEnabled:!0,ownerActorId:Q.actor.id,username:X.username});if(B(`Queued ${M.service.name} as operation ${M.operationId}.`),M.credential)X1("One-time credential — copy it now:"),W(`Username: ${M.credential.username}`),W(`Password: ${M.credential.password}`),W(`Reference: ${M.credential.secretRef}`)})));let J=G.command("data:adopt <name>","Adopt an existing provider resource as read-only inventory");H9(J),J.action((Y,X)=>q$(()=>D$(X.env,async(Q)=>{let Z=Q.store.create({organizationId:Q.controlPlane.organization.id,projectId:Q.controlPlane.project.id,environmentId:Q.environmentRecord.id,name:Y,engine:i6(X.engine),provider:t6(X.provider),placement:X.placement??Y,engineVersion:X.engineVersion,plan:X.plan??"external",storageGb:Number(X.storage)||void 0,highAvailability:X.highAvailability===!0,publicExposure:X.public===!0,allowedCidrs:X.cidrs?X.cidrs.split(",").map((z)=>z.trim()).filter(Boolean):[],desiredState:{},observedState:{},credentialRef:void 0,status:"adopted",origin:"adopted",managementEnabled:!1,ownerActorId:Q.actor.id});B(`Adopted ${Z.name} (${Z.id}) read-only.`)}))),G.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((Y,X)=>q$(()=>D$(X.env,async(Q)=>{let Z=D0(Q,Y),z=Q.store.credential(Z.id),F={...Z,credentialRef:void 0,credential:z?{configured:!0,username:z.username,version:z.version,rotatedAt:z.rotatedAt}:void 0,dependencies:Q.store.dependencies(Z.id).map((U)=>({resourceId:U.resourceId,requiresRedeploy:U.requiresRedeploy})),operations:Q.queue.list({projectId:Z.projectId,limit:250}).filter((U)=>DG(U.operation.input,Z.id))};if(W(JSON.stringify(F,null,2)),X.reveal){if(X.confirm!==Z.name)throw Error(`Credential reveal requires --confirm ${Z.name}.`);if(!z)throw Error("No managed credential is configured.");X1("Credential value follows; it will not be logged or persisted by ts-cloud:"),W(`Username: ${z.username}`),W(`Password: ${await Q.secrets.resolve(z.secretRef)}`)}},Y))),G.command("data:connect <service>","Print secret-free connection and tunnel guidance").option("--env <environment>","Target environment").action((Y,X)=>q$(()=>D$(X.env,async(Q)=>{let Z=D0(Q,Y);w(["Type","Command","Credential reference"],e6(Z,M9(Z)).map((z)=>[z.type,z.command,z.secretRef??"—"]))},Y))),G.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((Y,X,Q)=>q$(()=>D$(Q.env,async(Z)=>{if(!F9.includes(X))throw Error(`Action must be ${F9.join(", ")}.`);let z=D0(Z,Y),F=X,U=xG(Q),M=Z.lifecycle.plan(z,F,U);if(W(JSON.stringify({...M,productionExecutionCreated:!1},null,2)),!Q.execute)return;let H=Z.lifecycle.enqueue(z,F,U,Z.actor.id);B(`Queued ${F} as ${H}.`)},Y))),G.command("data:management <service>","Enable reviewed management of an adopted service").option("--env <environment>","Target environment").option("--confirm <name>","Exact service name").action((Y,X)=>q$(()=>D$(X.env,async(Q)=>{let Z=D0(Q,Y);if(Z.origin!=="adopted"||X.confirm!==Z.name)throw Error(`Enabling adopted management requires --confirm ${Z.name}.`);let z=Q.store.update(Z.id,Z.version,{managementEnabled:!0});B(`${z.name} management is enabled.`)},Y))),G.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((Y,X,Q)=>q$(()=>D$(Q.env,async(Z)=>{let z=D0(Z,Y),F=Z.controlPlane.store.listResources(Z.controlPlane.project.id,Z.environmentRecord.id).find((U)=>U.id===X||U.slug===X);if(!F)throw Error(`Resource ${X} was not found in ${Z.env}.`);if(Q.remove){W(Z.store.removeDependency(z.id,F.id)?"Dependency removed.":"Dependency was not registered.");return}Z.store.addDependency({serviceId:z.id,resourceId:F.id,secretRef:z.credentialRef??`secret://data-services/${z.projectId}/${z.name}`,requiresRedeploy:!Q.dynamic}),B(`Registered dependency ${F.slug} -> ${z.name}.`)},Y))),G.command("db:list","Alias for data:list").option("--env <environment>","Target environment").action((Y)=>q$(()=>D$(Y.env,async(X)=>w(["ID","Name","Engine","Provider","Status","Plan","Exposure","Management"],U9(X.store.list(X.controlPlane.project.id,X.environmentRecord.id)))))),G.command("db:connect <service>","Alias for data:connect").option("--env <environment>","Target environment").action((Y,X)=>q$(()=>D$(X.env,async(Q)=>{let Z=D0(Q,Y);w(["Type","Command","Credential reference"],e6(Z,M9(Z)).map((z)=>[z.type,z.command,z.secretRef??"—"]))},Y)));for(let Y of["backup","snapshot"])G.command(`db:${Y} <service>`,`Alias for data:action ${Y==="backup"?"backup":"backup"}`).option("--env <environment>","Target environment").option("--execute","Enqueue the backup; default previews").action((X,Q)=>q$(()=>D$(Q.env,async(Z)=>{let z=D0(Z,X),F=Z.lifecycle.plan(z,"backup");if(W(JSON.stringify({...F,productionExecutionCreated:!1},null,2)),Q.execute)B(`Queued backup as ${Z.lifecycle.enqueue(z,"backup",{},Z.actor.id)}.`)},X)))}function O9(G,$=15){if(!G)return $*60000;let J=/^(\d+)\s*([smhd])?$/.exec(G.trim());if(!J)return $*60000;let Y=Number(J[1]),X=J[2]??"m";return Y*({s:1000,m:60000,h:3600000,d:86400000}[X]??60000)}function NG(G,$){for(let J of G){let Y=J.timestamp?new Date(J.timestamp).toISOString():"",X=$?`[${$}] `:"";W(`${Y} ${X}${(J.message??"").trimEnd()}`)}}function JJ(G){G.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($)=>{try{let J=await x(),Y=$?.env||"production",{region:X,functions:Q}=V0(J,Y),Z=new o$(X),z=($?.function??"all").toLowerCase();if(z!=="all"&&!["http","queue","cli"].includes(z)){K("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let U=(z==="all"?["http","queue","cli"]:[z]).map((j)=>({mode:j,group:`/aws/lambda/${Q[j]}`}));R(`Logs — ${J.project.slug} (${Y})`);let M=Date.now()-O9($?.since),H=async()=>{let j=M;for(let{mode:V,group:T}of U)try{let{events:A=[]}=await Z.filterLogEvents({logGroupName:T,startTime:M,filterPattern:$?.filter,limit:200});NG(A,U.length>1?V:void 0);for(let q of A)if((q.timestamp??0)>j)j=q.timestamp??j}catch(A){if(!/ResourceNotFound/i.test(String(A?.message)))throw A}M=j+1};if(await H(),$?.tail){W(`
322
- (streaming — Ctrl+C to stop)`);for(;;)await new Promise((j)=>setTimeout(j,3000)),await H()}}catch(J){K(`Failed to read logs: ${J.message}`),process.exitCode=1}}),G.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($)=>{try{let J=await x(),Y=$?.env||"production",{region:X,functions:Q}=V0(J,Y),Z=new f0(X),z=O9($?.since,60),F=new Date,U=new Date(F.getTime()-z),M=Math.max(60,Math.round(z/1000));R(`Metrics — ${J.project.slug} (${Y}), last ${$?.since??"1h"}`);let H=[];for(let j of["http","queue","cli"]){let V=[{Name:"FunctionName",Value:Q[j]}],T=async(b,_)=>{let S=await Z.getMetricStatistics({Namespace:"AWS/Lambda",MetricName:b,Dimensions:V,StartTime:U,EndTime:F,Period:M,Statistics:[_]});if(!S.length)return 0;if(_==="Sum")return S.reduce((I,g)=>I+(g.Sum??0),0);if(_==="Maximum")return Math.max(...S.map((I)=>I.Maximum??0));return S.reduce((I,g)=>I+(g.Average??0),0)/S.length},[A,q,O,L,D]=await Promise.all([T("Invocations","Sum"),T("Errors","Sum"),T("Throttles","Sum"),T("Duration","Average"),T("Duration","Maximum")]),P=A?`${(q/A*100).toFixed(2)}%`:"—";H.push([j,String(A),String(q),P,String(O),`${L.toFixed(0)}ms`,`${D.toFixed(0)}ms`])}w(["Function","Invocations","Errors","Error rate","Throttles","Avg","Max"],H)}catch(J){K(`Failed to read metrics: ${J.message}`),process.exitCode=1}}),G.command("metrics:dashboard","Print the CloudWatch console URL for this app").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).action(async($)=>{try{let J=await x(),Y=$?.env||"production",{region:X,functions:Q}=V0(J,Y),Z=encodeURIComponent(JSON.stringify({metrics:["http","queue","cli"].flatMap((z)=>[["AWS/Lambda","Invocations","FunctionName",Q[z]],["AWS/Lambda","Errors","FunctionName",Q[z]]]),view:"timeSeries",region:X}));R("CloudWatch dashboard"),W(`https://console.aws.amazon.com/cloudwatch/home?region=${X}#metricsV2:graph=${Z}`)}catch(J){K(J.message),process.exitCode=1}}),G.command("alarms","List CloudWatch alarms").option("--prefix <prefix>","Filter by alarm name prefix").option("--region <region>","AWS region").action(async($)=>{R("CloudWatch Alarms");let J=$?.region;if(!J)try{J=(await x()).project.region}catch{}try{let X=await new f0(J||process.env.AWS_REGION||"us-east-1").describeAlarms({AlarmNamePrefix:$?.prefix,MaxRecords:100});if(!X.length){W("No alarms found");return}w(["Name","State","Metric","Condition"],X.map((Q)=>[Q.AlarmName??"-",Q.StateValue??"-",Q.MetricName??"-",`${Q.ComparisonOperator??""} ${Q.Threshold??""}`.trim()]))}catch(Y){K(`Failed to list alarms: ${Y.message}`),process.exitCode=1}}),G.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($)=>{if(R("Creating CloudWatch Alarm"),!$?.name||$?.threshold==null){K("Missing required options: --name and --threshold"),process.exitCode=1;return}try{let J=await x(),Y=$.env||"production",{region:X,functions:Q}=V0(J,Y),Z=$.function??"http";await new f0(X).putMetricAlarm({AlarmName:$.name,Namespace:"AWS/Lambda",MetricName:$.metric??"Errors",ComparisonOperator:$.comparison??"GreaterThanThreshold",Threshold:Number($.threshold),EvaluationPeriods:1,Period:300,Statistic:$.statistic??"Sum",Dimensions:[{Name:"FunctionName",Value:Q[Z]}],AlarmDescription:`ts-cloud: ${Q[Z]} ${$.metric} ${$.comparison} ${$.threshold}`}),B(`Alarm '${$.name}' created on ${Q[Z]} ${$.metric}`)}catch(J){K(`Failed to create alarm: ${J.message}`),process.exitCode=1}})}async function f1(G){let $=await x(),J=G||"production";return{region:$.environments?.[J]?.region||$.project.region||"us-east-1",prefix:`${$.project.slug}/${J}`}}function YJ(G){G.command("secrets:list","List secrets for an environment").option("--env <environment>","Environment (production, staging, development)").action(async($)=>{R("Secrets");try{let{region:J,prefix:Y}=await f1($?.env),X=new M0(J),{SecretList:Q=[]}=await X.listSecrets({Filters:[{Key:"name",Values:[`${Y}/`]}],MaxResults:100});if(!Q.length){W(`No secrets found under ${Y}/`);return}w(["Key","Last Changed"],Q.map((Z)=>[(Z.Name??"").replace(`${Y}/`,""),Z.LastChangedDate??Z.LastAccessedDate??"-"]))}catch(J){K(`Failed to list secrets: ${J.message}`),process.exitCode=1}}),G.command("secrets:set <key> <value>","Set (create or update) a secret").option("--env <environment>","Environment (production, staging, development)").action(async($,J,Y)=>{R(`Setting secret ${$}`);let{region:X,prefix:Q}=await f1(Y?.env),Z=new M0(X),z=`${Q}/${$}`,F=new E("Storing secret...");F.start();try{try{await Z.putSecretValue({SecretId:z,SecretString:J})}catch{await Z.createSecret({Name:z,SecretString:J})}F.succeed(`Secret ${$} stored (${z})`),N("Redeploy for the new value to take effect in running functions.")}catch(U){F.fail(`Failed to store secret: ${U.message}`),process.exitCode=1}}),G.command("secrets:get <key>","Reveal a secret value").option("--env <environment>","Environment (production, staging, development)").action(async($,J)=>{try{let{region:Y,prefix:X}=await f1(J?.env),Z=await new M0(Y).getSecretValue({SecretId:`${X}/${$}`});W(Z.SecretString??"")}catch(Y){K(`Failed to read secret: ${Y.message}`),process.exitCode=1}}),G.command("secrets:delete <key>","Delete a secret").option("--env <environment>","Environment (production, staging, development)").option("--force","Delete immediately without a recovery window").action(async($,J)=>{try{let{region:Y,prefix:X}=await f1(J?.env);await new M0(Y).deleteSecret({SecretId:`${X}/${$}`,ForceDeleteWithoutRecovery:J?.force}),B(`Secret ${$} deleted`)}catch(Y){K(`Failed to delete secret: ${Y.message}`),process.exitCode=1}})}function GJ(G){for(let[$,J]of[["firewall:rules","List provider firewall policy"],["firewall:block <ip>","Block an address"],["firewall:unblock <ip>","Unblock an address"],["firewall:countries","Manage geographic policy"]])G.command($,J).action(async(...Y)=>V$($.split(" ")[0],{target:String(Y[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 XJ(G){G.command("ssl:list","List all SSL certificates").option("--region <region>","AWS region (default: us-east-1)").action(async($)=>{R("SSL Certificates");let J=$?.region||"us-east-1",Y=new E("Fetching certificates from ACM...");Y.start();try{let X=new B0(J),Q=await X.listCertificates();if(Q.CertificateSummaryList.length===0){Y.succeed("No certificates found"),W(`
323
- No SSL certificates found in region ${J}`),W("Use 'cloud domain:ssl <domain>' to request a new certificate");return}let Z=await Promise.all(Q.CertificateSummaryList.map(async(U)=>{return await X.describeCertificate({CertificateArn:U.CertificateArn})}));Y.succeed(`Found ${Z.length} certificate(s)`);let z=(U)=>{if(!U)return"N/A";let M=typeof U==="string"?Number.parseFloat(U):U,H=M<1000000000000?M*1000:M;return new Date(H).toISOString().split("T")[0]},F=Z.map((U)=>{let M=z(U.NotAfter),H=U.Type==="AMAZON_ISSUED"?"Amazon Issued":U.Type||"Unknown",j=U.Status==="ISSUED"?"Available":U.Status||"Unknown";return[U.DomainName,U.Status||"Unknown",M,H,j]});w(["Domain","Status","Expiry","Type","State"],F),W(`
324
- ACM certificates are automatically renewed by AWS`),W(`Region: ${J}`)}catch(X){Y.fail("Failed to fetch certificates"),K(X.message)}}),G.command("ssl:renew <domain>","Renew SSL certificate").option("--region <region>","AWS region (default: us-east-1)").action(async($,J)=>{R(`Checking SSL Certificate for ${$}`);let Y=J?.region||"us-east-1";W(`Domain: ${$}`),W(`Region: ${Y}`);let X=new E("Checking certificate status...");X.start();try{let Z=await new B0(Y).findCertificateByDomain($);if(!Z){X.fail("Certificate not found"),K(`No certificate found for domain: ${$}`),W("Use 'cloud domain:ssl <domain>' to request a new certificate");return}X.succeed("Certificate found"),W(`
325
- Certificate is managed by AWS Certificate Manager`),W("ACM certificates are automatically renewed 60 days before expiry"),N(`
326
- No manual renewal needed for ACM certificates`);let z=(M)=>{if(!M)return"N/A";let H=typeof M==="string"?Number.parseFloat(M):M,j=H<1000000000000?H*1000:H;return new Date(j).toISOString().split("T")[0]},F=z(Z.NotAfter),U=z(Z.IssuedAt);if(W(`
327
- Certificate details:`),W(` - Domain: ${Z.DomainName}`),W(` - Status: ${Z.Status}`),W(` - Issued: ${U}`),W(` - Expiry: ${F}`),W(` - Type: ${Z.Type||"Unknown"}`),W(` - ARN: ${Z.CertificateArn}`),W(` - Auto-renewal: ${Z.Type==="AMAZON_ISSUED"?"Enabled":"N/A (imported)"}`),Z.SubjectAlternativeNames&&Z.SubjectAlternativeNames.length>1)W(` - SANs: ${Z.SubjectAlternativeNames.join(", ")}`)}catch(Q){X.fail("Failed to check certificate"),K(Q.message)}})}function A0(G){return G.toISOString().slice(0,10)}function w9(G){return new Date(Date.UTC(G.getUTCFullYear(),G.getUTCMonth(),G.getUTCDate()))}function D9(G=new Date){let $=w9(G),J=new Date(Date.UTC($.getUTCFullYear(),$.getUTCMonth(),1)),Y=new Date($.getTime()+86400000),X=new Date(Date.UTC($.getUTCFullYear(),$.getUTCMonth()+1,1)),Q=J,Z=new Date(Date.UTC($.getUTCFullYear(),$.getUTCMonth()-1,1));return{start:A0(J),end:A0(Y),label:J.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"}),daysElapsed:$.getUTCDate(),daysInMonth:Math.round((X.getTime()-J.getTime())/86400000),previous:{start:A0(Z),end:A0(Q),label:Z.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"})}}}function g0(G,$=new Date){if(!Number.isInteger(G)||G<1||G>366)throw Error("days must be an integer from 1 to 366");let J=new Date(w9($).getTime()+86400000),Y=new Date(J.getTime()-G*86400000),X=new Date(Y.getTime()-G*86400000);return{current:{start:A0(Y),end:A0(J),label:`last ${G} days`},previous:{start:A0(X),end:A0(Y),label:`previous ${G} days`}}}function QJ(G,$,J){return $>0?G/$*J:0}function Q1(G,$){if($===0)return G===0?0:null;return(G-$)/$*100}function ZJ(G,$){let J=new Map($.map((Y)=>[Y.service,Y.amount]));return G.map((Y)=>{let X=J.get(Y.service)??0;return{...Y,previousAmount:X,changePercent:Q1(Y.amount,X)}})}var PG=/(?:data.?transfer|natgateway.*bytes|aws-(?:in|out)-bytes|regional-bytes|inter.?az|internet)/i;function g1(G){return G.filter(($)=>PG.test($.key)).sort(($,J)=>J.amount-$.amount)}function v0(G){if(G==null)return[];return Array.isArray(G)?G:[G]}function x0(G,$={}){let[,,J="unknown",Y="",X="",Q=G]=G.split(":"),Z=Q.includes("/")?"/":":",[z=J,...F]=Q.split(Z),U=F.join(Z)||Q;return{arn:G,service:J,type:z,id:U,name:$.Name||$.name||U,region:Y||void 0,accountId:X||void 0,tags:$,metadata:{}}}function SG(G,$){if(!$)return!0;let J=$.toLowerCase().replace(/^aws[:/]/,"");return[G.service,G.type,`${G.service}:${G.type}`].some((Y)=>Y.toLowerCase()===J||Y.toLowerCase().includes(J))}function _G(G,$){let J=new Map;for(let Y of G){let X=`${Y.service}:${Y.type}:${Y.id}`,Q=J.get(X);J.set(X,Q?{...Q,...Y,arn:Q.accountId?Q.arn:Y.arn,accountId:Q.accountId??Y.accountId,tags:{...Q.tags,...Y.tags},metadata:{...Q.metadata,...Y.metadata}}:Y)}return[...J.values()].filter((Y)=>SG(Y,$)).sort((Y,X)=>Y.service.localeCompare(X.service)||Y.name.localeCompare(X.name)||Y.arn.localeCompare(X.arn))}class Z1{client;profile;region;constructor(G,$=process.env.AWS_REGION||"us-east-1"){this.profile=G,this.region=$,this.client=new WJ(x9(G))}async taggedResources(){let G=[],$;do{let J=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($?{PaginationToken:$}:{})});for(let Y of J?.ResourceTagMappingList??[]){let X=Object.fromEntries((Y.Tags??[]).map((Q)=>[String(Q.Key),String(Q.Value)]));G.push(x0(String(Y.ResourceARN),X))}$=J?.PaginationToken||void 0}while($);return G}async ec2Resources(){let G=async(Q)=>this.client.request({service:"ec2",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({Action:Q,Version:"2016-11-15"}).toString()}),[$,J,Y]=await Promise.all([G("DescribeInstances"),G("DescribeVolumes"),G("DescribeAddresses")]),X=[];for(let Q of v0($?.reservationSet?.item))for(let Z of v0(Q?.instancesSet?.item)){let z=String(Z.instanceId);X.push({...x0(`arn:aws:ec2:${this.region}::instance/${z}`),state:Z.instanceState?.name,metadata:{instanceType:Z.instanceType}})}for(let Q of v0(J?.volumeSet?.item)){let Z=String(Q.volumeId);X.push({...x0(`arn:aws:ec2:${this.region}::volume/${Z}`),state:Q.status,metadata:{sizeGiB:Number(Q.size??0),attached:v0(Q.attachmentSet?.item).length>0}})}for(let Q of v0(Y?.addressesSet?.item)){let Z=String(Q.allocationId??Q.publicIp);X.push({...x0(`arn:aws:ec2:${this.region}::elastic-ip/${Z}`),state:Q.associationId?"associated":"available",metadata:{associated:Boolean(Q.associationId),publicIp:Q.publicIp}})}return X}async rdsResources(){let G=new URLSearchParams({Action:"DescribeDBInstances",Version:"2014-10-31"}).toString(),$=await this.client.request({service:"rds",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-www-form-urlencoded"},body:G}),J=$?.DescribeDBInstancesResult??$;return v0(J?.DBInstances?.DBInstance).map((Y)=>({...x0(Y.DBInstanceArn||`arn:aws:rds:${this.region}::db:${String(Y.DBInstanceIdentifier)}`),state:Y.DBInstanceStatus,metadata:{engine:Y.Engine,instanceClass:Y.DBInstanceClass}}))}async lambdaResources(){let G=[],$;do{let J=await this.client.request({service:"lambda",region:this.region,method:"GET",path:"/2015-03-31/functions",queryParams:$?{Marker:$}:void 0});for(let Y of J?.Functions??[])G.push({...x0(Y.FunctionArn),state:Y.State,metadata:{runtime:Y.Runtime,memoryMiB:Number(Y.MemorySize??0)}});$=J?.NextMarker||void 0}while($);return G}async s3Resources(){return(await new Z$("us-east-1",this.profile).listBuckets()).Buckets.map(($)=>({...x0(`arn:aws:s3:::${$.Name}`),metadata:{creationDate:$.CreationDate}}))}async discover(G){let $=[["tagging:GetResources",()=>this.taggedResources()],["ec2:Describe*",()=>this.ec2Resources()],["rds:DescribeDBInstances",()=>this.rdsResources()],["lambda:ListFunctions",()=>this.lambdaResources()],["s3:ListAllMyBuckets",()=>this.s3Resources()]],J=await Promise.allSettled($.map(([,Q])=>Q())),Y=[],X=[];return J.forEach((Q,Z)=>{if(Q.status==="fulfilled")Y.push(...Q.value);else X.push(`${$[Z][0]} unavailable: ${Q.reason instanceof Error?Q.reason.message:String(Q.reason)}`)}),{resources:_G(Y,G?.type),warnings:X}}}function W1(G,$){if(G==null)return{currentCost:null,projectedCost:null,monthlySavings:null};let J=G*$;return{currentCost:G,projectedCost:G-J,monthlySavings:J}}function N9(G,$,J,Y=!1){let X=zJ(G,$,J);if(X)return[{category:"unused",resource:G,recommendation:X.recommendation,sourceSignal:X.signal,...W1(J,1)}];if((G.service==="ec2"&&G.type==="instance"||G.service==="rds"&&G.type==="db")&&$.cpuAverage!=null&&$.cpuAverage<20)return[{category:"right-size",resource:G,recommendation:"Validate memory, network, and peak demand, then test one smaller instance class.",sourceSignal:`${G.service.toUpperCase()} 30-day average CPU ${$.cpuAverage.toFixed(1)}% (<20%)`,...W1(J,0.4)}];if(Y&&(G.service==="ec2"&&G.type==="instance"&&G.state==="running"||G.service==="rds"&&G.type==="db"&&G.state==="available")&&$.cpuAverage!=null&&$.cpuAverage>=20)return[{category:"commitment",resource:G,recommendation:"Compare a one-year no-upfront commitment against this steady workload before purchasing.",sourceSignal:`${G.service.toUpperCase()} remained active with 30-day average CPU ${$.cpuAverage.toFixed(1)}%`,...W1(J,0.3)}];if(G.service==="s3"&&$.storageBytes!=null&&$.storageBytes>=5368709120&&$.requests!=null&&$.requests<9000){let Q=$.storageBytes/1073741824;return[{category:"storage",resource:G,recommendation:"Review object age and retrieval needs, then add Intelligent-Tiering or Glacier lifecycle rules.",sourceSignal:`${Q.toFixed(1)} GiB stored with ${$.requests.toFixed(0)} requests over 90 days`,...W1(J,0.25)}]}return[]}function bG(G,$){if(G<=0||$.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: ${$.slice(0,3).join(", ")}`,...W1(G,0.2)}}function IG(G){let $=G.map((J)=>J.Average).filter((J)=>J!=null);return $.length>0?$.reduce((J,Y)=>J+Y,0)/$.length:void 0}function kG(G){let $=G.map((J)=>J.Sum).filter((J)=>J!=null);return $.length>0?$.reduce((J,Y)=>J+Y,0):void 0}function zJ(G,$,J=null){if(G.service==="ec2"&&G.type==="volume"&&G.metadata.attached===!1)return{resource:G,signal:"EBS volume is detached",recommendation:"Snapshot if needed, then delete the detached volume.",monthlySavings:J};if(G.service==="ec2"&&G.type==="elastic-ip"&&G.metadata.associated===!1)return{resource:G,signal:"Elastic IP is not associated",recommendation:"Release the unassociated Elastic IP.",monthlySavings:J};if(G.service==="ec2"&&G.type==="instance"&&$.cpuAverage!=null&&$.cpuAverage<5)return{resource:G,signal:`30-day average CPU ${$.cpuAverage.toFixed(1)}% (<5%)`,recommendation:"Stop/delete if unused, or right-size after validating memory and network demand.",monthlySavings:J};if(G.service==="rds"&&G.type==="db"&&$.connectionsAverage!=null&&$.iopsAverage!=null&&$.connectionsAverage===0&&$.iopsAverage<1)return{resource:G,signal:`14-day connections 0 and average IOPS ${$.iopsAverage.toFixed(2)} (<1)`,recommendation:"Take a final snapshot, validate ownership, then stop or delete the database.",monthlySavings:J};if(G.service==="lambda"&&G.type==="function"&&$.invocations===0)return{resource:G,signal:"30-day invocations 0",recommendation:"Confirm no event source depends on it, then remove the function.",monthlySavings:J};if(G.service==="s3"&&$.objectCount===0&&$.requests===0)return{resource:G,signal:"Bucket is empty with zero requests over 90 days",recommendation:"Confirm retention policy, then delete the empty bucket.",monthlySavings:J};if(G.service==="elasticache"&&$.connectionsAverage!=null&&$.cacheHitRate!=null&&$.connectionsAverage===0&&$.cacheHitRate<20)return{resource:G,signal:`14-day connections 0 and cache hit rate ${$.cacheHitRate.toFixed(1)}%`,recommendation:"Validate clients and snapshots, then delete the idle cache cluster.",monthlySavings:J};return null}class v1{inventory;costs;profile;region;constructor(G,$=process.env.AWS_REGION||"us-east-1"){this.profile=G,this.region=$,this.inventory=new Z1(G,$),this.costs=new N0(G)}async metric(G,$,J,Y,X,Q,Z,z=[]){let F=new Date,U=new Date(F.getTime()-Q*24*60*60*1000),M=await G.getMetricStatistics({Namespace:J,MetricName:Y,Dimensions:[{Name:X,Value:$.id},...z],StartTime:U,EndTime:F,Period:86400,Statistics:[Z]});return Z==="Sum"?kG(M):IG(M)}async signals(G){let $=new f0(G.region??this.region,this.profile);if(G.service==="ec2"&&G.type==="instance")return{cpuAverage:await this.metric($,G,"AWS/EC2","CPUUtilization","InstanceId",30,"Average")};if(G.service==="rds"&&G.type==="db"){let[J,Y,X,Q]=await Promise.all([this.metric($,G,"AWS/RDS","CPUUtilization","DBInstanceIdentifier",30,"Average"),this.metric($,G,"AWS/RDS","DatabaseConnections","DBInstanceIdentifier",14,"Average"),this.metric($,G,"AWS/RDS","ReadIOPS","DBInstanceIdentifier",14,"Average"),this.metric($,G,"AWS/RDS","WriteIOPS","DBInstanceIdentifier",14,"Average")]);return{cpuAverage:J,connectionsAverage:Y,iopsAverage:X!=null&&Q!=null?X+Q:void 0}}if(G.service==="lambda"&&G.type==="function")return{invocations:await this.metric($,G,"AWS/Lambda","Invocations","FunctionName",30,"Sum")};if(G.service==="s3"){let[J,Y,X]=await Promise.all([this.metric($,G,"AWS/S3","NumberOfObjects","BucketName",90,"Average",[{Name:"StorageType",Value:"AllStorageTypes"}]),this.metric($,G,"AWS/S3","AllRequests","BucketName",90,"Sum"),this.metric($,G,"AWS/S3","BucketSizeBytes","BucketName",90,"Average",[{Name:"StorageType",Value:"StandardStorage"}])]);return{objectCount:J,requests:Y,storageBytes:X}}if(G.service==="elasticache"&&G.type.includes("cluster")){let[J,Y]=await Promise.all([this.metric($,G,"AWS/ElastiCache","CurrConnections","CacheClusterId",14,"Average"),this.metric($,G,"AWS/ElastiCache","CacheHitRate","CacheClusterId",14,"Average")]);return{connectionsAverage:J,cacheHitRate:Y}}return{}}async resourceCosts(){try{let G=g0(30).current,$=await this.costs.getCostByDimension({start:G.start,end:G.end,dimension:"RESOURCE_ID",granularity:"DAILY"});return new Map($.map((J)=>[J.key,J.amount]))}catch{return new Map}}resourceCost(G,$){return G.get($.id)??G.get($.arn)??null}async cloudFrontTransfer(){try{let G=g0(30).current,$=await this.costs.getCostByDimension({start:G.start,end:G.end,dimension:"USAGE_TYPE",granularity:"DAILY",filter:{Dimensions:{Key:"SERVICE",Values:["Amazon CloudFront"]}}}),J=g1($);return{cost:J.reduce((Y,X)=>Y+X.amount,0),usageTypes:J.map((Y)=>Y.key)}}catch{return{cost:0,usageTypes:[]}}}async unused(G){let[$,J]=await Promise.all([this.inventory.discover(G),this.resourceCosts()]),Y=(await Promise.all($.resources.map(async(X)=>{let Q=this.resourceCost(J,X),Z=zJ(X,{},Q);if(Z)return Z;try{return zJ(X,await this.signals(X),Q)}catch{return null}}))).filter((X)=>X!==null);return{findings:Y,inventory:$,savingsAvailable:Y.some((X)=>X.monthlySavings!=null)}}async optimize(G){let $=!G?.type||["cloudfront","cdn"].includes(G.type.toLowerCase()),[J,Y,X]=await Promise.all([this.inventory.discover({type:G?.type}),this.resourceCosts(),$?this.cloudFrontTransfer():Promise.resolve({cost:0,usageTypes:[]})]),Q=[],Z=[...J.warnings];for(let F of J.resources){let U=this.resourceCost(Y,F),M=N9(F,{},U,G?.includeCommitments);if(M.length>0){Q.push(...M);continue}try{Q.push(...N9(F,await this.signals(F),U,G?.includeCommitments))}catch(H){Z.push(`CloudWatch metrics unavailable for ${F.service}:${F.type}/${F.name}: ${H instanceof Error?H.message:String(H)}`)}}let z=bG(X.cost,X.usageTypes);if(z)Q.push(z);return Q.sort((F,U)=>(U.monthlySavings??-1)-(F.monthlySavings??-1)||F.category.localeCompare(U.category)||F.resource.name.localeCompare(U.resource.name)),{recommendations:Q,inventory:J,savingsAvailable:Q.some((F)=>F.monthlySavings!=null),warnings:Z}}}var FJ="Amazon Simple Storage Service";function hG(){let G=new Date,$=new Date(Date.UTC(G.getUTCFullYear(),G.getUTCMonth(),1)),J=new Date($);J.setUTCMonth(J.getUTCMonth()-1);let Y=(Q)=>Q.toISOString().slice(0,10),X=J.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"});return{start:Y(J),end:Y($),label:X}}function G$(G){return`$${G.toFixed(2)}`}function m1(G){if(G===null)return"new";if(Math.abs(G)<0.05)return"→ 0.0%";return`${G>0?"↑":"↓"} ${Math.abs(G).toFixed(1)}%`}function UJ(G){K(`Cost Explorer request failed: ${G instanceof Error?G.message:String(G)}`),W(`
328
- This command needs the ce:GetCostAndUsage IAM permission.`)}function yG(G){let{label:$,profile:J,rows:Y,total:X,count:Q}=G,Z=`# AWS Cost Analysis — ${$}`,z=J?`
329
- _Profile: \`${J}\`_
314
+ `),v.length>0){g(`Resources to add (${v.length}):`);for(let A of v){let k=h[A].Type;a(` + ${A} (${k})`)}console.log()}if(R.length>0){p(`Resources to remove (${R.length}):`);for(let A of R){let k=f[A].Type;a(` - ${A} (${k})`)}console.log()}if(b.length>0){I(`Resources to modify (${b.length}):`);for(let A of b){let k=h[A].Type;a(` ~ ${A} (${k})`)}console.log()}a("Summary:"),a(` - Add: ${v.length}`),a(` - Remove: ${R.length}`),a(` - Modify: ${b.length}`),a("\nRun `cloud deploy` to apply these changes")}catch(t){p(`Diff failed: ${t.message}`)}})}function gt(r,e=0){let t=Number(r);return Number.isFinite(t)?t:e}function Wr(r){return gt(r)*1024}function Wu(r){let e=r?.date?.trim(),t=r?.time?.trim();if(!e||!t)return null;let n=new Date(`${e}T${t}Z`);return Number.isFinite(n.getTime())?n.toISOString():null}function qu(r,e,t){let n;try{n=JSON.parse(r)}catch{return[]}let i=(n?.sysstat?.hosts??[]).flatMap((s)=>s.statistics??[]),o=[];for(let s of i){let c=Wu(s.timestamp);if(!c)continue;let l=new Date(c).getTime();if(l<e.getTime()||l>t.getTime())continue;let d=s["cpu-load"]?.find((m)=>m.cpu==="all")??s["cpu-load"]?.[0],u=gt(d?.idle,100);o.push({timestamp:c,cpuUsedPercent:Math.max(0,Math.min(100,100-u)),cpuUserPercent:gt(d?.user),cpuSystemPercent:gt(d?.system),cpuIowaitPercent:gt(d?.iowait),memoryUsedPercent:gt(s.memory?.["memused-percent"]),memoryUsedBytes:Wr(s.memory?.memused),memoryAvailableBytes:Wr(s.memory?.avail),swapUsedPercent:gt(s.memory?.["swpused-percent"]),swapUsedBytes:Wr(s.memory?.swpused)})}return o.sort((s,c)=>s.timestamp.localeCompare(c.timestamp))}function Gu(r){return Object.fromEntries(Object.entries(r.time_series??{}).map(([e,t])=>[e,(t.values??[]).map(([n,i])=>({timestamp:new Date(Number(n)*1000).toISOString(),value:gt(i,Number.NaN)})).filter((n)=>Number.isFinite(n.value))]))}function pa(r){return r.length?r.reduce((e,t)=>e+t,0)/r.length:null}function qr(r){return r.length?Math.max(...r):null}function Ku(r){return r.length?Math.min(...r):null}async function fa(r){let[e,t]=await Promise.all([r.client.getServerMetrics(r.serverId,{types:["cpu","disk","network"],start:r.from,end:r.to,step:r.step}),Gr(r.remote.host,"LC_ALL=C sadf -j -- -u -r -S",r.remote).catch(()=>"")]),n=qu(t,r.from,r.to),i=Gu(e),s=(i.cpu??[]).map((m)=>Math.max(0,Math.min(100,m.value/Math.max(1,r.cores)))),c=n.map((m)=>m.cpuUsedPercent),l=s.length?s:c,d=n.map((m)=>m.memoryUsedPercent),u=n.map((m)=>m.swapUsedPercent);return{from:r.from.toISOString(),to:r.to.toISOString(),cores:r.cores,host:n,provider:i,summary:{cpuAveragePercent:pa(l),cpuPeakPercent:qr(l),memoryAveragePercent:pa(d),memoryPeakPercent:qr(d),memoryMinimumAvailableBytes:Ku(n.map((m)=>m.memoryAvailableBytes)),swapPeakPercent:qr(u)}}}function Hu(r){if(r.length===0)throw Error("A server name cannot be empty.");if(r.length>253)throw Error(`'${r}' is longer than the 253 characters a hostname allows.`);for(let e of r.split(".")){if(e.length===0)throw Error(`'${r}' has an empty label — a hostname cannot contain '..'.`);if(e.length>63)throw Error(`'${e}' is longer than the 63 characters a hostname label allows.`);if(!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(e))throw Error(`'${e}' is not a valid hostname label. Use letters, digits and hyphens, not starting or ending with a hyphen.`)}}async function ga(r,e,t){if(Hu(e),r===e)throw Error(`'${r}' is already named that.`);if((await t.takenNames()).some((o)=>o===e&&o!==r))throw Error(`'${e}' is already taken. Server names have to be unique within a provider project.`);let i=[];if(t.providerName&&t.renameProvider){let{providerName:o,renameProvider:s}=t;i.push({id:"provider",title:"Rename the server at the provider",change:{from:r,to:e},satisfied:async()=>await o()===e,apply:()=>s(e)})}if(t.stateName&&t.writeStateName){let{stateName:o,writeStateName:s}=t;i.push({id:"state-pin",title:"Update the recorded name in the local driver state",change:{from:r,to:e},satisfied:async()=>{let c=await o();return c===void 0||c===e},apply:()=>s(e)})}if(t.remoteHostname&&t.setRemoteHostname){let{remoteHostname:o,setRemoteHostname:s}=t;i.push({id:"hostname",title:"Set the hostname on the box",change:{from:r,to:e},satisfied:async()=>await o()===e,apply:()=>s(e)})}return i.push({id:"inventory",title:"Rename the fleet inventory record",change:{from:r,to:e},satisfied:async()=>t.inventoryName()===e,apply:async()=>{await t.renameInventory(e)}}),{operation:"server:rename",target:r,steps:i}}function ha(r){let e=`'${r.replace(/'/g,`'"'"'`)}'`;return["set -eu",'TS_CLOUD_OLD="$(hostname)"',`hostnamectl set-hostname ${e} 2>/dev/null || { echo ${e} > /etc/hostname && hostname ${e}; }`,'if grep -q "127.0.1.1" /etc/hosts; then',` sed -i "s/^127\\.0\\.1\\.1.*/127.0.1.1\\t${r}/" /etc/hosts`,"else",` printf '127.0.1.1\\t%s\\n' ${e} >> /etc/hosts`,"fi",'printf "%s -> %s\\n" "$TS_CLOUD_OLD" "$(hostname)"'].join(`
315
+ `)}async function Ju(){let r=await P(),e=q(process.cwd(),r),t=new Xe(e.store),n=new Ea(t,[new Un("aws"),new Un("hetzner"),new Un("ssh")]);return{controlPlane:e,store:t,service:n}}async function Ue(r){let e=await Ju();try{return await r(e)}finally{e.controlPlane.store.close()}}var yt=(r,e)=>{let t=r.store.list(r.controlPlane.project.id).find((n)=>n.id===e||n.name===e);if(!t)throw Error(`Server ${e} was not found.`);return t},fe=(r)=>{p(r instanceof Error?r.message:String(r)),process.exitCode=1};function Yr(r,e){let t=Number.parseInt(r??"",10);return Number.isFinite(t)&&t>=0?t:e}function Yu(r="3h"){let e=r.match(/^(\d+)(m|h)$/);if(!e)throw Error("--range must use minutes or hours, for example 30m or 3h.");let t=Number(e[1])*(e[2]==="h"?3600000:60000);if(t<300000||t>86400000)throw Error("--range must be between 5 minutes and 24 hours.");return t}function cn(r,e="%"){return r==null?"unavailable":`${r.toFixed(1)}${e}`}async function Zu(r,e){let t=await P();if(t.cloud?.provider!=="hetzner")throw Error("server:monitoring currently requires the Hetzner provider.");let n=e.env??process.env.CLOUD_ENV??"production",i=Ve(t,n),o=await Rt(i);if(!o?.serverId)throw Error(`No pinned Hetzner server was found for ${i}.`);if(![o.serverName,String(o.serverId),o.stackName].includes(r))throw Error(`Refusing to read ${r}: project state is pinned to ${o.serverName}.`);let s=ln(t),c=new Vt({apiToken:Wt(s.apiToken,t)}),l=await c.getServer(o.serverId),d=o.publicIp??l.public_net.ipv4?.ip;if(!d)throw Error(`Server ${l.name} has no public IPv4 address.`);let u=new Date,m=new Date(u.getTime()-Yu(e.range)),f=Math.min(3600,Math.max(60,Yr(e.step,60))),h=await fa({client:c,serverId:l.id,cores:Math.max(1,Number(l.server_type.cores)||1),from:m,to:u,step:f,remote:{host:d,user:o.sshUser??s.sshUser,identityFile:s.sshPrivateKeyPath,connectTimeoutSec:15}});if(e.json){console.log(JSON.stringify({schemaVersion:1,server:l.name,result:h},null,2));return}x(["Server","Window","CPU average / peak","RAM average / peak","Minimum available","Swap peak"],[[l.name,`${h.from} to ${h.to}`,`${cn(h.summary.cpuAveragePercent)} / ${cn(h.summary.cpuPeakPercent)}`,`${cn(h.summary.memoryAveragePercent)} / ${cn(h.summary.memoryPeakPercent)}`,h.summary.memoryMinimumAvailableBytes==null?"unavailable":`${(h.summary.memoryMinimumAvailableBytes/1073741824).toFixed(2)} GB`,cn(h.summary.swapPeakPercent)]])}function Da(r){let e=r.targetType.prices?.find((t)=>t.location===r.location);return{serverId:r.serverId,serverName:r.serverName,location:r.location,from:r.currentType.name,to:r.targetType.name,capacityAvailable:r.capacityAvailable,alreadyComplete:r.alreadyComplete,upgradeDisk:r.upgradeDisk,target:{cores:r.targetType.cores,memoryGb:r.targetType.memory,diskGb:r.targetType.disk,architecture:r.targetType.architecture,cpuType:r.targetType.cpu_type},price:e?{hourlyNet:e.price_hourly.net,hourlyGross:e.price_hourly.gross,monthlyNet:e.price_monthly.net,monthlyGross:e.price_monthly.gross}:void 0}}function Qu(r){let e=Da(r);x(["Server","Location","Resize","Capacity","CPU / RAM / disk","Monthly"],[[`${e.serverName} (${e.serverId})`,e.location,`${e.from} -> ${e.to}`,e.capacityAvailable?"available":"waiting",`${e.target.cores??0} / ${e.target.memoryGb??0} GB / ${e.target.diskGb??0} GB`,e.price?.monthlyGross??e.price?.monthlyNet??"not reported"]])}async function Xu(r,e,t){let n=await P();if(n.cloud?.provider!=="hetzner")throw Error("server:resize currently requires the Hetzner provider.");let i=t.env??process.env.CLOUD_ENV??"production",o=Ve(n,i),s=await Rt(o),c=ln(n),l=new Vt({apiToken:Wt(c.apiToken,n)}),u=s&&(r===s.serverName||r===String(s.serverId)||r===s.stackName)?await l.getServer(s.serverId):(await l.listServers()).find((D)=>D.name===r||String(D.id)===r);if(!u)throw Error(`Hetzner server ${r} was not found.`);if(s?.serverId&&s.serverId!==u.id)throw Error(`Refusing to resize ${r}: project state is pinned to server ${s.serverId}.`);let m=va(e),f=t.upgradeDisk!==!1,h=await Hr(l,u.id,m,f);if(t.json)console.log(JSON.stringify({schemaVersion:1,plan:Da(h)},null,2));else Qu(h);if(!t.apply){if(!t.json)a(`Preview only. Re-run with --apply --confirm ${u.name} to execute.`);return}if(t.confirm!==u.name)throw Error(`Resize requires --confirm ${u.name}.`);let S=s?.publicIp??u.public_net.ipv4?.ip;if(!S)throw Error(`Server ${u.name} has no public IPv4 address for verification.`);let y={host:S,user:s?.sshUser??c.sshUser,identityFile:c.sshPrivateKeyPath,connectTimeoutSec:15,sshTimeoutMs:600000,routeTimeoutMs:20000},v=await Pa(o),R=new Date().toISOString(),b={schemaVersion:1,operationId:v?.targetType===m&&v.serverId===u.id?v.operationId:crypto.randomUUID(),stackName:o,serverId:u.id,serverName:u.name,sourceType:h.currentType.name,targetType:m,upgradeDisk:f,phase:"planning",status:"running",attempts:v?.targetType===m?v.attempts:0,startedAt:v?.targetType===m?v.startedAt:R,updatedAt:R,manifest:v?.targetType===m?v.manifest:void 0};await ht(b);let A=Yr(t.pollSeconds,900)*1000,k=Yr(t.maxWaitMinutes,0)*60000,T=Date.now();for(;;){let D=await Hr(l,u.id,m,f);if(!D.capacityAvailable&&!D.alreadyComplete){if(b={...b,phase:"waiting-capacity",status:"waiting-capacity",updatedAt:new Date().toISOString()},await ht(b),!t.wait){if(!t.json)a(`${m} has no capacity in ${D.location}. The server remains running and unchanged. Use --wait to poll safely.`);return}if(k>0&&Date.now()-T>=k)throw Error(`Timed out waiting for ${m} capacity in ${D.location}.`);if(!t.json)a(`Waiting ${Math.round(A/1000)} seconds for ${m} capacity...`);await Bun.sleep(A);continue}let E=await xa(o);try{let N=await(async()=>{try{return b={...b,status:"running",attempts:b.attempts+1,updatedAt:new Date().toISOString()},await ht(b),await wa({client:l,serverId:u.id,targetType:m,upgradeDisk:f,resumeFromOff:b.phase==="shutting-down"||b.phase==="resizing"||b.phase==="powering-on"||b.phase==="verifying",manifest:b.manifest,hooks:{preflight:async()=>{let _=await Sa(y);return b={...b,manifest:_,updatedAt:new Date().toISOString()},await ht(b),_},afterBoot:async(_,B,V,K)=>$a(y,B,V,K),onPhase:async(_,B)=>{if(b={...b,phase:_,status:_==="waiting-capacity"?"waiting-capacity":_==="complete"?"completed":"running",updatedAt:new Date().toISOString()},await ht(b),!t.json)a(`${_}${B?` ${JSON.stringify(B)}`:""}`)}}})}finally{await E()}})();if(b={...b,status:N.status==="completed"||N.status==="already-complete"?"completed":N.status==="recovered"?"recovered":"waiting-capacity",lastError:N.status==="recovered"?N.error:void 0,updatedAt:new Date().toISOString()},await ht(b),t.json)console.log(JSON.stringify({schemaVersion:1,result:N},null,2));else if(N.status==="completed"||N.status==="already-complete")g(`${u.name} is on ${m} and passed full post-boot verification.`);else if(N.status==="recovered"&&N.retryable&&t.wait){a(`Capacity race recovered safely. Waiting ${Math.round(A/1000)} seconds before retry.`),await Bun.sleep(A);continue}else if(N.status==="recovered")throw Error(`Resize did not complete and the original server was recovered: ${N.error}`);return}catch(N){throw b={...b,status:"failed",lastError:N instanceof Error?N.message:String(N),updatedAt:new Date().toISOString()},await ht(b),N}}}async function em(r,e){let t=await P();if(t.cloud?.provider!=="hetzner")throw Error("server:optimize currently requires the Hetzner provider.");let n=e.env??process.env.CLOUD_ENV??"production",i=Ve(t,n),o=await Rt(i);if(!o?.serverId)throw Error(`No pinned Hetzner server was found for ${i}.`);if(![o.serverName,String(o.serverId),o.stackName].includes(r))throw Error(`Refusing to optimize ${r}: project state is pinned to ${o.serverName}.`);let s=ln(t),c=new Vt({apiToken:Wt(s.apiToken,t)}),l=await c.getServer(o.serverId),d=o.publicIp??l.public_net.ipv4?.ip;if(!d)throw Error(`Server ${l.name} has no public IPv4 address.`);let u=Aa(t);if(e.json)console.log(JSON.stringify({schemaVersion:1,server:l.name,plan:u},null,2));else x(["Server","Firewall ports","Monitoring","Auto updates","Swap","SSH passwords"],[[l.name,u.firewallPorts.join(","),u.monitoring?"on":"off",u.autoUpdates?"on":"off",`${u.swapGb} GB`,"disabled"]]);if(!e.apply){if(!e.json)a(`Preview only. Re-run with --apply --confirm ${l.name} to execute.`);return}if(e.confirm!==l.name)throw Error(`Optimization requires --confirm ${l.name}.`);let m={host:d,user:o.sshUser??s.sshUser,identityFile:s.sshPrivateKeyPath,connectTimeoutSec:15},f=await Jr(m),h=f.routeProbes.filter((V)=>!V.ok),S=t.infrastructure?.compute,y=Kr(S)?new Set(["nginx.service","bun-gateway.service","ts-cloud-nginx.service"]):new Set,v=f.failedUnits.filter((V)=>!y.has(V));if(v.length>0)throw Error(`Preflight found failed services: ${v.join(", ")}`);if(h.length>0)throw Error(`Preflight found unhealthy routes: ${h.map((V)=>V.domain).join(", ")}`);let R=Kr(S)?[]:Object.values(t.sites??{}).flatMap((V)=>typeof V?.port==="number"&&![80,443].includes(V.port)?[V.port]:[]),b=ya({allowSsh:S?.allowSsh!==!1,sitePorts:R,allowedPorts:S?.firewall?.allowedPorts}),A=(o.firewallId?(await c.listFirewalls()).find((V)=>V.id===o.firewallId):void 0)??(await c.listFirewalls()).find((V)=>V.name===`${t.project.slug}-${n}-app-fw`);if(!A)throw Error(`Managed firewall for ${l.name} was not found.`);let k=await c.setFirewallRules(A.id,b);await Promise.all(k.map((V)=>c.waitForAction(V.id))),await ka(t,m);let[T,D]=await Promise.all([Jr(m),Ra(m)]),E=Ia(u,D),N=Ca(f,T),_=T.routeProbes.filter((V)=>!V.ok);if(N.stoppedServices.length>0)E.push(`services stopped: ${N.stoppedServices.join(", ")}`);if(N.changedRouteFragments.length>0)E.push(`route fragments changed: ${N.changedRouteFragments.join(", ")}`);if(N.missingRouteIds.length>0)E.push(`routes disappeared: ${N.missingRouteIds.join(", ")}`);if(N.changedReleaseLinks.length>0)E.push(`release links changed: ${N.changedReleaseLinks.join(", ")}`);if(N.missingData.length>0)E.push(`databases or volumes disappeared: ${N.missingData.join(", ")}`);if(_.length>0)E.push(`unhealthy routes: ${_.map((V)=>V.domain).join(", ")}`);if(E.length>0)throw Error(`Host optimization verification failed: ${E.join("; ")}`);let B={serverId:l.id,serverName:l.name,services:T.runningServices.length,routes:T.routeIds.length,healthyDomains:T.routeProbes.length,releaseLinks:T.releaseLinks.length,databasesAndVolumes:T.dataCatalog.length,report:D};if(e.json)console.log(JSON.stringify({schemaVersion:1,result:B},null,2));else g(`${l.name} passed full host, rpx route, service, release, and data verification.`)}async function tm(r,e,t){let n=await P(),i=t.env??"production",o=Ve(n,i);await Ue(async(s)=>{let c=yt(s,r),l=s.store.list(s.controlPlane.project.id,!0),d=c.provider==="hetzner"&&c.providerId?(()=>{try{let A=ln(n);return new Vt({apiToken:Wt(A.apiToken,n)})}catch{return null}})():null,u=c.providerId?Number(c.providerId):Number.NaN,m=await Rt(o),f=m!=null&&m.serverId===u,h=new Mn,S=c.trustState==="pinned",y={takenNames:async()=>{let A=l.map((k)=>k.name);if(d)A.push(...(await d.listServers()).map((k)=>k.name));return A},inventoryName:()=>s.store.get(c.id)?.name??c.name,renameInventory:(A)=>{s.store.update(c.id,{name:A})},...d&&Number.isFinite(u)?{providerName:async()=>(await d.getServer(u)).name,renameProvider:async(A)=>{await d.renameServer(u,A)}}:{},...f?{stateName:async()=>(await Rt(o))?.serverName,writeStateName:async(A)=>{let k=await Rt(o);if(k)await ba(o,{...k,serverName:A})}}:{},...S?{remoteHostname:async()=>{let A=await h.exec(c,"hostname");return A.code===0?A.stdout.trim():void 0},setRemoteHostname:async(A)=>{let k=await h.exec(c,ha(A));if(k.code!==0)throw Error(k.stderr.trim()||`hostname exited ${k.code}`)}}:{}},v=await ga(c.name,e,y),R=await On(v);if(!t.apply){let A=[d?"":"provider record (no provider id or no API token)",f?"":"local state pin (none points at this server)",S?"":"box hostname (host key is not pinned — run server:validate first)"].filter(Boolean);if(t.json){console.log(JSON.stringify({schemaVersion:1,operation:v.operation,target:v.target,rename:{from:c.name,to:e},notCovered:A,steps:R.map((k)=>({id:k.step.id,title:k.step.title,state:k.state,change:k.step.change,reason:k.reason}))},null,2));return}for(let k of Ln(v,R))console.log(k);for(let k of A)console.log(` not covered: ${k}`);console.log(" Re-run with --apply to perform it.");return}let b=await jn(v,R,{log:(A)=>a(A),audit:(A)=>s.controlPlane.store.appendEvent({projectId:s.controlPlane.project.id,resourceId:c.resourceId,type:`${A.operation}.${A.step}.${A.state}`,level:A.state==="failed"?"error":"info",payload:{target:A.target,renameTo:e,...A.error?{error:A.error}:{}}})});if(t.json)console.log(JSON.stringify({schemaVersion:1,outcome:b},null,2));if(!b.success){let A=b.steps.find((k)=>k.state==="failed");throw Error(`${v.operation} stopped at '${A?.title}': ${A?.error}. Earlier steps stayed applied; re-run the same command to continue from here.`)}if(!t.json)g(`Renamed ${c.name} to ${e}.`)})}function Zr(r){r.command("capabilities [server]","Show provider and target operation support").option("--json","Print structured JSON").action(async(e,t)=>{try{await Ue(async(n)=>{let i=e?[yt(n,e)]:n.store.list(n.controlPlane.project.id),o=i.flatMap((s)=>Object.entries(s.capabilities).map(([c,l])=>({server:s.name,provider:s.provider,action:c,...l})));if(t.json)console.log(JSON.stringify({schemaVersion:1,targets:o},null,2));else x(["Server","Provider","Action","Support","Explanation"],o.map((s)=>[s.server,s.provider,s.action,s.supported?"supported":"unsupported",s.reason??"—"]))})}catch(n){fe(n)}}),r.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(e,t,n)=>{try{await Xu(e,t,n)}catch(i){fe(i)}}),r.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(e,t)=>{try{await em(e,t)}catch(n){fe(n)}}),r.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(e,t)=>{try{await Zu(e,t)}catch(n){fe(n)}}),r.command("server:list","List fleet servers").option("--json","Print structured JSON").action(async(e)=>{try{await Ue(async(t)=>{let n=t.store.list(t.controlPlane.project.id);if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Provider ID","Provider / region","Status / trust","Roles","CPU / memory","Heartbeat"],n.map((i)=>[i.name,i.providerId??"—",`${i.provider} / ${i.region??"external"}`,`${i.status} / ${i.trustState}`,i.roles.join(","),`${i.capacity.cpu??0} / ${i.capacity.memoryBytes??0}`,i.heartbeatAt??"never"]))})}catch(t){fe(t)}}),r.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(e,t)=>{try{await Ue(async(n)=>{let i=t.provider;if(!["aws","hetzner","ssh"].includes(i)||!t.endpoint)throw Error("A supported --provider and --endpoint are required; provisioning and enrollment are separate operations.");let o=n.service.enroll({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,provider:i,providerId:t.providerId,endpoint:t.endpoint,sshUser:t.user??"deploy",credentialRef:t.credentialRef??"secret://fleet/agent",region:t.region,roles:String(t.roles??"application").split(","),labels:Object.fromEntries(String(t.labels??"").split(",").map((s)=>s.split("=")).filter((s)=>s[0]&&s[1]))});g(`Enrolled ${o.name} as ${o.id}; no remote mutation was performed.`)})}catch(n){fe(n)}}),r.command("server:validate <name>","Pin trust and validate a server").option("--accept-host-key <fingerprint>","Accept reviewed rotation").option("--json","Print JSON").action(async(e,t)=>{try{await Ue(async(n)=>{let i=yt(n,e);if(i=await n.service.test(i.id),i.trustState==="rotation_pending"){if(!t.acceptHostKey)throw Error(`Host key changed to ${i.pendingHostKey}; review it first.`);i=n.service.reviewHostKey(i.id,t.acceptHostKey)}let o=await n.service.validate(i.id);if(t.json)console.log(JSON.stringify(o.validation,null,2));else x(["Severity","Code","Finding","Remediation"],(o.validation?.findings??[]).map((s)=>[s.severity,s.code,s.message,s.remediation??"—"]));if(!o.validation?.valid)process.exitCode=1})}catch(n){fe(n)}}),r.command("server:bootstrap <name>","Preview or queue bootstrap").option("--apply","Apply reviewed plan").action(async(e,t)=>{try{await Ue(async(n)=>{let i=n.service.bootstrap(yt(n,e).id,!!t.apply);if(i.preview)x(["Step"],i.steps.map((o)=>[o]));else g(`Bootstrap queued: ${i.operation?.id}`)})}catch(n){fe(n)}}),r.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(e,t,n)=>{try{await tm(e,t,n)}catch(i){fe(i)}}),r.command("server:drain <name>","Drain without terminating").option("--complete","Mark movement complete").action(async(e,t)=>{try{await Ue(async(n)=>g(n.service.drain(yt(n,e).id,!!t.complete).status))}catch(n){fe(n)}}),r.command("server:uncordon <name>","Return a server to scheduling").action(async(e)=>{try{await Ue(async(t)=>{t.service.uncordon(yt(t,e).id),g(`Uncordoned ${e}.`)})}catch(t){fe(t)}}),r.command("server:archive <name>","Archive inventory without termination").option("--confirm <name>","Exact name").action(async(e,t)=>{try{await Ue(async(n)=>{n.service.archive(yt(n,e).id,t.confirm??""),g(`Archived ${e}; provider infrastructure was not terminated.`)})}catch(n){fe(n)}}),r.command("server:ssh <name>","Open strict SSH using the enrolled endpoint").action(async(e)=>{try{await Ue(async(t)=>{let n=yt(t,e);if(n.trustState!=="pinned")throw Error("Validate and pin the server host key first.");let i=Bun.spawn(["ssh","-p",String(n.sshPort),"-o","StrictHostKeyChecking=yes",`${n.sshUser}@${n.endpoint}`],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}),o=await i.exited;if(o)process.exitCode=o})}catch(t){fe(t)}});for(let[e,t]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"]])r.command(e,t).action(async(...n)=>be(e.split(" ")[0],{target:String(n[0]??""),nextAction:"Use the dashboard capability view or the corresponding runtime, release, firewall, backup, or maintenance service."}))}async function nm(){let r=await P(),e=q(process.cwd(),r),t=new Na(e.store),n=new Oa(t,new Xe(e.store));return{controlPlane:e,store:t,service:n}}async function dn(r){let e=await nm();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}var Vn=(r)=>Object.fromEntries(String(r??"").split(",").map((e)=>e.split("=")).filter((e)=>e[0]&&e[1])),Ta=(r)=>Object.fromEntries(Object.entries(Vn(r)).map(([e,t])=>[e,Number(t)]));function Qr(r){r.command("pool:list","List capacity pools and reservations").option("--json","Print structured JSON").action(async(e)=>dn((t)=>{let n=t.store.listPools(t.controlPlane.project.id);if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Purpose","Backend","Region / arch","Status","Capacity","Concurrency"],n.map((i)=>[i.name,i.purpose,i.backend,`${i.region??"any"} / ${i.architecture??"any"}`,i.status,JSON.stringify(i.capacity),String(i.concurrency)]))})),r.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(e,t)=>dn((n)=>{let{purpose:i,backend:o}=t;if(!["application","build","worker","monitoring","backup"].includes(i)||!["server","ecs","asg"].includes(o))throw Error("Unsupported pool purpose or backend.");let s=n.store.createPool({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,purpose:i,backend:o,region:t.region,architecture:t.arch,labels:Vn(t.labels),requiredServerLabels:Vn(t.selector),toleratedTaints:[],capacity:{cpu:0,memoryBytes:0,diskBytes:0,gpu:0,...Ta(t.capacity)},maxWorkloads:Number(t.max),costWeight:1,concurrency:Number(t.concurrency),ephemeralWorkspaces:!0,allowProductionSecrets:!1,status:"active"});g(`Created ${s.name} (${s.id}).`)})),r.command("pool:add-server <pool> <server>","Attach an enrolled server to a pool").action(async(e,t)=>dn((n)=>{let i=n.store.listPools(n.controlPlane.project.id).find((s)=>s.id===e||s.name===e),o=new Xe(n.controlPlane.store).list(n.controlPlane.project.id).find((s)=>s.id===t||s.name===t);if(!i||!o)throw Error("Pool or server was not found.");n.store.addMember(i.id,o.id),g(`Attached ${o.name} to ${i.name}.`)})),r.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(e)=>dn((t)=>{let n=t.service.explain(t.controlPlane.project.id,{purpose:e.purpose,resources:Ta(e.resources),region:e.region,architecture:e.arch,labels:Vn(e.labels)});if(e.json)console.log(JSON.stringify(n,null,2));else x(["Pool","Server","Eligible","Available","Fit / spread / cost","Reasons"],n.map((i)=>[i.poolName,i.serverId??"provider-managed",i.eligible?"yes":"no",JSON.stringify(i.available),`${i.score.fit.toFixed(2)} / ${i.score.spread} / ${i.score.cost}`,i.reasons.join("; ")||"eligible"]))})),r.command("pool:drain <pool>","Move stateless placements and block stateful placements").action(async(e)=>dn((t)=>{let n=t.store.listPools(t.controlPlane.project.id).find((o)=>o.id===e||o.name===e);if(!n)throw Error("Pool was not found.");let i=t.service.drainPool(n.id);g(`Draining ${n.name}: moved ${i.moved.length}, blocked ${i.blocked.length} stateful placement(s).`)}))}async function rm(){let r=await P(),e=q(process.cwd(),r),t=new La(e.store),n=new ja(t);return{controlPlane:e,store:t,service:n}}async function It(r){let e=await rm();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}var un=(r,e)=>{let t=r.store.list(r.controlPlane.project.id).find((n)=>n.id===e||n.name===e);if(!t)throw Error("Regional topology was not found.");return t},im=(r)=>r.split(",").map((e,t)=>{let[n,i="aws"]=e.split(":");return{region:String(n),provider:i,role:t===0?"primary":"secondary"}}),om=(r)=>{let e=new Set(["s3","dynamodb","secrets"]),t=r.split(",").map((i)=>i.trim()),n=t.find((i)=>!e.has(i));if(n)throw Error(`Unsupported replication kind: ${n}`);return t};function Xr(r){r.command("region:list","List regional topology, traffic, and replication health").option("--json","Print structured JSON").action(async(e)=>It((t)=>{let n=t.store.list(t.controlPlane.project.id).map((i)=>({topology:i,targets:t.store.targets(i.id),channels:t.store.channels(i.id),route:t.store.route(i.id),executions:t.store.executions(i.id)}));if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Hostname","Active","Status","Revision","Replication"],n.map((i)=>[i.topology.name,i.topology.hostname,i.topology.activeRegion,i.topology.status,i.topology.revision??"—",`${i.channels.filter((o)=>o.status==="in_sync").length}/${i.channels.length} in sync`]))})),r.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(e,t)=>It((n)=>{if(!t.hostname||!t.regions)throw Error("--hostname and --regions are required.");let i=n.service.create({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,hostname:t.hostname,regions:im(t.regions),trafficPolicy:"active_passive",dataPolicy:{replicate:om(String(t.replicate)),maxLagSeconds:Number(t.maxLag),retainOnDestroy:!0},dnsProvider:"route53",cdnEnabled:!!t.cdn,wafEnabled:!!t.waf});g(`Created ${i.name} (${i.id}); run region:rollout with an immutable manifest.`)})),r.command("region:plan <name> <operation>","Preview exact regional execution steps").option("--target <region>","Failover or failback region").action(async(e,t,n)=>It((i)=>x(["Step"],i.service.plan(un(i,e).id,t,n.target).map((o)=>[o])))),r.command("region:rollout <name> <revision>","Queue secondary-first regional stack rollout").option("--manifest <file>","Regional stack manifest JSON").action(async(e,t,n)=>It(async(i)=>{let o=n.manifest?await Bun.file(n.manifest).json():{},s=i.service.enqueue({topologyId:un(i,e).id,kind:"rollout",revision:t,manifest:o});g(`Regional rollout queued: ${s.operation.id}`)})),r.command("region:failover <name> <region>","Queue health and RPO-gated failover").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"failover",targetRegion:t});g(`Failover queued: ${i.operation.id}`)})),r.command("region:failback <name> <region>","Queue health and RPO-gated failback").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"failback",targetRegion:t});g(`Failback queued: ${i.operation.id}`)})),r.command("region:destroy <name>","Drain traffic and delete regional stacks while retaining data").option("--confirm <name>","Exact topology name").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"destroy",confirmation:t.confirm});g(`Regional teardown queued: ${i.operation.id}; replicated data remains retained.`)}))}var mn="0.10.0";async function am(){let r=await P(),e=q(process.cwd(),r),t=new Fa(e.store),n=new Ma(t),i=["releases","artifacts","operations","logs","previews","snapshots","orphaned_resources"].map((o)=>new Ba(o,e.store,e.project.id,e.organization.id));return{controlPlane:e,store:t,service:n,cleanup:i}}async function et(r){let e=await am();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}function ei(r){r.command("maintenance:list","List updates, campaigns, cleanup plans, and DR drills").option("--json","Print structured JSON").action(async(e)=>et((t)=>{let n={manifests:t.store.manifests(t.controlPlane.organization.id),windows:t.store.windows(t.controlPlane.project.id),campaigns:t.store.campaigns(t.controlPlane.project.id),cleanups:t.store.cleanups(t.controlPlane.project.id),drills:t.store.drills(t.controlPlane.project.id)},i=[...n.manifests.map((o)=>["update",o.version,o.compatibility.compatible?"compatible":"blocked",o.compatibility.reasons.join("; ")||o.channel]),...n.campaigns.map((o)=>["upgrade",o.id,o.status,o.currentStage??o.fromVersion]),...n.cleanups.map((o)=>["cleanup",o.kind,o.status,`${o.candidates.length} items / ${o.estimatedBytes} bytes`]),...n.drills.map((o)=>["DR drill",o.scenario,o.status,o.isolatedTarget])];if(e.json)console.log(JSON.stringify(n,null,2));else x(["Area","Name / version","Status","Detail"],i)})),r.command("maintenance:key:add <name> <pem>","Trust an Ed25519 update signing key").action(async(e,t)=>et(async(n)=>{let i=n.store.addTrustedKey({organizationId:n.controlPlane.organization.id,name:e,publicKeyPem:await Bun.file(t).text()});g(`Trusted ${i.name}: ${i.fingerprint}`)})),r.command("maintenance:manifest <document> <signature> <key>","Verify and import a signed update manifest").action(async(e,t,n)=>et(async(i)=>{let o=await Bun.file(e).json(),s=(await Bun.file(t).text()).trim(),c=i.store.importManifest({organizationId:i.controlPlane.organization.id,keyId:n,document:o,signature:s,current:{schemaVersion:i.controlPlane.store.health().schemaVersion,currentVersion:mn,platform:process.platform,architecture:process.arch}});g(`Verified ${c.version}; ${c.compatibility.compatible?"compatible":"blocked: "+c.compatibility.reasons.join("; ")}`)})),r.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(e,t,n)=>et((i)=>{let o=i.store.createWindow({projectId:i.controlPlane.project.id,name:e,schedule:t,timezone:n.timezone,durationMinutes:Number(n.minutes),allowedOperations:["platform.upgrade"],requireApproval:!0});g(`Created ${o.name} (${o.id}).`)})),r.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(e,t,n)=>et((i)=>{let o=new Xe(i.controlPlane.store).list(i.controlPlane.project.id).filter((l)=>l.status==="ready").map((l)=>({serverId:l.id,target:l.name,previousVersion:l.bootstrapVersion??mn}));if(!o.length)o.push({target:"control-plane",previousVersion:mn});let s=i.store.createCampaign({projectId:i.controlPlane.project.id,manifestId:e,fromVersion:mn,strategy:n.strategy==="rolling"?"rolling":"canary",batchSize:Number(n.batch),healthGate:{kind:"platform"},backupId:t,targets:o}),c=i.service.enqueueCampaign(s.id);g(`Staged upgrade queued: ${c.operation.id}`)})),r.command("cleanup:preview <kind>","Discover a bounded cleanup candidate set without deleting").option("--older-than <days>","Minimum age",{default:"30"}).action(async(e,t)=>et(async(n)=>{let i=await n.service.previewCleanup({projectId:n.controlPlane.project.id,kind:e,criteria:{olderThanDays:Number(t.olderThan)}},n.cleanup);x(["ID","Candidate","Bytes"],i.candidates.map((o)=>[o.id,o.label,String(o.bytes)])),a(`Apply with: cloud cleanup:apply ${i.id} --confirm "DELETE ${i.candidates.length} ${i.kind}"`)})),r.command("cleanup:apply <plan>","Apply an unchanged cleanup preview").option("--confirm <text>","Exact preview confirmation").action(async(e,t)=>et((n)=>{let i=n.service.enqueueCleanup(e,String(t.confirm??""));g(`Cleanup queued: ${i.operation.id}`)})),r.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(e,t,n)=>et((i)=>{let o=i.store.createDrill({projectId:i.controlPlane.project.id,scenario:e,isolatedTarget:t,expectedRpoMinutes:Number(n.rpo),expectedRtoMinutes:Number(n.rto),recoveryPointId:n.recoveryPoint,topologyId:n.topology});g(`Planned isolated drill ${o.id}.`)})),r.command("dr:run <drill>","Queue an isolated disaster-recovery drill").action(async(e)=>et((t)=>{let n=t.service.enqueueDrill(e);g(`DR drill queued: ${n.operation.id}`)}))}async function ti(){try{return(await P()).project.region||process.env.AWS_REGION||"us-east-1"}catch{return process.env.AWS_REGION||"us-east-1"}}function ni(r){r.command("function:list","List all Lambda functions").option("--region <region>","AWS region").action(async(e)=>{w("Lambda Functions");let t=e?.region||await ti(),n=new tt(t);try{let{Functions:i=[]}=await n.listFunctions({MaxItems:50});if(!i.length){a("No functions found");return}x(["Name","Runtime","Memory","Timeout","Last Modified"],i.map((o)=>[o.FunctionName??"-",o.Runtime??"-",o.MemorySize?`${o.MemorySize} MB`:"-",o.Timeout?`${o.Timeout}s`:"-",o.LastModified??"-"]))}catch(i){p(`Failed to list functions: ${i.message}`),process.exitCode=1}}),r.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(e,t)=>{w(`Invoking ${e}`);let n=t?.region||await ti(),i=new tt(n),o=new C("Invoking function...");o.start();try{let s=await i.invoke({FunctionName:e,InvocationType:t?.async?"Event":"RequestResponse",Payload:t?.payload||"{}",LogType:t?.async?"None":"Tail"});if(s.FunctionError){if(o.fail(`Function returned an error (${s.FunctionError})`),s.Payload)a(s.Payload);process.exitCode=1;return}if(o.succeed(`Invoked (status ${s.StatusCode})`),s.Payload)a(`
316
+ Response:`),a(s.Payload);if(s.LogResult)a(`
317
+ Logs:`),a(Buffer.from(s.LogResult,"base64").toString("utf-8"))}catch(s){o.fail(`Invocation failed: ${s.message}`),process.exitCode=1}}),r.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(e,t)=>{w(`Logs for ${e}`);let n=t?.region||await ti(),i=new nt(n),o=e.startsWith("/aws/lambda/")?e:`/aws/lambda/${e}`,s=Number(t?.since??15),c=(l)=>{for(let d of l){let u=d.timestamp?new Date(d.timestamp).toISOString():"";a(`${u} ${(d.message??"").trimEnd()}`)}};try{let l=Date.now()-s*60000,{events:d=[]}=await i.filterLogEvents({logGroupName:o,startTime:l,filterPattern:t?.filter,limit:200});if(c(d),d.length)l=(d[d.length-1].timestamp??l)+1;if(t?.tail){a(`
318
+ (streaming Ctrl+C to stop)`);for(;;){await new Promise((m)=>setTimeout(m,3000));let{events:u=[]}=await i.filterLogEvents({logGroupName:o,startTime:l,filterPattern:t?.filter,limit:200});if(u.length)c(u),l=(u[u.length-1].timestamp??l)+1}}}catch(l){p(`Failed to read logs: ${l.message}`),process.exitCode=1}})}async function _a(r){let e=Bun.spawn(["docker",...r],{stdin:"inherit",stdout:"inherit",stderr:"pipe"}),[t,n]=await Promise.all([e.exited,new Response(e.stderr).text()]);return{ok:t===0,exitCode:t,command:["docker",...r].join(" "),stderr:n.trim()}}function ri(r){r.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(e)=>{let t=["build","--file",e.file??"Dockerfile","--tag",e.tag??"latest","."];if(e.dryRun){console.log(`docker ${t.join(" ")}`);return}let n=await _a(t);if(e.json)console.log(JSON.stringify(n));if(!n.ok)p(n.stderr||`docker exited ${n.exitCode}`),process.exitCode=n.exitCode;else g(`Built ${e.tag??"latest"}.`)}),r.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(e)=>{if(!e.image){p("--image with a full registry reference is required; credentials must come from docker credential helpers."),process.exitCode=2;return}if(e.dryRun){console.log(`docker push ${e.image}`);return}let t=await _a(["push",e.image]);if(e.json)console.log(JSON.stringify(t));if(!t.ok)p(t.stderr),process.exitCode=t.exitCode;else g(`Pushed ${e.image}.`)}),r.command("container:deploy","Deploy a container image").action(async()=>be("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 ui(r){r.command("domain:list","List all domains").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(e)=>{w("Domains");try{let t=Be(e?.provider),n=ce(e?.provider)?.provider||"unknown",i=new C(`Fetching domains from ${n}...`);i.start();let o=await t.listDomains();if(i.succeed(`Found ${o.length} domain(s)`),o.length===0){a("No domains found in this provider");return}let s=o.map((c)=>[c,"Active","-",n.charAt(0).toUpperCase()+n.slice(1)]);x(["Domain","Status","SSL","Provider"],s)}catch(t){p(`Failed to list domains: ${t instanceof Error?t.message:String(t)}`)}}),r.command("domain:add <domain>","Add a new domain").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(e,t)=>{w(`Adding Domain: ${e}`);try{let n=Be(t?.provider),i=ce(t?.provider)?.provider||"unknown",o=new C(`Checking if ${i} can manage ${e}...`);if(o.start(),await n.canManageDomain(e))o.succeed(`Domain ${e} is already available in ${i}`),a(`
319
+ The domain is ready to use. You can now:`),a(` - Add DNS records: cloud dns:add ${e} A 192.168.1.1`),a(` - Generate SSL: cloud domain:ssl ${e}`);else if(o.warn(`Domain ${e} is not available in ${i}`),a(`
320
+ To add this domain:`),i==="route53")a(" - Create a hosted zone in Route53 for this domain"),a(" - Update nameservers at your registrar to point to Route53");else a(` - Ensure the domain is registered with ${i}`),a(" - Enable API access for the domain in your provider dashboard")}catch(n){p(`Failed to check domain: ${n instanceof Error?n.message:String(n)}`)}}),r.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(e,t)=>{w(`Generating SSL Certificate for ${e}`);try{let n=ce(t?.provider);if(!n)throw Error("No DNS provider configured");let i=t?.region||"us-east-1",o=n.provider;a(`DNS Provider: ${o}`),a(`ACM Region: ${i}`);let s=new di(n,i),c=new C("Requesting certificate and creating validation records...");c.start();let l=await s.findOrCreateCertificate({domainName:e,subjectAlternativeNames:[`*.${e}`],waitForValidation:t?.wait??!0,maxWaitMinutes:10});if(l.isNew)c.succeed("Certificate requested and validation records created");else c.succeed("Found existing valid certificate");if(a(`Certificate ARN: ${l.certificateArn}`),a(`Status: ${l.status}`),l.status==="issued")g(`
321
+ SSL Certificate is ready!`),a(`
322
+ You can now use this certificate with:`),a(" - CloudFront distributions"),a(" - Application Load Balancers"),a(" - API Gateway custom domains");else if(l.status==="pending")a(`
323
+ DNS validation records have been created.`),a("Certificate validation may take a few more minutes."),a(`
324
+ Check status with: cloud domain:verify ${e}`);else p(`
325
+ Certificate validation failed. Check ACM console for details.`)}catch(n){p(`Failed to generate SSL: ${n instanceof Error?n.message:String(n)}`)}}),r.command("domain:verify <domain>","Verify domain ownership and SSL status").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(e,t)=>{w(`Verifying Domain: ${e}`);try{let n=Be(t?.provider),i=ce(t?.provider)?.provider||"unknown",o=new C("Checking domain ownership...");if(o.start(),!await n.canManageDomain(e)){o.fail("Domain not found in provider"),p(`Domain ${e} is not available in ${i}`);return}o.succeed("Domain ownership verified"),o.text="Fetching DNS records...",o.start();let l=(await n.listRecords(e)).records||[];o.succeed(`Found ${l.length} DNS record(s)`),o.text="Checking SSL certificate status...",o.start();let d=new vt("us-east-1"),u="Not found",m="";try{let S=(await d.listCertificates()).CertificateSummaryList.find((y)=>y.DomainName===e||y.DomainName===`*.${e}`);if(S)m=S.CertificateArn||"",u=(await d.describeCertificate({CertificateArn:m})).Status||"Unknown"}catch{}if(o.succeed("SSL check complete"),a(`
326
+ Verification details:`),a(` - Provider: ${i}`),a(` - DNS records found: ${l.length}`),a(" - Domain managed: Yes"),a(` - SSL certificate: ${u}`),m)a(` - Certificate ARN: ${m}`);let f=new Map;for(let h of l){let S=f.get(h.type)||0;f.set(h.type,S+1)}if(f.size>0){a(`
327
+ Record summary:`);for(let[h,S]of f)a(` - ${h}: ${S}`)}}catch(n){p(`Failed to verify domain: ${n instanceof Error?n.message:String(n)}`)}}),r.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(e,t)=>{w(`Zone migration: ${e} (Route53 → Cloudflare)`);let n=process.env.CLOUDFLARE_API_TOKEN;if(!n){p("CLOUDFLARE_API_TOKEN is not set.");return}try{let i=new Wn(t?.region||"us-east-1"),o=t?.hostedZone;if(!o){let S=await i.findHostedZoneForDomain(e);if(!S){p(`No Route53 hosted zone found for ${e}`);return}o=S.Id.replace("/hostedzone/","")}let s=new C(`Exporting ${e} from Route53...`);s.start();let c=await si(i,o);s.succeed(`Exported ${c.length} record set(s) from ${o}`);let l=ai(c,e);a(`Planned ${l.records.length} record(s), skipped ${l.skipped.length}`);for(let S of l.skipped)a(` - ${S.type} ${S.name}: ${S.reason}`);for(let S of l.warnings)I(` ⚠ ${S}`);for(let S of l.records.filter((y)=>y.translatedFrom))a(` ~ ${S.name} ${S.translatedFrom} → CNAME ${S.content}`);if(!t?.apply){a(""),a("Dry run — nothing was written. Re-run with --apply to migrate.");return}let d=new pn(n,{accountId:t?.account}),u=await d.createZone(e,{accountId:t?.account});g(u.created?`Created Cloudflare zone ${u.name} (${u.id})`:`Using existing Cloudflare zone ${u.name} (${u.id})`);let m=new C(`Importing ${l.records.length} record(s)...`);m.start();let f=await ci(d,l);m.succeed(`Imported ${f.applied.length} record(s)`);for(let S of f.failed)p(` ✗ ${S.record.type} ${S.record.name}: ${S.message}`);let h=await li(d,l);if(h.ok)g(`Parity check passed — all ${h.matched.length} record(s) present at Cloudflare`);else{p(`Parity check FAILED — ${h.missing.length} record(s) missing or different:`);for(let S of h.missing)p(` ✗ ${S.record.type} ${S.record.name} → ${S.record.content}${S.found?` (found: ${S.found})`:""}`);I("Do NOT move the nameservers until this is clean.");return}a(""),a("Nameservers to set at the registrar:");for(let S of u.nameServers)a(` ${S}`);a(""),a(` cloud dns:nameservers ${e} --set ${u.nameServers.join(",")}`)}catch(i){p(`Migration failed: ${i instanceof Error?i.message:String(i)}`)}}),r.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(e,t)=>{w(`Nameservers for ${e}`);let n=(t?.registrar||cm())?.toLowerCase();if(!n){p("No registrar credentials found. Set GODADDY_API_KEY/GODADDY_API_SECRET or PORKBUN_API_KEY/PORKBUN_SECRET_KEY.");return}let i=n==="porkbun"?new ii(process.env.PORKBUN_API_KEY,process.env.PORKBUN_SECRET_KEY):new oi(process.env.GODADDY_API_KEY,process.env.GODADDY_API_SECRET);try{let o=await i.getNameServers(e);a(`Current (${n}):`);for(let l of o)a(` ${l}`);if(!t?.set)return;let s=t.set.split(",").map((l)=>l.trim()).filter(Boolean);if(s.length<2){p("At least two nameservers are required.");return}if(I(""),I("Changing nameservers moves authority for the ENTIRE zone — every"),I("record on it, including mail. Make sure the destination zone is"),I("already populated and parity-checked (cloud dns:migrate)."),await i.updateNameServers(e,s)){g(`Delegated ${e} to:`);for(let l of s)g(` ${l}`);a("Propagation typically takes minutes to a few hours.")}else p("The registrar rejected the nameserver update.")}catch(o){p(`Failed: ${o instanceof Error?o.message:String(o)}`)}}),r.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(e,t)=>{w(`DNS Records for ${e}`);try{let n=Be(t?.provider),i=ce(t?.provider)?.provider||"unknown",o=new C(`Fetching records from ${i}...`);o.start();let c=(await n.listRecords(e)).records||[];if(o.succeed(`Found ${c.length} record(s)`),t?.type){let d=t.type.toUpperCase();c=c.filter((u)=>u.type.toUpperCase()===d),a(`Filtered to ${c.length} ${d} record(s)`)}if(c.length===0){a("No records found");return}let l=c.map((d)=>[d.type,d.name||"@",d.content.length>50?`${d.content.substring(0,47)}...`:d.content,String(d.ttl||300)]);x(["Type","Name","Value","TTL"],l)}catch(n){p(`Failed to list records: ${n instanceof Error?n.message:String(n)}`)}}),r.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(e,t,n,i)=>{w("Adding DNS Record");try{let o=Be(i?.provider),s=ce(i?.provider)?.provider||"unknown",c=i?.name||"@",l=Number.parseInt(i?.ttl||"300",10),d=t.toUpperCase();a(`Provider: ${s}`),a(`Domain: ${e}`),a(`Type: ${d}`),a(`Name: ${c}`),a(`Value: ${n}`),a(`TTL: ${l}`);let u=new C(`Adding record via ${s}...`);u.start();let m=await o.createRecord(e,{type:d,name:c==="@"?"":c,content:n,ttl:l});if(!m.success){u.fail("Failed to add DNS record"),p(m.message||"DNS provider rejected the record");return}u.succeed("DNS record added successfully"),g(`
328
+ Record created!`),a(`
329
+ Note: DNS changes may take a few minutes to propagate`)}catch(o){p(`Failed to add record: ${o instanceof Error?o.message:String(o)}`)}}),r.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(e,t,n)=>{w("Deleting DNS Record");try{let i=Be(n?.provider),o=ce(n?.provider)?.provider||"unknown",s=n?.name||"@",c=t.toUpperCase();a(`Provider: ${o}`),a(`Domain: ${e}`),a(`Type: ${c}`),a(`Name: ${s}`);let l=new C("Finding record...");l.start();let m=((await i.listRecords(e)).records||[]).filter((S)=>S.type.toUpperCase()===c&&(S.name===s||S.name===""&&s==="@"));if(m.length===0){l.fail("No matching record found");return}if(m.length>1&&!n?.value){l.warn("Multiple records found"),a(`
330
+ Please specify --value to identify which record to delete:`);for(let S of m)a(` - ${S.content}`);return}let f=n?.value?m.find((S)=>S.content===n.value)||m[0]:m[0];if(a(`Value: ${f.content}`),!await O("Delete this record?",!1)){a("Deletion cancelled");return}l.text=`Deleting record via ${o}...`,l.start(),await i.deleteRecord(e,f),l.succeed("DNS record deleted successfully"),g(`
331
+ Record deleted!`)}catch(i){p(`Failed to delete record: ${i instanceof Error?i.message:String(i)}`)}})}function cm(){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 za=["postgres","mysql","mariadb","redis","mongodb","libsql"],Ua=["aws_rds","aws_aurora","aws_elasticache","server","container","external"],Va=["observe","connect","backup","restore","restart","resize","version","rotate","expose","delete","logs","slow_queries","users","databases"];async function lm(r,e){let t=await P(),n=q(process.cwd(),t),i=new Gn(n.store),o=!r&&e?i.get(e)??i.list(n.project.id).find((y)=>y.name===e):void 0,s=o?.environmentId?n.store.listEnvironments(n.project.id).find((y)=>y.id===o.environmentId)?.slug:void 0,c=r??s??Object.keys(t.environments??{})[0]??"production",l=n.environments.get(c);if(!l)throw n.store.close(),Error(`Environment ${c} was not found.`);let d=new We(n.store,{workerId:`cli:${process.pid}`}),u=new Kn(n.store,le(process.cwd())),m=new Ha(i,d,u),f=n.store.getActorByExternalId("system","cli")??n.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"}),h=t.project?.region??"us-east-1",S={aws_rds:new Ja(new ec(new qn(h))),aws_aurora:new Ya(new tc(new qn(h))),aws_elasticache:new Za(new nc(new qe(h))),server:new Qa(new gi),container:new Xa(new gi)};return{config:t,controlPlane:n,environmentRecord:l,env:c,store:i,queue:d,secrets:u,lifecycle:m,actor:f,resolveAdapter:(y)=>S[y.provider]}}async function Pe(r,e,t){let n=await lm(r,t);try{return await e(n)}finally{n.controlPlane.store.close()}}async function we(r){try{await r()}catch(e){p(e instanceof Error?e.message:String(e))}}function mi(r){if(!za.includes(r))throw Error(`--engine must be ${za.join(", ")}.`);return r}function pi(r){if(!Ua.includes(r))throw Error(`--provider must be ${Ua.join(", ")}.`);return r}function Pt(r,e){let t=r.store.list(r.controlPlane.project.id,r.environmentRecord.id),n=r.store.get(e)??t.find((i)=>i.name===e);if(!n||n.environmentId!==r.environmentRecord.id)throw Error(`Data service ${e} was not found in ${r.env}.`);return n}function Wa(r){return r.map((e)=>[e.id,e.name,e.engine,e.provider,e.status,e.plan,e.publicExposure?"public allowlist":"private",e.managementEnabled?"managed":"read-only"])}function dm(r,e){return r!==null&&typeof r==="object"&&!Array.isArray(r)&&r.serviceId===e}function um(r){let e={};if(r.plan)e.plan=r.plan;if(r.storage!=null)e.storageGb=Number(r.storage);if(r.engineVersion)e.engineVersion=r.engineVersion;if(r.retention)e.retention=r.retention;if(r.confirm)e.confirm=r.confirm;if(r.backupId)e.backupId=r.backupId;if(r.compatibilityReviewed)e.compatibilityReviewed=!0;if(r.minCapacity!=null)e.minCapacity=Number(r.minCapacity);if(r.maxCapacity!=null)e.maxCapacity=Number(r.maxCapacity);if(r.public!=null)e.publicExposure=r.public;if(r.cidrs)e.allowedCidrs=r.cidrs.split(",").map((t)=>t.trim()).filter(Boolean);return e}function qa(r){let e=typeof r.observedState.endpoint==="string"?r.observedState.endpoint:r.placement,t=Number(r.observedState.port)||(r.engine==="postgres"?5432:r.engine==="redis"?6379:r.engine==="mongodb"?27017:r.engine==="libsql"?8080:3306),n=typeof r.observedState.database==="string"?r.observedState.database:void 0;return[{type:r.publicExposure?"external":"internal",host:e,port:t,database:n,tls:typeof r.observedState.tls==="boolean"?r.observedState.tls:r.provider.startsWith("aws_")},{type:"tunnel",host:e,port:t,database:n,tls:typeof r.observedState.tls==="boolean"?r.observedState.tls:r.provider.startsWith("aws_")}]}function Ga(r){r.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 hi(r){r.command("data:list","List first-class databases and caches").option("--env <environment>","Target environment").option("--json","Print structured JSON").action((n)=>we(()=>Pe(n.env,async(i)=>{let o=i.store.list(i.controlPlane.project.id,i.environmentRecord.id);if(n.json)a(JSON.stringify(o,null,2));else x(["ID","Name","Engine","Provider","Status","Plan","Exposure","Management"],Wa(o))}))),r.command("data:capabilities <engine> <provider>","Explain lifecycle support before taking action").action((n,i)=>we(async()=>{a(JSON.stringify(Ka(mi(n),pi(i)),null,2))}));let e=r.command("data:create <name>","Provision an encrypted, private-by-default data service");Ga(e),e.action((n,i)=>we(()=>Pe(i.env,async(o)=>{let s=mi(i.engine),c=pi(i.provider),l=o.resolveAdapter({provider:c});if(!l||!l.engines.includes(s))throw Error(`No ${c}/${s} provisioning adapter is configured. Use data:adopt for read-only inventory.`);let d={...i.database?{database:i.database}:{},...i.subnetGroup?{subnetGroup:i.subnetGroup}:{},...i.securityGroups?{securityGroupIds:i.securityGroups.split(",").map((m)=>m.trim()).filter(Boolean)}:{}},u=await o.lifecycle.create({organizationId:o.controlPlane.organization.id,projectId:o.controlPlane.project.id,environmentId:o.environmentRecord.id,name:n,engine:s,provider:c,placement:i.placement??n,engineVersion:i.engineVersion,plan:i.plan??"db.t4g.micro",storageGb:Number(i.storage)||void 0,highAvailability:i.highAvailability===!0,publicExposure:i.public===!0,allowedCidrs:i.cidrs?i.cidrs.split(",").map((m)=>m.trim()).filter(Boolean):[],desiredState:d,observedState:{},origin:"managed",managementEnabled:!0,ownerActorId:o.actor.id,username:i.username});if(g(`Queued ${u.service.name} as operation ${u.operationId}.`),u.credential)fn("One-time credential — copy it now:"),a(`Username: ${u.credential.username}`),a(`Password: ${u.credential.password}`),a(`Reference: ${u.credential.secretRef}`)})));let t=r.command("data:adopt <name>","Adopt an existing provider resource as read-only inventory");Ga(t),t.action((n,i)=>we(()=>Pe(i.env,async(o)=>{let s=o.store.create({organizationId:o.controlPlane.organization.id,projectId:o.controlPlane.project.id,environmentId:o.environmentRecord.id,name:n,engine:mi(i.engine),provider:pi(i.provider),placement:i.placement??n,engineVersion:i.engineVersion,plan:i.plan??"external",storageGb:Number(i.storage)||void 0,highAvailability:i.highAvailability===!0,publicExposure:i.public===!0,allowedCidrs:i.cidrs?i.cidrs.split(",").map((c)=>c.trim()).filter(Boolean):[],desiredState:{},observedState:{},credentialRef:void 0,status:"adopted",origin:"adopted",managementEnabled:!1,ownerActorId:o.actor.id});g(`Adopted ${s.name} (${s.id}) read-only.`)}))),r.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((n,i)=>we(()=>Pe(i.env,async(o)=>{let s=Pt(o,n),c=o.store.credential(s.id),l={...s,credentialRef:void 0,credential:c?{configured:!0,username:c.username,version:c.version,rotatedAt:c.rotatedAt}:void 0,dependencies:o.store.dependencies(s.id).map((d)=>({resourceId:d.resourceId,requiresRedeploy:d.requiresRedeploy})),operations:o.queue.list({projectId:s.projectId,limit:250}).filter((d)=>dm(d.operation.input,s.id))};if(a(JSON.stringify(l,null,2)),i.reveal){if(i.confirm!==s.name)throw Error(`Credential reveal requires --confirm ${s.name}.`);if(!c)throw Error("No managed credential is configured.");fn("Credential value follows; it will not be logged or persisted by ts-cloud:"),a(`Username: ${c.username}`),a(`Password: ${await o.secrets.resolve(c.secretRef)}`)}},n))),r.command("data:connect <service>","Print secret-free connection and tunnel guidance").option("--env <environment>","Target environment").action((n,i)=>we(()=>Pe(i.env,async(o)=>{let s=Pt(o,n);x(["Type","Command","Credential reference"],fi(s,qa(s)).map((c)=>[c.type,c.command,c.secretRef??"—"]))},n))),r.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((n,i,o)=>we(()=>Pe(o.env,async(s)=>{if(!Va.includes(i))throw Error(`Action must be ${Va.join(", ")}.`);let c=Pt(s,n),l=i,d=um(o),u=s.lifecycle.plan(c,l,d);if(a(JSON.stringify({...u,productionExecutionCreated:!1},null,2)),!o.execute)return;let m=s.lifecycle.enqueue(c,l,d,s.actor.id);g(`Queued ${l} as ${m}.`)},n))),r.command("data:management <service>","Enable reviewed management of an adopted service").option("--env <environment>","Target environment").option("--confirm <name>","Exact service name").action((n,i)=>we(()=>Pe(i.env,async(o)=>{let s=Pt(o,n);if(s.origin!=="adopted"||i.confirm!==s.name)throw Error(`Enabling adopted management requires --confirm ${s.name}.`);let c=o.store.update(s.id,s.version,{managementEnabled:!0});g(`${c.name} management is enabled.`)},n))),r.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((n,i,o)=>we(()=>Pe(o.env,async(s)=>{let c=Pt(s,n),l=s.controlPlane.store.listResources(s.controlPlane.project.id,s.environmentRecord.id).find((d)=>d.id===i||d.slug===i);if(!l)throw Error(`Resource ${i} was not found in ${s.env}.`);if(o.remove){a(s.store.removeDependency(c.id,l.id)?"Dependency removed.":"Dependency was not registered.");return}s.store.addDependency({serviceId:c.id,resourceId:l.id,secretRef:c.credentialRef??`secret://data-services/${c.projectId}/${c.name}`,requiresRedeploy:!o.dynamic}),g(`Registered dependency ${l.slug} -> ${c.name}.`)},n))),r.command("db:list","Alias for data:list").option("--env <environment>","Target environment").action((n)=>we(()=>Pe(n.env,async(i)=>x(["ID","Name","Engine","Provider","Status","Plan","Exposure","Management"],Wa(i.store.list(i.controlPlane.project.id,i.environmentRecord.id)))))),r.command("db:connect <service>","Alias for data:connect").option("--env <environment>","Target environment").action((n,i)=>we(()=>Pe(i.env,async(o)=>{let s=Pt(o,n);x(["Type","Command","Credential reference"],fi(s,qa(s)).map((c)=>[c.type,c.command,c.secretRef??"—"]))},n)));for(let n of["backup","snapshot"])r.command(`db:${n} <service>`,`Alias for data:action ${n==="backup"?"backup":"backup"}`).option("--env <environment>","Target environment").option("--execute","Enqueue the backup; default previews").action((i,o)=>we(()=>Pe(o.env,async(s)=>{let c=Pt(s,i),l=s.lifecycle.plan(c,"backup");if(a(JSON.stringify({...l,productionExecutionCreated:!1},null,2)),o.execute)g(`Queued backup as ${s.lifecycle.enqueue(c,"backup",{},s.actor.id)}.`)},i)))}function rc(r,e=15){if(!r)return e*60000;let t=/^(\d+)\s*([smhd])?$/.exec(r.trim());if(!t)return e*60000;let n=Number(t[1]),i=t[2]??"m";return n*({s:1000,m:60000,h:3600000,d:86400000}[i]??60000)}function mm(r,e){for(let t of r){let n=t.timestamp?new Date(t.timestamp).toISOString():"",i=e?`[${e}] `:"";a(`${n} ${i}${(t.message??"").trimEnd()}`)}}function yi(r){r.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(e)=>{try{let t=await P(),n=e?.env||"production",{region:i,functions:o}=bt(t,n),s=new nt(i),c=(e?.function??"all").toLowerCase();if(c!=="all"&&!["http","queue","cli"].includes(c)){p("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let d=(c==="all"?["http","queue","cli"]:[c]).map((f)=>({mode:f,group:`/aws/lambda/${o[f]}`}));w(`Logs — ${t.project.slug} (${n})`);let u=Date.now()-rc(e?.since),m=async()=>{let f=u;for(let{mode:h,group:S}of d)try{let{events:y=[]}=await s.filterLogEvents({logGroupName:S,startTime:u,filterPattern:e?.filter,limit:200});mm(y,d.length>1?h:void 0);for(let v of y)if((v.timestamp??0)>f)f=v.timestamp??f}catch(y){if(!/ResourceNotFound/i.test(String(y?.message)))throw y}u=f+1};if(await m(),e?.tail){a(`
332
+ (streaming Ctrl+C to stop)`);for(;;)await new Promise((f)=>setTimeout(f,3000)),await m()}}catch(t){p(`Failed to read logs: ${t.message}`),process.exitCode=1}}),r.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(e)=>{try{let t=await P(),n=e?.env||"production",{region:i,functions:o}=bt(t,n),s=new qt(i),c=rc(e?.since,60),l=new Date,d=new Date(l.getTime()-c),u=Math.max(60,Math.round(c/1000));w(`Metrics — ${t.project.slug} (${n}), last ${e?.since??"1h"}`);let m=[];for(let f of["http","queue","cli"]){let h=[{Name:"FunctionName",Value:o[f]}],S=async(T,D)=>{let E=await s.getMetricStatistics({Namespace:"AWS/Lambda",MetricName:T,Dimensions:h,StartTime:d,EndTime:l,Period:u,Statistics:[D]});if(!E.length)return 0;if(D==="Sum")return E.reduce((N,_)=>N+(_.Sum??0),0);if(D==="Maximum")return Math.max(...E.map((N)=>N.Maximum??0));return E.reduce((N,_)=>N+(_.Average??0),0)/E.length},[y,v,R,b,A]=await Promise.all([S("Invocations","Sum"),S("Errors","Sum"),S("Throttles","Sum"),S("Duration","Average"),S("Duration","Maximum")]),k=y?`${(v/y*100).toFixed(2)}%`:"—";m.push([f,String(y),String(v),k,String(R),`${b.toFixed(0)}ms`,`${A.toFixed(0)}ms`])}x(["Function","Invocations","Errors","Error rate","Throttles","Avg","Max"],m)}catch(t){p(`Failed to read metrics: ${t.message}`),process.exitCode=1}}),r.command("metrics:dashboard","Print the CloudWatch console URL for this app").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).action(async(e)=>{try{let t=await P(),n=e?.env||"production",{region:i,functions:o}=bt(t,n),s=encodeURIComponent(JSON.stringify({metrics:["http","queue","cli"].flatMap((c)=>[["AWS/Lambda","Invocations","FunctionName",o[c]],["AWS/Lambda","Errors","FunctionName",o[c]]]),view:"timeSeries",region:i}));w("CloudWatch dashboard"),a(`https://console.aws.amazon.com/cloudwatch/home?region=${i}#metricsV2:graph=${s}`)}catch(t){p(t.message),process.exitCode=1}}),r.command("alarms","List CloudWatch alarms").option("--prefix <prefix>","Filter by alarm name prefix").option("--region <region>","AWS region").action(async(e)=>{w("CloudWatch Alarms");let t=e?.region;if(!t)try{t=(await P()).project.region}catch{}try{let i=await new qt(t||process.env.AWS_REGION||"us-east-1").describeAlarms({AlarmNamePrefix:e?.prefix,MaxRecords:100});if(!i.length){a("No alarms found");return}x(["Name","State","Metric","Condition"],i.map((o)=>[o.AlarmName??"-",o.StateValue??"-",o.MetricName??"-",`${o.ComparisonOperator??""} ${o.Threshold??""}`.trim()]))}catch(n){p(`Failed to list alarms: ${n.message}`),process.exitCode=1}}),r.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(e)=>{if(w("Creating CloudWatch Alarm"),!e?.name||e?.threshold==null){p("Missing required options: --name and --threshold"),process.exitCode=1;return}try{let t=await P(),n=e.env||"production",{region:i,functions:o}=bt(t,n),s=e.function??"http";await new qt(i).putMetricAlarm({AlarmName:e.name,Namespace:"AWS/Lambda",MetricName:e.metric??"Errors",ComparisonOperator:e.comparison??"GreaterThanThreshold",Threshold:Number(e.threshold),EvaluationPeriods:1,Period:300,Statistic:e.statistic??"Sum",Dimensions:[{Name:"FunctionName",Value:o[s]}],AlarmDescription:`ts-cloud: ${o[s]} ${e.metric} ${e.comparison} ${e.threshold}`}),g(`Alarm '${e.name}' created on ${o[s]} ${e.metric}`)}catch(t){p(`Failed to create alarm: ${t.message}`),process.exitCode=1}})}async function Hn(r){let e=await P(),t=r||"production";return{region:e.environments?.[t]?.region||e.project.region||"us-east-1",prefix:`${e.project.slug}/${t}`}}function vi(r){r.command("secrets:list","List secrets for an environment").option("--env <environment>","Environment (production, staging, development)").action(async(e)=>{w("Secrets");try{let{region:t,prefix:n}=await Hn(e?.env),i=new pt(t),{SecretList:o=[]}=await i.listSecrets({Filters:[{Key:"name",Values:[`${n}/`]}],MaxResults:100});if(!o.length){a(`No secrets found under ${n}/`);return}x(["Key","Last Changed"],o.map((s)=>[(s.Name??"").replace(`${n}/`,""),s.LastChangedDate??s.LastAccessedDate??"-"]))}catch(t){p(`Failed to list secrets: ${t.message}`),process.exitCode=1}}),r.command("secrets:set <key> <value>","Set (create or update) a secret").option("--env <environment>","Environment (production, staging, development)").action(async(e,t,n)=>{w(`Setting secret ${e}`);let{region:i,prefix:o}=await Hn(n?.env),s=new pt(i),c=`${o}/${e}`,l=new C("Storing secret...");l.start();try{try{await s.putSecretValue({SecretId:c,SecretString:t})}catch{await s.createSecret({Name:c,SecretString:t})}l.succeed(`Secret ${e} stored (${c})`),I("Redeploy for the new value to take effect in running functions.")}catch(d){l.fail(`Failed to store secret: ${d.message}`),process.exitCode=1}}),r.command("secrets:get <key>","Reveal a secret value").option("--env <environment>","Environment (production, staging, development)").action(async(e,t)=>{try{let{region:n,prefix:i}=await Hn(t?.env),s=await new pt(n).getSecretValue({SecretId:`${i}/${e}`});a(s.SecretString??"")}catch(n){p(`Failed to read secret: ${n.message}`),process.exitCode=1}}),r.command("secrets:delete <key>","Delete a secret").option("--env <environment>","Environment (production, staging, development)").option("--force","Delete immediately without a recovery window").action(async(e,t)=>{try{let{region:n,prefix:i}=await Hn(t?.env);await new pt(n).deleteSecret({SecretId:`${i}/${e}`,ForceDeleteWithoutRecovery:t?.force}),g(`Secret ${e} deleted`)}catch(n){p(`Failed to delete secret: ${n.message}`),process.exitCode=1}})}function bi(r){for(let[e,t]of[["firewall:rules","List provider firewall policy"],["firewall:block <ip>","Block an address"],["firewall:unblock <ip>","Unblock an address"],["firewall:countries","Manage geographic policy"]])r.command(e,t).action(async(...n)=>be(e.split(" ")[0],{target:String(n[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 wi(r){r.command("ssl:list","List all SSL certificates").option("--region <region>","AWS region (default: us-east-1)").action(async(e)=>{w("SSL Certificates");let t=e?.region||"us-east-1",n=new C("Fetching certificates from ACM...");n.start();try{let i=new vt(t),o=await i.listCertificates();if(o.CertificateSummaryList.length===0){n.succeed("No certificates found"),a(`
333
+ No SSL certificates found in region ${t}`),a("Use 'cloud domain:ssl <domain>' to request a new certificate");return}let s=await Promise.all(o.CertificateSummaryList.map(async(d)=>await i.describeCertificate({CertificateArn:d.CertificateArn})));n.succeed(`Found ${s.length} certificate(s)`);let c=(d)=>{if(!d)return"N/A";let u=typeof d==="string"?Number.parseFloat(d):d,m=u<1000000000000?u*1000:u;return new Date(m).toISOString().split("T")[0]},l=s.map((d)=>{let u=c(d.NotAfter),m=d.Type==="AMAZON_ISSUED"?"Amazon Issued":d.Type||"Unknown",f=d.Status==="ISSUED"?"Available":d.Status||"Unknown";return[d.DomainName,d.Status||"Unknown",u,m,f]});x(["Domain","Status","Expiry","Type","State"],l),a(`
334
+ ACM certificates are automatically renewed by AWS`),a(`Region: ${t}`)}catch(i){n.fail("Failed to fetch certificates"),p(i.message)}}),r.command("ssl:renew <domain>","Renew SSL certificate").option("--region <region>","AWS region (default: us-east-1)").action(async(e,t)=>{w(`Checking SSL Certificate for ${e}`);let n=t?.region||"us-east-1";a(`Domain: ${e}`),a(`Region: ${n}`);let i=new C("Checking certificate status...");i.start();try{let s=await new vt(n).findCertificateByDomain(e);if(!s){i.fail("Certificate not found"),p(`No certificate found for domain: ${e}`),a("Use 'cloud domain:ssl <domain>' to request a new certificate");return}i.succeed("Certificate found"),a(`
335
+ Certificate is managed by AWS Certificate Manager`),a("ACM certificates are automatically renewed 60 days before expiry"),I(`
336
+ No manual renewal needed for ACM certificates`);let c=(u)=>{if(!u)return"N/A";let m=typeof u==="string"?Number.parseFloat(u):u,f=m<1000000000000?m*1000:m;return new Date(f).toISOString().split("T")[0]},l=c(s.NotAfter),d=c(s.IssuedAt);if(a(`
337
+ Certificate details:`),a(` - Domain: ${s.DomainName}`),a(` - Status: ${s.Status}`),a(` - Issued: ${d}`),a(` - Expiry: ${l}`),a(` - Type: ${s.Type||"Unknown"}`),a(` - ARN: ${s.CertificateArn}`),a(` - Auto-renewal: ${s.Type==="AMAZON_ISSUED"?"Enabled":"N/A (imported)"}`),s.SubjectAlternativeNames&&s.SubjectAlternativeNames.length>1)a(` - SANs: ${s.SubjectAlternativeNames.join(", ")}`)}catch(o){i.fail("Failed to check certificate"),p(o.message)}})}function wt(r){return r.toISOString().slice(0,10)}function ic(r){return new Date(Date.UTC(r.getUTCFullYear(),r.getUTCMonth(),r.getUTCDate()))}function oc(r=new Date){let e=ic(r),t=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth(),1)),n=new Date(e.getTime()+86400000),i=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth()+1,1)),o=t,s=new Date(Date.UTC(e.getUTCFullYear(),e.getUTCMonth()-1,1));return{start:wt(t),end:wt(n),label:t.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"}),daysElapsed:e.getUTCDate(),daysInMonth:Math.round((i.getTime()-t.getTime())/86400000),previous:{start:wt(s),end:wt(o),label:s.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"})}}}function Gt(r,e=new Date){if(!Number.isInteger(r)||r<1||r>366)throw Error("days must be an integer from 1 to 366");let t=new Date(ic(e).getTime()+86400000),n=new Date(t.getTime()-r*86400000),i=new Date(n.getTime()-r*86400000);return{current:{start:wt(n),end:wt(t),label:`last ${r} days`},previous:{start:wt(i),end:wt(n),label:`previous ${r} days`}}}function Si(r,e,t){return e>0?r/e*t:0}function gn(r,e){if(e===0)return r===0?0:null;return(r-e)/e*100}function $i(r,e){let t=new Map(e.map((n)=>[n.service,n.amount]));return r.map((n)=>{let i=t.get(n.service)??0;return{...n,previousAmount:i,changePercent:gn(n.amount,i)}})}var pm=/(?:data.?transfer|natgateway.*bytes|aws-(?:in|out)-bytes|regional-bytes|inter.?az|internet)/i;function Jn(r){return r.filter((e)=>pm.test(e.key)).sort((e,t)=>t.amount-e.amount)}function Kt(r){if(r==null)return[];return Array.isArray(r)?r:[r]}function xt(r,e={}){let[,,t="unknown",n="",i="",o=r]=r.split(":"),s=o.includes("/")?"/":":",[c=t,...l]=o.split(s),d=l.join(s)||o;return{arn:r,service:t,type:c,id:d,name:e.Name||e.name||d,region:n||void 0,accountId:i||void 0,tags:e,metadata:{}}}function fm(r,e){if(!e)return!0;let t=e.toLowerCase().replace(/^aws[:/]/,"");return[r.service,r.type,`${r.service}:${r.type}`].some((n)=>n.toLowerCase()===t||n.toLowerCase().includes(t))}function gm(r,e){let t=new Map;for(let n of r){let i=`${n.service}:${n.type}:${n.id}`,o=t.get(i);t.set(i,o?{...o,...n,arn:o.accountId?o.arn:n.arn,accountId:o.accountId??n.accountId,tags:{...o.tags,...n.tags},metadata:{...o.metadata,...n.metadata}}:n)}return[...t.values()].filter((n)=>fm(n,e)).sort((n,i)=>n.service.localeCompare(i.service)||n.name.localeCompare(i.name)||n.arn.localeCompare(i.arn))}class hn{client;profile;region;constructor(r,e=process.env.AWS_REGION||"us-east-1"){this.profile=r,this.region=e,this.client=new Ci(sc(r))}async taggedResources(){let r=[],e;do{let t=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(e?{PaginationToken:e}:{})});for(let n of t?.ResourceTagMappingList??[]){let i=Object.fromEntries((n.Tags??[]).map((o)=>[String(o.Key),String(o.Value)]));r.push(xt(String(n.ResourceARN),i))}e=t?.PaginationToken||void 0}while(e);return r}async ec2Resources(){let r=async(o)=>this.client.request({service:"ec2",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({Action:o,Version:"2016-11-15"}).toString()}),[e,t,n]=await Promise.all([r("DescribeInstances"),r("DescribeVolumes"),r("DescribeAddresses")]),i=[];for(let o of Kt(e?.reservationSet?.item))for(let s of Kt(o?.instancesSet?.item)){let c=String(s.instanceId);i.push({...xt(`arn:aws:ec2:${this.region}::instance/${c}`),state:s.instanceState?.name,metadata:{instanceType:s.instanceType}})}for(let o of Kt(t?.volumeSet?.item)){let s=String(o.volumeId);i.push({...xt(`arn:aws:ec2:${this.region}::volume/${s}`),state:o.status,metadata:{sizeGiB:Number(o.size??0),attached:Kt(o.attachmentSet?.item).length>0}})}for(let o of Kt(n?.addressesSet?.item)){let s=String(o.allocationId??o.publicIp);i.push({...xt(`arn:aws:ec2:${this.region}::elastic-ip/${s}`),state:o.associationId?"associated":"available",metadata:{associated:Boolean(o.associationId),publicIp:o.publicIp}})}return i}async rdsResources(){let r=new URLSearchParams({Action:"DescribeDBInstances",Version:"2014-10-31"}).toString(),e=await this.client.request({service:"rds",region:this.region,method:"POST",path:"/",headers:{"content-type":"application/x-www-form-urlencoded"},body:r}),t=e?.DescribeDBInstancesResult??e;return Kt(t?.DBInstances?.DBInstance).map((n)=>({...xt(n.DBInstanceArn||`arn:aws:rds:${this.region}::db:${String(n.DBInstanceIdentifier)}`),state:n.DBInstanceStatus,metadata:{engine:n.Engine,instanceClass:n.DBInstanceClass}}))}async lambdaResources(){let r=[],e;do{let t=await this.client.request({service:"lambda",region:this.region,method:"GET",path:"/2015-03-31/functions",queryParams:e?{Marker:e}:void 0});for(let n of t?.Functions??[])r.push({...xt(n.FunctionArn),state:n.State,metadata:{runtime:n.Runtime,memoryMiB:Number(n.MemorySize??0)}});e=t?.NextMarker||void 0}while(e);return r}async s3Resources(){return(await new de("us-east-1",this.profile).listBuckets()).Buckets.map((e)=>({...xt(`arn:aws:s3:::${e.Name}`),metadata:{creationDate:e.CreationDate}}))}async discover(r){let e=[["tagging:GetResources",()=>this.taggedResources()],["ec2:Describe*",()=>this.ec2Resources()],["rds:DescribeDBInstances",()=>this.rdsResources()],["lambda:ListFunctions",()=>this.lambdaResources()],["s3:ListAllMyBuckets",()=>this.s3Resources()]],t=await Promise.allSettled(e.map(([,o])=>o())),n=[],i=[];return t.forEach((o,s)=>{if(o.status==="fulfilled")n.push(...o.value);else i.push(`${e[s][0]} unavailable: ${o.reason instanceof Error?o.reason.message:String(o.reason)}`)}),{resources:gm(n,r?.type),warnings:i}}}function yn(r,e){if(r==null)return{currentCost:null,projectedCost:null,monthlySavings:null};let t=r*e;return{currentCost:r,projectedCost:r-t,monthlySavings:t}}function ac(r,e,t,n=!1){let i=Ai(r,e,t);if(i)return[{category:"unused",resource:r,recommendation:i.recommendation,sourceSignal:i.signal,...yn(t,1)}];if((r.service==="ec2"&&r.type==="instance"||r.service==="rds"&&r.type==="db")&&e.cpuAverage!=null&&e.cpuAverage<20)return[{category:"right-size",resource:r,recommendation:"Validate memory, network, and peak demand, then test one smaller instance class.",sourceSignal:`${r.service.toUpperCase()} 30-day average CPU ${e.cpuAverage.toFixed(1)}% (<20%)`,...yn(t,0.4)}];if(n&&(r.service==="ec2"&&r.type==="instance"&&r.state==="running"||r.service==="rds"&&r.type==="db"&&r.state==="available")&&e.cpuAverage!=null&&e.cpuAverage>=20)return[{category:"commitment",resource:r,recommendation:"Compare a one-year no-upfront commitment against this steady workload before purchasing.",sourceSignal:`${r.service.toUpperCase()} remained active with 30-day average CPU ${e.cpuAverage.toFixed(1)}%`,...yn(t,0.3)}];if(r.service==="s3"&&e.storageBytes!=null&&e.storageBytes>=5368709120&&e.requests!=null&&e.requests<9000){let o=e.storageBytes/1073741824;return[{category:"storage",resource:r,recommendation:"Review object age and retrieval needs, then add Intelligent-Tiering or Glacier lifecycle rules.",sourceSignal:`${o.toFixed(1)} GiB stored with ${e.requests.toFixed(0)} requests over 90 days`,...yn(t,0.25)}]}return[]}function hm(r,e){if(r<=0||e.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: ${e.slice(0,3).join(", ")}`,...yn(r,0.2)}}function ym(r){let e=r.map((t)=>t.Average).filter((t)=>t!=null);return e.length>0?e.reduce((t,n)=>t+n,0)/e.length:void 0}function vm(r){let e=r.map((t)=>t.Sum).filter((t)=>t!=null);return e.length>0?e.reduce((t,n)=>t+n,0):void 0}function Ai(r,e,t=null){if(r.service==="ec2"&&r.type==="volume"&&r.metadata.attached===!1)return{resource:r,signal:"EBS volume is detached",recommendation:"Snapshot if needed, then delete the detached volume.",monthlySavings:t};if(r.service==="ec2"&&r.type==="elastic-ip"&&r.metadata.associated===!1)return{resource:r,signal:"Elastic IP is not associated",recommendation:"Release the unassociated Elastic IP.",monthlySavings:t};if(r.service==="ec2"&&r.type==="instance"&&e.cpuAverage!=null&&e.cpuAverage<5)return{resource:r,signal:`30-day average CPU ${e.cpuAverage.toFixed(1)}% (<5%)`,recommendation:"Stop/delete if unused, or right-size after validating memory and network demand.",monthlySavings:t};if(r.service==="rds"&&r.type==="db"&&e.connectionsAverage!=null&&e.iopsAverage!=null&&e.connectionsAverage===0&&e.iopsAverage<1)return{resource:r,signal:`14-day connections 0 and average IOPS ${e.iopsAverage.toFixed(2)} (<1)`,recommendation:"Take a final snapshot, validate ownership, then stop or delete the database.",monthlySavings:t};if(r.service==="lambda"&&r.type==="function"&&e.invocations===0)return{resource:r,signal:"30-day invocations 0",recommendation:"Confirm no event source depends on it, then remove the function.",monthlySavings:t};if(r.service==="s3"&&e.objectCount===0&&e.requests===0)return{resource:r,signal:"Bucket is empty with zero requests over 90 days",recommendation:"Confirm retention policy, then delete the empty bucket.",monthlySavings:t};if(r.service==="elasticache"&&e.connectionsAverage!=null&&e.cacheHitRate!=null&&e.connectionsAverage===0&&e.cacheHitRate<20)return{resource:r,signal:`14-day connections 0 and cache hit rate ${e.cacheHitRate.toFixed(1)}%`,recommendation:"Validate clients and snapshots, then delete the idle cache cluster.",monthlySavings:t};return null}class Yn{inventory;costs;profile;region;constructor(r,e=process.env.AWS_REGION||"us-east-1"){this.profile=r,this.region=e,this.inventory=new hn(r,e),this.costs=new Et(r)}async metric(r,e,t,n,i,o,s,c=[]){let l=new Date,d=new Date(l.getTime()-o*24*60*60*1000),u=await r.getMetricStatistics({Namespace:t,MetricName:n,Dimensions:[{Name:i,Value:e.id},...c],StartTime:d,EndTime:l,Period:86400,Statistics:[s]});return s==="Sum"?vm(u):ym(u)}async signals(r){let e=new qt(r.region??this.region,this.profile);if(r.service==="ec2"&&r.type==="instance")return{cpuAverage:await this.metric(e,r,"AWS/EC2","CPUUtilization","InstanceId",30,"Average")};if(r.service==="rds"&&r.type==="db"){let[t,n,i,o]=await Promise.all([this.metric(e,r,"AWS/RDS","CPUUtilization","DBInstanceIdentifier",30,"Average"),this.metric(e,r,"AWS/RDS","DatabaseConnections","DBInstanceIdentifier",14,"Average"),this.metric(e,r,"AWS/RDS","ReadIOPS","DBInstanceIdentifier",14,"Average"),this.metric(e,r,"AWS/RDS","WriteIOPS","DBInstanceIdentifier",14,"Average")]);return{cpuAverage:t,connectionsAverage:n,iopsAverage:i!=null&&o!=null?i+o:void 0}}if(r.service==="lambda"&&r.type==="function")return{invocations:await this.metric(e,r,"AWS/Lambda","Invocations","FunctionName",30,"Sum")};if(r.service==="s3"){let[t,n,i]=await Promise.all([this.metric(e,r,"AWS/S3","NumberOfObjects","BucketName",90,"Average",[{Name:"StorageType",Value:"AllStorageTypes"}]),this.metric(e,r,"AWS/S3","AllRequests","BucketName",90,"Sum"),this.metric(e,r,"AWS/S3","BucketSizeBytes","BucketName",90,"Average",[{Name:"StorageType",Value:"StandardStorage"}])]);return{objectCount:t,requests:n,storageBytes:i}}if(r.service==="elasticache"&&r.type.includes("cluster")){let[t,n]=await Promise.all([this.metric(e,r,"AWS/ElastiCache","CurrConnections","CacheClusterId",14,"Average"),this.metric(e,r,"AWS/ElastiCache","CacheHitRate","CacheClusterId",14,"Average")]);return{connectionsAverage:t,cacheHitRate:n}}return{}}async resourceCosts(){try{let r=Gt(30).current,e=await this.costs.getCostByDimension({start:r.start,end:r.end,dimension:"RESOURCE_ID",granularity:"DAILY"});return new Map(e.map((t)=>[t.key,t.amount]))}catch{return new Map}}resourceCost(r,e){return r.get(e.id)??r.get(e.arn)??null}async cloudFrontTransfer(){try{let r=Gt(30).current,e=await this.costs.getCostByDimension({start:r.start,end:r.end,dimension:"USAGE_TYPE",granularity:"DAILY",filter:{Dimensions:{Key:"SERVICE",Values:["Amazon CloudFront"]}}}),t=Jn(e);return{cost:t.reduce((n,i)=>n+i.amount,0),usageTypes:t.map((n)=>n.key)}}catch{return{cost:0,usageTypes:[]}}}async unused(r){let[e,t]=await Promise.all([this.inventory.discover(r),this.resourceCosts()]),n=(await Promise.all(e.resources.map(async(i)=>{let o=this.resourceCost(t,i),s=Ai(i,{},o);if(s)return s;try{return Ai(i,await this.signals(i),o)}catch{return null}}))).filter((i)=>i!==null);return{findings:n,inventory:e,savingsAvailable:n.some((i)=>i.monthlySavings!=null)}}async optimize(r){let e=!r?.type||["cloudfront","cdn"].includes(r.type.toLowerCase()),[t,n,i]=await Promise.all([this.inventory.discover({type:r?.type}),this.resourceCosts(),e?this.cloudFrontTransfer():Promise.resolve({cost:0,usageTypes:[]})]),o=[],s=[...t.warnings];for(let l of t.resources){let d=this.resourceCost(n,l),u=ac(l,{},d,r?.includeCommitments);if(u.length>0){o.push(...u);continue}try{o.push(...ac(l,await this.signals(l),d,r?.includeCommitments))}catch(m){s.push(`CloudWatch metrics unavailable for ${l.service}:${l.type}/${l.name}: ${m instanceof Error?m.message:String(m)}`)}}let c=hm(i.cost,i.usageTypes);if(c)o.push(c);return o.sort((l,d)=>(d.monthlySavings??-1)-(l.monthlySavings??-1)||l.category.localeCompare(d.category)||l.resource.name.localeCompare(d.resource.name)),{recommendations:o,inventory:t,savingsAvailable:o.some((l)=>l.monthlySavings!=null),warnings:s}}}var ki="Amazon Simple Storage Service";function bm(){let r=new Date,e=new Date(Date.UTC(r.getUTCFullYear(),r.getUTCMonth(),1)),t=new Date(e);t.setUTCMonth(t.getUTCMonth()-1);let n=(o)=>o.toISOString().slice(0,10),i=t.toLocaleString("en-US",{month:"long",year:"numeric",timeZone:"UTC"});return{start:n(t),end:n(e),label:i}}function se(r){return`$${r.toFixed(2)}`}function Zn(r){if(r===null)return"new";if(Math.abs(r)<0.05)return"→ 0.0%";return`${r>0?"↑":"↓"} ${Math.abs(r).toFixed(1)}%`}function Ri(r){p(`Cost Explorer request failed: ${r instanceof Error?r.message:String(r)}`),a(`
338
+ This command needs the ce:GetCostAndUsage IAM permission.`)}function wm(r){let{label:e,profile:t,rows:n,total:i,count:o}=r,s=`# AWS Cost Analysis — ${e}`,c=t?`
339
+ _Profile: \`${t}\`_
330
340
  `:`
331
- `,F=`| Service | Resources | Cost | % of Total |
332
- |---|---|---|---|`,U=Y.map(([H,j,V,T])=>`| ${H} | ${j} | ${V} | ${T} |`).join(`
333
- `),M=`
334
- **Total: ${G$(X)} across ${Q} service${Q===1?"":"s"}**`;return`${Z}
335
- ${z}
341
+ `,l=`| Service | Resources | Cost | % of Total |
342
+ |---|---|---|---|`,d=n.map(([m,f,h,S])=>`| ${m} | ${f} | ${h} | ${S} |`).join(`
343
+ `),u=`
344
+ **Total: ${se(i)} across ${o} service${o===1?"":"s"}**`;return`${s}
345
+ ${c}
336
346
  | Service | Resources | Cost | % of Total |
337
347
  |---|---|---|---|
338
- ${U}
339
- ${M}
340
- `}function MJ(G){G.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($)=>{let J=$?.profile,Y=$?.cache!==!1,{start:X,end:Q,label:Z}=hG();R(`Cost Analysis — ${Z}${J?` (profile: ${J})`:""}`);let z=new E("Querying AWS Cost Explorer...");z.start();let F=new N0(J,{useCache:Y}),U;try{U=await F.getCostByService({start:X,end:Q})}catch(A){z.stop(),K(`Cost Explorer request failed: ${A?.message??A}`),W(`
341
- This command needs the ce:GetCostAndUsage IAM permission.`);return}let M=null,H=!1;if(U.some((A)=>A.service===FJ))try{M=(await new Z$("us-east-1",J).listBuckets()).Buckets?.length??0}catch{H=!0}if(z.stop(),U.length===0){W("No billed services in this period.");return}let j=U.reduce((A,q)=>A+q.amount,0),V=U.map((A)=>{let q="-";if(A.service===FJ){if(H)q="unknown (no s3:ListAllMyBuckets)";else if(M!==null)q=`${M} bucket${M===1?"":"s"}`}let O=j>0?`${(A.amount/j*100).toFixed(1)}%`:"—";return[A.service,q,G$(A.amount),O]});if(w(["Service","Resources","Cost","% of Total"],V),W(`
342
- Total: ${G$(j)} across ${U.length} service${U.length===1?"":"s"}`),F.lastCacheAgeSeconds!==null)W(`(cached, ${F.lastCacheAgeSeconds}s old — pass --no-cache to refresh)`);if((U.find((A)=>A.service===FJ)?.amount??0)>1&&M===0)N(`
343
- 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($?.output){let A=`${process.cwd()}/aws.md`;await Bun.write(A,yG({label:Z,profile:J,rows:V,total:j,count:U.length})),B(`
344
- Wrote ${A}`)}}),G.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($)=>{let J=$?.all?void 0:$?.profile,Y=S9(J),X=P9(J);W(`Cleared ${X.deletedFiles} cached response${X.deletedFiles===1?"":"s"} for ${X.scope}`),W(`(${Y})`)}),G.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($)=>{let J=D9(),Y=new N0($?.profile,{useCache:$?.cache!==!1});R(`AWS Cost — ${J.label}${$?.profile?` (profile: ${$.profile})`:""}`);let X=new E("Querying month-to-date AWS spend...");X.start();try{let[Q,Z]=await Promise.all([Y.getCostByService({start:J.start,end:J.end,granularity:"DAILY"}),Y.getCostByService({start:J.previous.start,end:J.previous.end,granularity:"MONTHLY"})]);X.stop();let F=ZJ(Q,Z).map((j)=>{let V=QJ(j.amount,J.daysElapsed,J.daysInMonth);return[j.service,G$(j.amount),G$(V),G$(j.previousAmount),m1(Q1(V,j.previousAmount))]});w(["Service","MTD","Projected",J.previous.label,"Change"],F);let U=Q.reduce((j,V)=>j+V.amount,0),M=Z.reduce((j,V)=>j+V.amount,0),H=QJ(U,J.daysElapsed,J.daysInMonth);W(`
345
- MTD: ${G$(U)} through day ${J.daysElapsed} of ${J.daysInMonth}`),W(`Projected: ${G$(H)} (${m1(Q1(H,M))} vs ${J.previous.label})`)}catch(Q){X.stop(),UJ(Q)}}),G.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($)=>{let J=Number.parseInt($?.days??"30",10),Y;try{Y=g0(J)}catch(Z){K(Z instanceof Error?Z.message:String(Z));return}let X=new N0($?.profile,{useCache:$?.cache!==!1});R(`AWS Cost Breakdown — ${Y.current.label}${$?.profile?` (profile: ${$.profile})`:""}`);let Q=new E("Comparing AWS service spend...");Q.start();try{let[Z,z]=await Promise.all([X.getCostByService({start:Y.current.start,end:Y.current.end,granularity:"DAILY"}),X.getCostByService({start:Y.previous.start,end:Y.previous.end,granularity:"DAILY"})]);Q.stop();let F=ZJ(Z,z);w(["Service",`Last ${J}d`,`Previous ${J}d`,"Trend"],F.map((H)=>[H.service,G$(H.amount),G$(H.previousAmount),m1(H.changePercent)]));let U=Z.reduce((H,j)=>H+j.amount,0),M=z.reduce((H,j)=>H+j.amount,0);W(`
346
- Total: ${G$(U)} (${m1(Q1(U,M))})`)}catch(Z){Q.stop(),UJ(Z)}}),G.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($)=>{let J=Number.parseInt($?.days??"30",10),Y;try{Y=g0(J).current}catch(Z){K(Z instanceof Error?Z.message:String(Z));return}let X=new N0($?.profile,{useCache:$?.cache!==!1});R(`AWS Egress Cost — ${Y.label}${$?.profile?` (profile: ${$.profile})`:""}`);let Q=new E("Querying data-transfer usage types...");Q.start();try{let Z=g1(await X.getCostByDimension({start:Y.start,end:Y.end,dimension:"USAGE_TYPE",granularity:"DAILY"}));if(Q.stop(),Z.length===0){W("No billed data-transfer usage types were found in this period.");return}w(["Usage type","Cost"],Z.map((z)=>[z.key,G$(z.amount)])),W(`
347
- Total ranked egress: ${G$(Z.reduce((z,F)=>z+F.amount,0))}`),W("Usage types classify NAT, internet, inter-AZ, and inter-region transfer; destination correlation requires VPC Flow Logs.")}catch(Z){Q.stop(),UJ(Z)}}),G.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($)=>{R(`AWS Resources${$?.profile?` (profile: ${$.profile})`:""}`);let J=new E("Discovering tagged and untagged resources...");J.start();let Y=new Z1($?.profile,$?.region);try{let X=await Y.discover({type:$?.type});if(J.stop(),X.resources.length===0)W("No matching resources were discovered.");else w(["Type","Name","Region","State","Monthly cost"],X.resources.map((Q)=>[`${Q.service}:${Q.type}`,Q.name,Q.region??"global",Q.state??"unknown","— (requires CUR resource IDs)"])),W(`
348
- ${X.resources.length} resource${X.resources.length===1?"":"s"} discovered.`);for(let Q of X.warnings)N(Q)}catch(X){J.stop(),K(`Resource discovery failed: ${X instanceof Error?X.message:String(X)}`)}}),G.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($)=>{R(`Unused AWS Resources${$?.profile?` (profile: ${$.profile})`:""}`);let J=new E("Evaluating inventory and CloudWatch idle signals...");J.start();try{let Y=await new v1($?.profile,$?.region).unused({type:$?.type});if(J.stop(),Y.findings.length===0)W("No resources met the conservative unused-resource thresholds.");else{w(["Resource","Signal","Recommendation","Monthly savings"],Y.findings.map((Q)=>[`${Q.resource.service}:${Q.resource.type}/${Q.resource.name}`,Q.signal,Q.recommendation,Q.monthlySavings==null?"—":G$(Q.monthlySavings)]));let X=Y.findings.reduce((Q,Z)=>Q+(Z.monthlySavings??0),0);W(`
349
- Known monthly savings: ${Y.savingsAvailable?G$(X):"unavailable (enable CUR resource IDs)"}`)}for(let X of Y.inventory.warnings)N(X)}catch(Y){J.stop(),K(`Unused-resource analysis failed: ${Y instanceof Error?Y.message:String(Y)}`)}}),G.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($)=>{R(`AWS Cost Optimizations${$?.profile?` (profile: ${$.profile})`:""}`);let J=new E("Combining inventory, CloudWatch signals, and Cost Explorer line items...");J.start();try{let Y=await new v1($?.profile,$?.region).optimize({type:$?.type,includeCommitments:$?.includeCommitments});if(J.stop(),Y.recommendations.length===0)W("No resources met the conservative optimization thresholds.");else{w(["Category","Resource","Current","Projected","Savings","Source signal","Recommendation"],Y.recommendations.map((Q)=>[Q.category,`${Q.resource.service}:${Q.resource.type}/${Q.resource.name}`,Q.currentCost==null?"—":G$(Q.currentCost),Q.projectedCost==null?"—":G$(Q.projectedCost),Q.monthlySavings==null?"—":G$(Q.monthlySavings),Q.sourceSignal,Q.recommendation]));let X=Y.recommendations.reduce((Q,Z)=>Q+(Z.monthlySavings??0),0);W(`
350
- Estimated monthly savings: ${Y.savingsAvailable?G$(X):"unavailable (enable CUR resource IDs)"}`)}for(let X of Y.warnings)N(X);W("Estimates are directional; validate workload peaks and current AWS pricing before applying changes.")}catch(Y){J.stop(),K(`Optimization analysis failed: ${Y instanceof Error?Y.message:String(Y)}`)}})}async function m0(G){let $=await x(),J=G??Object.keys($.environments??{})[0]??"production";if(!Object.hasOwn($.environments??{},J))throw Error(`Environment ${J} was not found`);let Y=u(process.cwd(),$);return{config:$,env:J,controlPlane:Y,controls:new z1(Y.store)}}function d0(G){K(G instanceof Error?G.message:String(G)),process.exitCode=1}function _9(G){let $=G?.infrastructure?.compute??{};return{ddos:$.ddos===!1?void 0:typeof $.ddos==="object"?$.ddos:{ports:[80,443]},waf:$.waf===!1?void 0:typeof $.waf==="object"?$.waf:{mode:"detection"}}}function AJ(G){G.command("protect:status","Show the current edge-protection posture").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async($)=>{try{let J=await m0($.env),Y=J.controls.current(),X=jJ(Y);if($.json){W(JSON.stringify({posture:X,controls:J.controls.raw()},null,2));return}if(R("Edge protection"),X.mitigationPaused||X.attackMode)N(X.summary);else B(X.summary);let Q=_9(J.config);if(W(`L3/L4 mitigation: ${Q.ddos?"enabled":"disabled in config"}`),W(`WAF: ${Q.waf?`enabled (${Q.waf.mode??"detection"})`:"disabled in config"}`),W(`Allowlist: ${Y.ipRules.allow.length} entry(s)`),W(`Blocklist: ${Y.ipRules.block.length} entry(s)`),Y.ipRules.allow.length>0||Y.ipRules.block.length>0)w(["List","CIDR"],[...Y.ipRules.allow.map((Z)=>["allow",Z]),...Y.ipRules.block.map((Z)=>["block",Z])])}catch(J){d0(J)}}),G.command("protect:attack-mode <state>","Challenge every visitor for a bounded window (on|off)").option("--hours <hours>",`How long before it lifts itself (default ${KJ}, max ${d1})`).option("--reason <reason>","Why, for the record").option("--env <environment>","Target environment").action(async($,J)=>{try{let Y=await m0(J.env);if($==="off"){B(Y.controls.disableAttackMode()?"Attack mode is off.":"Attack mode was not on.");return}if($!=="on")throw Error("State must be 'on' or 'off'.");let X=Y.controls.enableAttackMode({hours:J.hours==null?void 0:Number(J.hours),reason:J.reason??"Enabled from the CLI."});N("Attack mode is ON. Every visitor is challenged, including legitimate ones."),W(`It lifts itself at ${X.expiresAt} — you do not need to remember.`)}catch(Y){d0(Y)}}),G.command("protect:pause <state>","Pause or resume automatic mitigation (on|off)").option("--hours <hours>",`How long (max ${d1})`).option("--reason <reason>","Why — required when pausing").option("--env <environment>","Target environment").action(async($,J)=>{try{let Y=await m0(J.env);if($==="off"){B(Y.controls.resumeMitigations()?"Automatic mitigation resumed.":"Mitigation was not paused.");return}if($!=="on")throw Error("State must be 'on' or 'off'.");if(!J.reason)throw Error("--reason is required: you are liable for the traffic this admits.");let X=Y.controls.pauseMitigations({hours:J.hours==null?void 0:Number(J.hours),reason:J.reason});N("Automatic mitigation is PAUSED. All traffic is served, and billed, including attacks."),W(`It resumes automatically at ${X.expiresAt}.`)}catch(Y){d0(Y)}}),G.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($,J)=>{try{let Y=await m0(J.env),X=J.remove?Y.controls.removeIpRule("allow",$):Y.controls.addIpRule("allow",$);B(`Allowlist now has ${X.allow.length} entry(s).`),W("It reaches the boxes on the next deploy. `cloud protect:apply` shows exactly what will land.")}catch(Y){d0(Y)}}),G.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($,J)=>{try{let Y=await m0(J.env),X=J.remove?Y.controls.removeIpRule("block",$):Y.controls.addIpRule("block",$);B(`Blocklist now has ${X.block.length} entry(s).`),W("It reaches the boxes on the next deploy. `cloud protect:apply` shows exactly what will land.")}catch(Y){d0(Y)}}),G.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($)=>{try{let J=await m0($.env),Y=_9(J.config);if($.waf){if(!Y.waf)throw Error("The WAF is disabled in this configuration.");let X=VJ(Y.waf);for(let Q of X.warnings)N(Q);W(X.config);return}if(!Y.ddos)throw Error("L3/L4 mitigation is disabled in this configuration.");W(HJ(BJ(Y.ddos,J.controls.current())))}catch(J){d0(J)}})}async function R$(G){let $=await x(),J=G??Object.keys($.environments??{})[0]??"production";if(!Object.hasOwn($.environments??{},J))throw Error(`Environment ${J} was not found`);let Y=u(process.cwd(),$),X=Y.environments.get(J),Q=new y9(Y.store),Z=new F1(Y.store,{encryptionKey:Q$(process.cwd())});return{config:$,env:J,controlPlane:Y,environmentRecord:X,store:Q,alerts:Z,service:new f9(Q),gate:new g9(Y.store),organizationId:Y.organization.id,projectId:Y.project.id}}function c1(G){if(G==null)return;let $=String(G).trim().replace(/[$,\s]/g,"");if(!$)return;if(/^\d+c$/i.test($))return Number.parseInt($,10);let J=Number($);if(!Number.isFinite(J)||J<0)throw Error(`Invalid amount: ${G}`);return Math.round(J*100)}function b9(G){if(!G)return;let $=G.split(",").map((J)=>J.trim()).filter(Boolean);for(let J of $)if(!qJ.includes(J))throw Error(`Unknown enforcement action '${J}'. Valid: ${qJ.join(", ")}`);return $}function I9(G){if(G==null)return"monthly";if(G==="daily"||G==="weekly"||G==="monthly")return G;throw Error(`Unknown period '${G}'. Valid: daily, weekly, monthly`)}function CG(G){return k9[G]?.label??G}function fG(G,$=24){let J=Math.max(0,Math.min($,Math.round(G/100*$)));return`${"█".repeat(J)}${"░".repeat($-J)}`}function L$(G){K(G instanceof Error?G.message:String(G)),process.exitCode=1}function EJ(G){G.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($)=>{try{let J=await R$($.env),Y=J.service.usageReport({organizationId:J.organizationId,projectId:J.projectId,period:I9($.period),timezone:$.timezone});if($.json){W(JSON.stringify(Y,null,2));return}if(R(`Usage — ${Y.window.label}`),W(`Total: ${d$(Y.totalCents,Y.currency)}`),Y.byMeter.length===0)W("No metered usage in this window yet.");else w(["Meter","Provider","Quantity","Cost"],Y.byMeter.map((X)=>[CG(X.meter),X.provider||"-",X.quantity.toLocaleString(void 0,{maximumFractionDigits:3}),d$(X.costCents,Y.currency)]));if(Y.budgets.length===0){N("No budgets are configured. Nothing is capping this project."),W("Create one with: cloud budget:create --name 'Monthly' --hard 200");return}if(R("Budgets"),w(["Name","Period","Spent","Limit","Used","Projected","Time to cap","State"],Y.budgets.map((X)=>[X.name+(X.dryRun?" (dry run)":""),X.period,d$(X.spentCents,Y.currency),X.limitCents==null?"-":d$(X.limitCents,Y.currency),`${fG(X.usedPercent)} ${X.usedPercent.toFixed(0)}%`,`${d$(X.projectedCents,Y.currency)} (${(X.projectionConfidence*100).toFixed(0)}% conf)`,X.timeToCap||"-",X.level])),Y.enforcement.strongestAction)N(`Enforcement in force: ${Y.enforcement.strongestAction}`)}catch(J){L$(J)}}),G.command("budget:list","List budgets governing this project").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async($)=>{try{let J=await R$($.env),Y=J.store.listBudgets({organizationId:J.organizationId,projectId:J.projectId});if($.json){W(JSON.stringify(Y,null,2));return}if(Y.length===0){N("No budgets configured.");return}w(["ID","Name","Scope","Period","Soft","Hard","Mode","Enabled"],Y.map((X)=>[X.id.slice(0,8),X.name,X.environmentId?"environment":X.projectId?"project":"organization",X.period,X.softLimitCents==null?"-":d$(X.softLimitCents,X.currency),X.hardLimitCents==null?"-":d$(X.hardLimitCents,X.currency),X.dryRun?"dry run":"enforcing",X.enabled?"yes":"no"]))}catch(J){L$(J)}}),G.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($)=>{try{if(!$.name)throw Error("--name is required.");let J=await R$($.env),Y=$.scope??"project";if(!["organization","project","environment"].includes(Y))throw Error(`Unknown scope '${Y}'. Valid: organization, project, environment`);let X=b9($.actions),Q=J.store.createBudget({organizationId:J.organizationId,projectId:Y==="organization"?void 0:J.projectId,environmentId:Y==="environment"?J.environmentRecord?.id:void 0,name:$.name,period:I9($.period),timezone:$.timezone,softLimitCents:c1($.soft),hardLimitCents:c1($.hard),graceSeconds:$.grace==null?void 0:Number($.grace),thresholds:X?[{atPercent:80,actions:["notify"]},{atPercent:100,actions:X}]:[...h9],dryRun:$.dryRun===!0});B(`Created budget '${Q.name}' (${Q.id}).`),W(`Mode: ${Q.dryRun?"dry run — reports only, enforces nothing":"enforcing"}`),W(`Ladder: ${Q.thresholds.map((Z)=>`${Z.atPercent}% → ${Z.actions.join("+")}`).join(", ")}`)}catch(J){L$(J)}}),G.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($,J)=>{try{let Y=await R$(J.env),X=Y.store.getBudget($);if(!X)throw Error(`Budget '${$}' was not found.`);let Q=Y.store.updateBudget($,{softLimitCents:c1(J.soft)??X.softLimitCents,hardLimitCents:c1(J.hard)??X.hardLimitCents,enabled:J.disable?!1:J.enable?!0:X.enabled,dryRun:J.enforce?!1:J.dryRun?!0:X.dryRun});B(`Updated budget '${Q.name}'.`),W(`Mode: ${Q.dryRun?"dry run":"enforcing"} · Enabled: ${Q.enabled?"yes":"no"}`)}catch(Y){L$(Y)}}),G.command("budget:delete <budgetId>","Delete a budget and lift anything it was enforcing").option("--env <environment>","Target environment").action(async($,J)=>{try{let Y=await R$(J.env),X=Y.store.getBudget($);if(!X)throw Error(`Budget '${$}' was not found.`);let Q=Y.gate.closeBudget($);if(Y.store.deleteBudget($),B(`Deleted budget '${X.name}'.`),Q>0)W(`Lifted ${Q} enforcement action(s) it had in force.`)}catch(Y){L$(Y)}}),G.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($)=>{try{let J=await R$($.env),Y=new RJ({controlPlane:J.controlPlane.store,store:J.store,alerts:$.apply?J.alerts:void 0});if(!$.apply){let Z=J.store.budgetsForScope(J.organizationId,J.projectId).map((z)=>J.service.status(z));if($.json){W(JSON.stringify(Z,null,2));return}if(R("Spend check (preview — nothing was applied)"),Z.length===0){N("No budgets govern this project.");return}w(["Budget","Used","Projected","Level","Would apply"],Z.map((z)=>[z.budget.name,`${z.decision.usedPercent.toFixed(1)}%`,`${z.decision.projectedPercent.toFixed(1)}%`,z.decision.level,z.decision.actions.join(", ")||"-"])),W(`
351
- Run with --apply to enforce.`);return}let X=await Y.run({organizationId:J.organizationId,projectId:J.projectId,environmentId:J.environmentRecord?.id,environmentKind:J.environmentRecord?.kind});if($.json){W(JSON.stringify(X,null,2));return}if(R("Spend cycle"),W(`Evaluated ${X.decisions.length} budget(s) in ${X.durationMs}ms.`),X.applied.length>0)N(`Applied: ${X.applied.join(", ")}`);if(X.released.length>0)B(`Released: ${X.released.join(", ")}`);if(X.withheld.length>0)N(`Withheld pending approval: ${X.withheld.join(", ")}`);if(X.anomalies.length>0)N(`${X.anomalies.length} anomaly(ies) detected.`);W(`Notifications queued: ${X.notificationsSent}`);for(let Q of X.warnings)N(Q);if(X.applied.length===0&&X.released.length===0)B("Nothing to change.")}catch(J){L$(J)}}),G.command("spend:work","Run the spend loop continuously: meter, evaluate, enforce, notify").option("--env <environment>","Target environment").option("--interval <seconds>",`Seconds between cycles (default ${LJ})`).option("--once","Run a single cycle and exit").action(async($)=>{try{let J=await R$($.env),Y=new RJ({controlPlane:J.controlPlane.store,store:J.store,alerts:J.alerts}),X={organizationId:J.organizationId,projectId:J.projectId,environmentId:J.environmentRecord?.id,environmentKind:J.environmentRecord?.kind};if($.once){let U=await Y.run(X);B(`Cycle finished in ${U.durationMs}ms.`);for(let M of U.warnings)N(M);return}let Q=Math.max(10,Number($.interval??LJ)),Z=new v9(J.controlPlane.store,{owner:`cli:${process.pid}`});R(`Spend worker (every ${Q}s)`),W("Press Ctrl+C to stop.");let z=m9(Y,{intervalSeconds:Q,lease:Z,immediate:!0,onResult:(U)=>{let M=U.flatMap((V)=>V.applied),H=U.flatMap((V)=>V.released),j=U.flatMap((V)=>V.warnings);if(M.length>0)N(`Applied: ${M.join(", ")}`);if(H.length>0)B(`Released: ${H.join(", ")}`);for(let V of j)N(V)},onSkip:(U)=>W(`Skipped: ${U} holds the loop lease.`),onError:(U)=>K(U instanceof Error?U.message:String(U))}),F=()=>{z(),W(`
352
- Spend worker stopped; the loop lease was released.`),process.exit(0)};process.on("SIGINT",F),process.on("SIGTERM",F),await new Promise(()=>{})}catch(J){L$(J)}}),G.command("spend:status","Show enforcement currently in force").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async($)=>{try{let J=await R$($.env),Y=J.gate.listUnder({organizationId:J.organizationId});if($.json){W(JSON.stringify(Y,null,2));return}if(Y.length===0){B("No spend enforcement is in force.");return}w(["Action","Budget","Scope","Mode","Since","Reason"],Y.map((X)=>[X.action,X.budgetId.slice(0,8),X.environmentId?"environment":X.projectId?"project":"organization",X.simulated?"dry run":"enforcing",X.appliedAt,X.reason]))}catch(J){L$(J)}}),G.command("spend:release <budgetId> <action>","Manually lift one enforcement action").option("--env <environment>","Target environment").action(async($,J,Y)=>{try{let X=await R$(Y.env),Q=b9(J)?.[0];if(!Q)throw Error("An action is required.");if(!X.gate.close($,Q))throw Error(`${Q} was not in force for budget ${$}.`);B(`Lifted ${Q} for budget ${$}.`),N("The next spend cycle will re-apply this if spend is still over the threshold.")}catch(X){L$(X)}}),G.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($)=>{try{if($.signals){w(["Signal","Source","Empty hour","Min samples","Unit"],C9.map((Z)=>[Z.key,Z.source,Z.gapPolicy==="zero"?"counts as zero":"ignored",String(Z.minSamples),Z.unit]));return}let J=await R$($.env),Y=J.service.anomalyConfigs;if($.list){let Z=Y.list({organizationId:J.organizationId,projectId:J.projectId});if(Z.length===0){W("No signals are configured; every one uses its shipped defaults.");return}w(["Signal","Enabled","Sensitivity","Season","Min delta","Severity"],Z.map((z)=>[z.signal,z.enabled?"yes":"no",z.sensitivity,String(z.seasonLength),String(z.minAbsoluteDelta),z.severity]));return}let X=$.sensitivity;if(X&&!["low","medium","high"].includes(X))throw Error("--sensitivity must be low, medium, or high.");let Q=Y.upsert({organizationId:J.organizationId,projectId:J.projectId,signal:$.signal??"cost",sensitivity:X,seasonLength:$.season==null?void 0:Number($.season),minAbsoluteDelta:$.minDelta==null?void 0:Number($.minDelta),severity:$.severity,enabled:$.disable?!1:$.enable?!0:void 0});B(`Anomaly detection for '${Q.signal}' updated.`),W(`${Q.enabled?"Enabled":"Disabled"} · ${Q.sensitivity} sensitivity · season ${Q.seasonLength} · floor ${Q.minAbsoluteDelta} · ${Q.severity}`)}catch(J){L$(J)}}),G.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($)=>{try{let J=await R$($.env),Y=J.service.anomalyConfigs;if($.remove){B(Y.removeSilence($.remove)?"Silence removed.":"That silence was not found.");return}if($.list){let Q=Y.listSilences({organizationId:J.organizationId,projectId:J.projectId});if(Q.length===0){B("No active silences.");return}w(["ID","Signal","Route","Status","Expires","Reason"],Q.map((Z)=>[Z.id.slice(0,8),Z.signal??"-",Z.routePattern??"-",Z.statusCode==null?"-":String(Z.statusCode),Z.expiresAt??"never",Z.reason]));return}if(!$.reason)throw Error("--reason is required: someone will ask why this stopped alerting.");let X=Y.silence({organizationId:J.organizationId,projectId:J.projectId,signal:$.signal,routePattern:$.route,statusCode:$.status==null?void 0:Number($.status),reason:$.reason,expiresAt:$.until});if(B(`Silenced (${X.id.slice(0,8)}).`),!X.expiresAt)N("This silence never expires. Pass --until to bound it, or it will outlive the reason for it.")}catch(J){L$(J)}}),G.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($)=>{try{let J=await R$($.env),Y=J.store.listAnomalies({organizationId:J.organizationId,projectId:J.projectId,unacknowledgedOnly:$.unacknowledged===!0});if($.json){W(JSON.stringify(Y,null,2));return}if(Y.length===0){B("No anomalies detected.");return}w(["Hour","Signal","Direction","Observed","Expected","Score","Severity"],Y.map((X)=>[X.bucketStart,X.signal,X.direction,d$(X.observed),d$(X.expected),X.score.toFixed(1),X.severity]))}catch(J){L$(J)}})}import{readFileSync as gG}from"node:fs";var vG={github:"GITHUB_TOKEN",gitlab:"GITLAB_TOKEN",bitbucket:"BITBUCKET_TOKEN",gitea:"GITEA_TOKEN",generic_https:"GIT_TOKEN"},mG={github:"https://github.com",gitlab:"https://gitlab.com",bitbucket:"https://bitbucket.org",gitea:"https://gitea.com"};function OJ(G,$={}){if($.provider&&!["github","gitlab","bitbucket","gitea","generic_https","generic_ssh"].includes($.provider))throw Error(`Unsupported Git provider: ${$.provider}`);let J=$.cloneUrl??(/^(?:https?:\/\/|ssh:\/\/|git@)/.test(G)?G:void 0),Y="",X=J??G;if(J?.startsWith("git@")){let z=/^git@([^:]+):(.+)$/.exec(J);if(!z)throw Error("SSH repository URL must use git@host:owner/name.git");Y=z[1],X=z[2]}else if(J){let z=new URL(J);Y=z.hostname,X=z.pathname}if(X=X.replace(/^\/+|\/+$/g,"").replace(/\.git$/,""),!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(X))throw Error("Repository must use owner/name or a supported clone URL");let Q=$.provider??(Y.includes("github")?"github":Y.includes("gitlab")?"gitlab":Y.includes("bitbucket")?"bitbucket":Y.includes("gitea")?"gitea":J?.startsWith("git@")||J?.startsWith("ssh://")?"generic_ssh":J?"generic_https":"github"),Z=$.host??mG[Q]??(Y?`https://${Y}`:"");if(!Z)throw Error("A provider host is required");return{provider:Q,host:Z,fullName:X,cloneUrl:J}}async function dG(){let G=await x(),$=u(process.cwd(),G);return{config:G,controlPlane:$,sources:new u1($.store,{encryptionKey:Q$(process.cwd())})}}async function $0(G){let $=await dG();try{return await G($)}finally{$.controlPlane.store.close()}}function U1(G,$,J){let Y=G.listConnections($);if(!J)return Y.length===1?Y[0]:void 0;return Y.find((X)=>X.id===J||X.name.toLowerCase()===J.toLowerCase())}function d9(G,$){if(!G)throw Error(`${$} file is required`);return gG(G,"utf8")}async function cG(G,$,J,Y){if(["github","gitlab","bitbucket","gitea"].includes($.provider)){let Q=G.listRepositories($.id).find((F)=>F.fullName.toLowerCase()===J.fullName.toLowerCase());if(Q)return Q;let z=(await l9(G,$.id,{search:J.fullName,maxPages:20})).find((F)=>F.fullName.toLowerCase()===J.fullName.toLowerCase());if(!z)throw Error(`Repository ${J.fullName} is not granted to this connection`);return z}let X=Y.url??J.cloneUrl;if(!X)throw Error("Generic Git connections require --url <clone-url>");return G.upsertRepository({connectionId:$.id,providerRepositoryId:`manual:${J.fullName}`,fullName:J.fullName,cloneUrl:X,defaultBranch:Y.branch??"main",visibility:"unknown",archived:!1,metadata:{source:"cli"}})}async function c9(G,$,J){let Y=OJ($,{provider:J.provider,host:J.host,cloneUrl:J.url}),X=U1(G.sources,G.controlPlane.organization.id,J.connection);if(!X){if(J.connection)throw Error(`Source connection ${J.connection} was not found`);let Z=J.tokenEnv??vG[Y.provider],z=Z?process.env[Z]:void 0;if(["github","gitlab","bitbucket","gitea"].includes(Y.provider)&&!z)throw Error(`Set ${Z} or pass --token-env <name>; secrets are never accepted as command arguments`);let F=Y.provider==="generic_ssh"?{publicKey:d9(J.publicKey,"Public key"),privateKey:d9(J.privateKey,"Private key"),hostKey:J.hostKey??""}:void 0;G.controlPlane.store.database.transaction(()=>{if(X=G.sources.createConnection({organizationId:G.controlPlane.organization.id,provider:Y.provider,name:J.name??`${Y.provider.replace("_"," ")} · ${new URL(Y.host).host}`,host:Y.host,owner:Y.fullName.split("/")[0],authKind:Y.provider==="generic_ssh"?"deploy_key":z?"access_token":"none",credential:z?{token:z}:void 0}),F)G.sources.createDeployKey({connectionId:X.id,name:J.deployKey??`${Y.fullName} deploy key`,...F,host:J.sshHost??new URL(Y.host).hostname,actorId:void 0})})()}if(!X)throw Error("Source connection could not be created");if(X.status==="disconnected")throw Error("The selected source connection is disconnected");let Q=await cG(G.sources,X,Y,J);if(J.site){let Z=G.controlPlane.environments.get("production")??[...G.controlPlane.environments.values()][0],z=G.controlPlane.store.listResources(G.controlPlane.project.id,Z?.id).find((U)=>U.kind==="application"&&U.slug===J.site);if(!z)throw Error(`Application ${J.site} was not found`);let F=X.provider==="generic_ssh"?G.sources.listDeployKeys(X.id)[0]:void 0;G.sources.createBinding({projectId:G.controlPlane.project.id,environmentId:Z?.id,resourceId:z.id,connectionId:X.id,repositoryId:Q.id,repositoryFullName:Q.fullName,defaultBranch:J.branch??Q.defaultBranch,branchRule:J.tag?void 0:J.branch??Q.defaultBranch,tagRule:J.tag,monorepoRoot:J.root??".",includePaths:J.include?.split(",").map((U)=>U.trim()).filter(Boolean),excludePaths:J.exclude?.split(",").map((U)=>U.trim()).filter(Boolean),submodules:J.submodules,cloneDepth:J.fullClone?void 0:20,deployKeyId:F?.id})}return{connection:X,repository:Q}}function J0(G){K(G instanceof Error?G.message:"Git integration command failed")}function wJ(G){G.command("git:connections","List encrypted Git provider connections").action(async()=>{try{await $0(async({sources:$,controlPlane:J})=>{let Y=$.listConnections(J.organization.id);w(["ID","Name","Provider","Status","Credential","Last sync"],Y.map((X)=>[X.id,X.name,X.provider,X.status,X.credentialConfigured?"encrypted":"none",X.lastSyncedAt??"-"]))})}catch($){J0($)}}),G.command("git:repositories","List repositories granted to a connection").option("--connection <id>","Connection ID or name").action(async($)=>{try{await $0(async({sources:J,controlPlane:Y})=>{let X=U1(J,Y.organization.id,$.connection);if(!X)throw Error("Choose a connection with --connection <id>");let Q=J.listRepositories(X.id);w(["ID","Repository","Default branch","Visibility","Archived"],Q.map((Z)=>[Z.id,Z.fullName,Z.defaultBranch,Z.visibility,Z.archived?"yes":"no"]))})}catch(J){J0(J)}}),G.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($,J)=>{try{if(!J.yes&&!await k(`Connect ${$}?`,!0))return;let Y=await $0((X)=>c9(X,$,J));if(B(`Connected ${Y.repository.fullName}`),W(`Connection: ${Y.connection.name} (${Y.connection.id})`),J.site)W(`Application binding: ${J.site}`)}catch(Y){J0(Y)}}),G.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($)=>{try{await $0(async(J)=>{let Y=Object.entries(J.config.sites??{}).filter(([,X])=>X.repository?.url);if(!Y.length)throw Error("No site.repository entries were found");if(!$.yes&&!await k(`Import ${Y.length} repository configuration${Y.length===1?"":"s"}?`,!0))return;for(let[X,Q]of Y){let Z=Q.repository,z=Z.provider==="custom"?void 0:Z.provider;await c9(J,Z.url,{connection:$.connection,tokenEnv:$.tokenEnv,site:X,provider:z,branch:Z.branch??"main",tag:Z.strategy==="tag"?Z.tag??Z.tagPattern??"v*":void 0,url:Z.url}),B(`Imported ${X} · ${Z.url}`)}})}catch(J){J0(J)}}),G.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($,J)=>{try{await $0(async({sources:Y,controlPlane:X})=>{let Q=U1(Y,X.organization.id,J.connection);if(!Q)throw Error("Choose a connection with --connection <id>");let Z=Y.listRepositories(Q.id).find((F)=>F.id===$||F.fullName.toLowerCase()===$.toLowerCase());if(!Z)throw Error(`Repository ${$} was not found`);let z=await TJ(Y,{connectionId:Q.id,repository:Z.fullName,repositoryId:Z.id,type:J.tags?"tags":"branches",deployKeyId:J.deployKey??Y.listDeployKeys(Q.id)[0]?.id});w([J.tags?"Tag":"Branch","Commit","Protected"],z.refs.map((F)=>[F.name,F.commitSha,F.protected?"yes":"no"]))})}catch(Y){J0(Y)}}),G.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($,J)=>{try{await $0(async({sources:Y,controlPlane:X})=>{let Q=X.environments.get(J.env??"production");if(!Q)throw Error(`Environment ${J.env} was not found`);let Z=Y.listBindings({projectId:X.project.id,status:"active"}).find((M)=>(!J.binding||M.id===J.binding)&&(!J.repo||M.repositoryFullName.toLowerCase()===J.repo.toLowerCase())&&(!M.environmentId||M.environmentId===Q.id));if(!Z)throw Error("No active source binding matched; use --binding or --repo");let F=(await TJ(Y,{connectionId:Z.connectionId,repository:Z.repositoryFullName,repositoryId:Z.repositoryId,type:"branches",deployKeyId:Z.deployKeyId})).refs.find((M)=>M.name===$);if(!F)throw Error(`Branch ${$} was not found`);let U=X.store.createOperation({projectId:Z.projectId,environmentId:Q.id,resourceId:Z.resourceId,kind:"deploy.source",idempotencyKey:`cli-source:${Z.id}:${F.commitSha}`,correlationId:`cli-source:${Z.id}:${F.commitSha}`,input:{source:{connectionId:Z.connectionId,bindingId:Z.id,repository:Z.repositoryFullName,commitSha:F.commitSha,branch:$,monorepoRoot:Z.monorepoRoot,submodules:Z.submodules,cloneDepth:Z.cloneDepth??null}}});B(`Deployment queued: ${U.id}`),W(`${Z.repositoryFullName}@${$} · ${F.commitSha}`)})}catch(Y){J0(Y)}}),G.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($,J)=>{try{await $0(async({sources:Y,controlPlane:X})=>{let Q=U1(Y,X.organization.id,J.connection);if(!Q)throw Error("Choose a connection with --connection <id>");let Z=Y.listRepositories(Q.id).find((H)=>H.id===$||H.fullName.toLowerCase()===$.toLowerCase());if(!Z)throw Error(`Repository ${$} was not found`);let z=J.baseUrl??process.env.TS_CLOUD_WEBHOOK_BASE_URL;if(!z)throw Error("Pass --base-url or set TS_CLOUD_WEBHOOK_BASE_URL");let F=Y.createWebhook({connectionId:Q.id,repositoryId:Z.id,repositoryFullName:Z.fullName}),U=u9(z,F.webhook),M=await r9(Y,F.webhook.id,z);B(`Webhook ${M.status}: ${U}`),W("The signing secret is encrypted and is not printed.")})}catch(Y){J0(Y)}}),G.command("git:webhook:remove <webhook>","Disable and remove a provider webhook").option("--yes","Skip confirmation").action(async($,J)=>{try{await $0(async({sources:Y,controlPlane:X})=>{let Q=Y.listConnections(X.organization.id).flatMap((z)=>Y.listWebhooks(z.id)).find((z)=>z.id===$||z.repositoryFullName.toLowerCase()===$.toLowerCase());if(!Q)throw Error(`Webhook ${$} was not found`);if(!J.yes&&!await k(`Remove webhook for ${Q.repositoryFullName}?`,!0))return;let Z=await a9(Y,Q.id);B(`Webhook disabled: ${Z.id}`)})}catch(Y){J0(Y)}}),G.command("git:disconnect <connection>","Preview and disconnect a Git provider connection").option("--yes","Skip confirmation").action(async($,J)=>{try{await $0(async({sources:Y,controlPlane:X})=>{let Q=U1(Y,X.organization.id,$);if(!Q)throw Error(`Source connection ${$} was not found`);let Z=Y.listBindings({connectionId:Q.id,status:"active"});W(`${Z.length} active binding${Z.length===1?"":"s"} will be disabled.`);for(let z of Z)W(` ${z.repositoryFullName} · ${z.branchRule??z.defaultBranch} · ${z.monorepoRoot}`);if(!J.yes&&!await k(`Disconnect ${Q.name}?`,!1))return;Y.disconnectConnection(Q.id),B(`Disconnected ${Q.name}`)})}catch(Y){J0(Y)}})}import{readFileSync as $2,writeFileSync as uG}from"node:fs";import{resolve as l1}from"node:path";async function lG(){let G=await x(),$=u(process.cwd(),G),J=Q$(process.cwd());return{config:G,controlPlane:$,drafts:new i9($.store),artifacts:new e9($.store,{cwd:process.cwd()}),registries:new t9($.store,{encryptionKey:J}),sources:new u1($.store,{encryptionKey:J})}}async function x$(G){let $=await lG();try{return await G($)}finally{$.controlPlane.store.close()}}function M$(G){K(G instanceof Error?G.message:"Application onboarding command failed")}function p9(G){return JSON.parse($2(l1(G),"utf8"))}function rG(G){let $=G.site,J=["laravel","php","statamic","wordpress"].includes($.type),Y=["static","spa"].includes($.type)||!$.start&&!J,X=Y?{kind:"static",publishDirectory:$.root??$.webDirectory??"dist",buildCommand:$.build}:{kind:"server",runtime:J?"php":"bun",runtimeVersion:J?$.phpVersion:void 0,installCommand:J?"composer install --no-dev --prefer-dist --optimize-autoloader":void 0,buildCommand:$.build,startCommand:$.start??(J?"php -S 0.0.0.0:$PORT -t public":"bun run start")};return{schemaVersion:1,name:$.name??G.siteSlug,slug:G.siteSlug,projectId:G.projectId,environmentId:G.environmentId,source:G.source??{kind:"local",root:$.root??"."},build:X,runtime:{target:"server",architecture:$.architecture==="arm64"?"arm64":"x86_64",port:Y?void 0:Number($.port??3000),healthCheck:Y?void 0:{protocol:"http",path:$.healthCheck?.path??"/health"}},domain:$.domain?{hostname:$.domain,tls:$.ssl?.provider!=="none"}:void 0}}function xJ(G){G.command("app:detect [root]","Detect application frameworks without executing project code").action(async($=".")=>{try{let J=n9(s9(l1($)));w(["Framework","Strategy","Confidence","Evidence"],J.map((Y)=>[Y.framework,Y.strategy,`${Math.round(Y.confidence*100)}%`,Y.evidence.map((X)=>X.path).join(", ")||"manual"]));for(let Y of J)W(`${Y.framework}: ${Y.description}`)}catch(J){M$(J)}}),G.command("app:plan <draft>","Validate and preview a secret-free application draft").option("--secrets <names>","Comma-separated configured secret names").action(async($,J)=>{try{let Y=DJ(p9($),J.secrets?.split(",").map((X)=>X.trim()).filter(Boolean));if(W(JSON.stringify(Y,null,2)),!Y.valid)M$(Error("Plan is not deployable; resolve the reported issues and missing secrets."))}catch(Y){M$(Y)}}),G.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($,J)=>{try{await x$(async({drafts:Y,controlPlane:X})=>{let Q=p9($),Z=J.secrets?.split(",").map((F)=>F.trim()).filter(Boolean)??[],z=J.id?Y.update(J.id,Number(J.version),{draft:Q,step:J.step,suppliedSecretNames:Z}):Y.create({organizationId:X.organization.id,projectId:X.project.id,name:Q.name,draft:{...Q,projectId:X.project.id},step:J.step,suppliedSecretNames:Z});B(`Draft saved: ${z.id} · v${z.version} · ${z.status}`)})}catch(Y){M$(Y)}}),G.command("app:drafts","List resumable application drafts").action(async()=>{try{await x$(async({drafts:$,controlPlane:J})=>{let Y=$.list(J.project.id);w(["ID","Application","Step","Status","Version","Updated"],Y.map((X)=>[X.id,X.input.name,X.step,X.status,String(X.version),X.updatedAt]))})}catch($){M$($)}}),G.command("app:export <draft>","Export a deterministic application manifest").option("--output <path>","Output path",{default:"application.manifest.json"}).action(async($,J)=>{try{await x$(async({drafts:Y})=>{let X=Y.get($);if(!X)throw Error(`Draft ${$} was not found`);let Q=DJ(X.input,X.suppliedSecretNames),Z=l1(J.output??"application.manifest.json");uG(Z,Q.serializedManifest,{mode:384}),B(`Manifest exported: ${Z}`)})}catch(Y){M$(Y)}}),G.command("app:deploy <draft>","Confirm and queue a validated application plan").option("--confirm <environment>","Exact target environment slug").action(async($,J)=>{try{await x$(async({drafts:Y,controlPlane:X})=>{let Q=Y.get($);if(!Q)throw Error(`Draft ${$} was not found`);if(!J.confirm)throw Error("Pass --confirm <environment> to acknowledge the target and recurring cost drivers");let Z=o9({controlPlane:X.store,drafts:Y,draftId:$,expectedVersion:Q.version,confirmEnvironment:J.confirm});B(`Application queued: ${Z.operation.id}`),W(`Cost drivers: ${Z.plan.costDrivers.join(", ")}`)})}catch(Y){M$(Y)}}),G.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($,J)=>{try{await x$(async({config:Y,controlPlane:X,sources:Q,drafts:Z})=>{let z=Y.sites?.[$];if(!z)throw Error(`Site ${$} was not found`);let F=X.environments.get(J.env??"production");if(!F)throw Error(`Environment ${J.env} was not found`);let U;if(z.repository?.url){if(!J.connection)throw Error("Repository-backed imports require --connection <id>");let j=Q.getConnection(J.connection);if(!j)throw Error("Git connection was not found");let V=OJ(z.repository.url),T=Q.listRepositories(j.id).find((A)=>A.fullName.toLowerCase()===V.fullName.toLowerCase());if(!T)throw Error(`Repository ${V.fullName} is not synchronized`);U={kind:"git",connectionId:j.id,repositoryId:T.id,repositoryFullName:T.fullName,ref:z.repository.branch??"main"}}let M=rG({site:z,siteSlug:$,projectId:X.project.id,environmentId:F.id,source:U}),H=Z.create({organizationId:X.organization.id,projectId:X.project.id,name:`Import ${$}`,draft:M,step:"review"});B(`Imported draft: ${H.id}`)})}catch(Y){M$(Y)}}),G.command("app:artifact:add <file>","Inspect and store a bounded ZIP or TAR artifact").action(async($)=>{try{await x$(async({artifacts:J,controlPlane:Y})=>{let X=l1($),Q=J.create({organizationId:Y.organization.id,projectId:Y.project.id,filename:X.split("/").pop(),bytes:$2(X)});B(`Artifact stored: ${Q.id} · ${Q.entryCount} entries · ${Q.sha256}`)})}catch(J){M$(J)}}),G.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($,J)=>{try{await x$(async({registries:Y,controlPlane:X})=>{let Q={username:process.env[J.usernameEnv],password:process.env[J.passwordEnv],token:process.env[J.tokenEnv]},Z=Y.create({organizationId:X.organization.id,provider:J.provider,name:J.name,host:$,credential:Object.values(Q).some(Boolean)?Q:void 0});B(`Registry connection created: ${Z.id}`)})}catch(Y){M$(Y)}}),G.command("app:registries","List safe registry connection metadata").action(async()=>{try{await x$(async({registries:$,controlPlane:J})=>w(["ID","Name","Provider","Host","Status"],$.list(J.organization.id).map((Y)=>[Y.id,Y.name,Y.provider,Y.host,Y.status])))}catch($){M$($)}}),G.command("app:registry:test <id>","Test registry and optional image pull access").option("--image <image>","Image reference").action(async($,J)=>{try{await x$(async({registries:Y})=>{let X=await Y.test($,{image:J.image});W(`${X.status}: ${X.healthMessage}`)})}catch(Y){M$(Y)}}),G.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($,J)=>{try{await x$(async({registries:Y})=>{let X={username:process.env[J.usernameEnv],password:process.env[J.passwordEnv],token:process.env[J.tokenEnv]};if(!Object.values(X).some(Boolean))throw Error("Set at least one configured registry credential environment variable");let Q=Y.rotate($,X,{expiresAt:J.expires?new Date(J.expires).toISOString():void 0});B(`Registry credential rotated: ${Q.id} · ${Q.status}`)})}catch(Y){M$(Y)}}),G.command("app:registry:disconnect <id>","Disconnect and erase registry credentials").option("--yes","Skip confirmation").action(async($,J)=>{try{await x$(async({registries:Y,drafts:X,controlPlane:Q})=>{let Z=Y.get($);if(!Z)throw Error(`Registry ${$} was not found`);let z=X.list(Q.project.id).filter((F)=>F.input.source.kind==="image"&&F.input.source.registryConnectionId===$||F.input.build.kind==="prebuilt_image"&&F.input.build.registryConnectionId===$);if(W(`${z.length} application draft${z.length===1?"":"s"} reference this registry.`),!J.yes&&!await k(`Disconnect ${Z.name}?`,!1))return;Y.disconnect($),B(`Disconnected ${Z.name}`)})}catch(Y){M$(Y)}})}import{existsSync as aG,readFileSync as pG,writeFileSync as nG}from"node:fs";function NJ(G,$){K(`'${G}' is not implemented yet.`),W($),process.exitCode=1}function sG(G){let $={};for(let J of G.split(`
353
- `)){let Y=J.trim();if(!Y||Y.startsWith("#"))continue;let X=Y.indexOf("=");if(X===-1)continue;let Q=Y.slice(0,X).trim(),Z=Y.slice(X+1).trim();if(Z.startsWith('"')&&Z.endsWith('"')||Z.startsWith("'")&&Z.endsWith("'"))Z=Z.slice(1,-1);$[Q]=Z}return $}function iG(G){return Object.entries(G).sort(([J],[Y])=>J.localeCompare(Y)).map(([J,Y])=>{if(!/[\s#"']/.test(Y))return`${J}=${Y}`;let X='"'+Y.replace(/"/g,"\\\"")+'"';return`${J}=${X}`}).join(`
348
+ ${d}
349
+ ${u}
350
+ `}function Ii(r){r.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(e)=>{let t=e?.profile,n=e?.cache!==!1,{start:i,end:o,label:s}=bm();w(`Cost Analysis — ${s}${t?` (profile: ${t})`:""}`);let c=new C("Querying AWS Cost Explorer...");c.start();let l=new Et(t,{useCache:n}),d;try{d=await l.getCostByService({start:i,end:o})}catch(y){c.stop(),p(`Cost Explorer request failed: ${y?.message??y}`),a(`
351
+ This command needs the ce:GetCostAndUsage IAM permission.`);return}let u=null,m=!1;if(d.some((y)=>y.service===ki))try{u=(await new de("us-east-1",t).listBuckets()).Buckets?.length??0}catch{m=!0}if(c.stop(),d.length===0){a("No billed services in this period.");return}let f=d.reduce((y,v)=>y+v.amount,0),h=d.map((y)=>{let v="-";if(y.service===ki){if(m)v="unknown (no s3:ListAllMyBuckets)";else if(u!==null)v=`${u} bucket${u===1?"":"s"}`}let R=f>0?`${(y.amount/f*100).toFixed(1)}%`:"—";return[y.service,v,se(y.amount),R]});if(x(["Service","Resources","Cost","% of Total"],h),a(`
352
+ Total: ${se(f)} across ${d.length} service${d.length===1?"":"s"}`),l.lastCacheAgeSeconds!==null)a(`(cached, ${l.lastCacheAgeSeconds}s old — pass --no-cache to refresh)`);if((d.find((y)=>y.service===ki)?.amount??0)>1&&u===0)I(`
353
+ 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(e?.output){let y=`${process.cwd()}/aws.md`;await Bun.write(y,wm({label:s,profile:t,rows:h,total:f,count:d.length})),g(`
354
+ Wrote ${y}`)}}),r.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(e)=>{let t=e?.all?void 0:e?.profile,n=lc(t),i=cc(t);a(`Cleared ${i.deletedFiles} cached response${i.deletedFiles===1?"":"s"} for ${i.scope}`),a(`(${n})`)}),r.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(e)=>{let t=oc(),n=new Et(e?.profile,{useCache:e?.cache!==!1});w(`AWS Cost — ${t.label}${e?.profile?` (profile: ${e.profile})`:""}`);let i=new C("Querying month-to-date AWS spend...");i.start();try{let[o,s]=await Promise.all([n.getCostByService({start:t.start,end:t.end,granularity:"DAILY"}),n.getCostByService({start:t.previous.start,end:t.previous.end,granularity:"MONTHLY"})]);i.stop();let l=$i(o,s).map((f)=>{let h=Si(f.amount,t.daysElapsed,t.daysInMonth);return[f.service,se(f.amount),se(h),se(f.previousAmount),Zn(gn(h,f.previousAmount))]});x(["Service","MTD","Projected",t.previous.label,"Change"],l);let d=o.reduce((f,h)=>f+h.amount,0),u=s.reduce((f,h)=>f+h.amount,0),m=Si(d,t.daysElapsed,t.daysInMonth);a(`
355
+ MTD: ${se(d)} through day ${t.daysElapsed} of ${t.daysInMonth}`),a(`Projected: ${se(m)} (${Zn(gn(m,u))} vs ${t.previous.label})`)}catch(o){i.stop(),Ri(o)}}),r.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(e)=>{let t=Number.parseInt(e?.days??"30",10),n;try{n=Gt(t)}catch(s){p(s instanceof Error?s.message:String(s));return}let i=new Et(e?.profile,{useCache:e?.cache!==!1});w(`AWS Cost Breakdown — ${n.current.label}${e?.profile?` (profile: ${e.profile})`:""}`);let o=new C("Comparing AWS service spend...");o.start();try{let[s,c]=await Promise.all([i.getCostByService({start:n.current.start,end:n.current.end,granularity:"DAILY"}),i.getCostByService({start:n.previous.start,end:n.previous.end,granularity:"DAILY"})]);o.stop();let l=$i(s,c);x(["Service",`Last ${t}d`,`Previous ${t}d`,"Trend"],l.map((m)=>[m.service,se(m.amount),se(m.previousAmount),Zn(m.changePercent)]));let d=s.reduce((m,f)=>m+f.amount,0),u=c.reduce((m,f)=>m+f.amount,0);a(`
356
+ Total: ${se(d)} (${Zn(gn(d,u))})`)}catch(s){o.stop(),Ri(s)}}),r.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(e)=>{let t=Number.parseInt(e?.days??"30",10),n;try{n=Gt(t).current}catch(s){p(s instanceof Error?s.message:String(s));return}let i=new Et(e?.profile,{useCache:e?.cache!==!1});w(`AWS Egress Cost — ${n.label}${e?.profile?` (profile: ${e.profile})`:""}`);let o=new C("Querying data-transfer usage types...");o.start();try{let s=Jn(await i.getCostByDimension({start:n.start,end:n.end,dimension:"USAGE_TYPE",granularity:"DAILY"}));if(o.stop(),s.length===0){a("No billed data-transfer usage types were found in this period.");return}x(["Usage type","Cost"],s.map((c)=>[c.key,se(c.amount)])),a(`
357
+ Total ranked egress: ${se(s.reduce((c,l)=>c+l.amount,0))}`),a("Usage types classify NAT, internet, inter-AZ, and inter-region transfer; destination correlation requires VPC Flow Logs.")}catch(s){o.stop(),Ri(s)}}),r.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(e)=>{w(`AWS Resources${e?.profile?` (profile: ${e.profile})`:""}`);let t=new C("Discovering tagged and untagged resources...");t.start();let n=new hn(e?.profile,e?.region);try{let i=await n.discover({type:e?.type});if(t.stop(),i.resources.length===0)a("No matching resources were discovered.");else x(["Type","Name","Region","State","Monthly cost"],i.resources.map((o)=>[`${o.service}:${o.type}`,o.name,o.region??"global",o.state??"unknown","— (requires CUR resource IDs)"])),a(`
358
+ ${i.resources.length} resource${i.resources.length===1?"":"s"} discovered.`);for(let o of i.warnings)I(o)}catch(i){t.stop(),p(`Resource discovery failed: ${i instanceof Error?i.message:String(i)}`)}}),r.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(e)=>{w(`Unused AWS Resources${e?.profile?` (profile: ${e.profile})`:""}`);let t=new C("Evaluating inventory and CloudWatch idle signals...");t.start();try{let n=await new Yn(e?.profile,e?.region).unused({type:e?.type});if(t.stop(),n.findings.length===0)a("No resources met the conservative unused-resource thresholds.");else{x(["Resource","Signal","Recommendation","Monthly savings"],n.findings.map((o)=>[`${o.resource.service}:${o.resource.type}/${o.resource.name}`,o.signal,o.recommendation,o.monthlySavings==null?"—":se(o.monthlySavings)]));let i=n.findings.reduce((o,s)=>o+(s.monthlySavings??0),0);a(`
359
+ Known monthly savings: ${n.savingsAvailable?se(i):"unavailable (enable CUR resource IDs)"}`)}for(let i of n.inventory.warnings)I(i)}catch(n){t.stop(),p(`Unused-resource analysis failed: ${n instanceof Error?n.message:String(n)}`)}}),r.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(e)=>{w(`AWS Cost Optimizations${e?.profile?` (profile: ${e.profile})`:""}`);let t=new C("Combining inventory, CloudWatch signals, and Cost Explorer line items...");t.start();try{let n=await new Yn(e?.profile,e?.region).optimize({type:e?.type,includeCommitments:e?.includeCommitments});if(t.stop(),n.recommendations.length===0)a("No resources met the conservative optimization thresholds.");else{x(["Category","Resource","Current","Projected","Savings","Source signal","Recommendation"],n.recommendations.map((o)=>[o.category,`${o.resource.service}:${o.resource.type}/${o.resource.name}`,o.currentCost==null?"—":se(o.currentCost),o.projectedCost==null?"—":se(o.projectedCost),o.monthlySavings==null?"—":se(o.monthlySavings),o.sourceSignal,o.recommendation]));let i=n.recommendations.reduce((o,s)=>o+(s.monthlySavings??0),0);a(`
360
+ Estimated monthly savings: ${n.savingsAvailable?se(i):"unavailable (enable CUR resource IDs)"}`)}for(let i of n.warnings)I(i);a("Estimates are directional; validate workload peaks and current AWS pricing before applying changes.")}catch(n){t.stop(),p(`Optimization analysis failed: ${n instanceof Error?n.message:String(n)}`)}})}async function Ht(r){let e=await P(),t=r??Object.keys(e.environments??{})[0]??"production";if(!Object.hasOwn(e.environments??{},t))throw Error(`Environment ${t} was not found`);let n=q(process.cwd(),e);return{config:e,env:t,controlPlane:n,controls:new vn(n.store)}}function Jt(r){p(r instanceof Error?r.message:String(r)),process.exitCode=1}function dc(r){let e=r?.infrastructure?.compute??{};return{ddos:e.ddos===!1?void 0:typeof e.ddos==="object"?e.ddos:{ports:[80,443]},waf:e.waf===!1?void 0:typeof e.waf==="object"?e.waf:{mode:"detection"}}}function Ni(r){r.command("protect:status","Show the current edge-protection posture").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>{try{let t=await Ht(e.env),n=t.controls.current(),i=Di(n);if(e.json){a(JSON.stringify({posture:i,controls:t.controls.raw()},null,2));return}if(w("Edge protection"),i.mitigationPaused||i.attackMode)I(i.summary);else g(i.summary);let o=dc(t.config);if(a(`L3/L4 mitigation: ${o.ddos?"enabled":"disabled in config"}`),a(`WAF: ${o.waf?`enabled (${o.waf.mode??"detection"})`:"disabled in config"}`),a(`Allowlist: ${n.ipRules.allow.length} entry(s)`),a(`Blocklist: ${n.ipRules.block.length} entry(s)`),n.ipRules.allow.length>0||n.ipRules.block.length>0)x(["List","CIDR"],[...n.ipRules.allow.map((s)=>["allow",s]),...n.ipRules.block.map((s)=>["block",s])])}catch(t){Jt(t)}}),r.command("protect:attack-mode <state>","Challenge every visitor for a bounded window (on|off)").option("--hours <hours>",`How long before it lifts itself (default ${Ei}, max ${Qn})`).option("--reason <reason>","Why, for the record").option("--env <environment>","Target environment").action(async(e,t)=>{try{let n=await Ht(t.env);if(e==="off"){g(n.controls.disableAttackMode()?"Attack mode is off.":"Attack mode was not on.");return}if(e!=="on")throw Error("State must be 'on' or 'off'.");let i=n.controls.enableAttackMode({hours:t.hours==null?void 0:Number(t.hours),reason:t.reason??"Enabled from the CLI."});I("Attack mode is ON. Every visitor is challenged, including legitimate ones."),a(`It lifts itself at ${i.expiresAt} — you do not need to remember.`)}catch(n){Jt(n)}}),r.command("protect:pause <state>","Pause or resume automatic mitigation (on|off)").option("--hours <hours>",`How long (max ${Qn})`).option("--reason <reason>","Why — required when pausing").option("--env <environment>","Target environment").action(async(e,t)=>{try{let n=await Ht(t.env);if(e==="off"){g(n.controls.resumeMitigations()?"Automatic mitigation resumed.":"Mitigation was not paused.");return}if(e!=="on")throw Error("State must be 'on' or 'off'.");if(!t.reason)throw Error("--reason is required: you are liable for the traffic this admits.");let i=n.controls.pauseMitigations({hours:t.hours==null?void 0:Number(t.hours),reason:t.reason});I("Automatic mitigation is PAUSED. All traffic is served, and billed, including attacks."),a(`It resumes automatically at ${i.expiresAt}.`)}catch(n){Jt(n)}}),r.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(e,t)=>{try{let n=await Ht(t.env),i=t.remove?n.controls.removeIpRule("allow",e):n.controls.addIpRule("allow",e);g(`Allowlist now has ${i.allow.length} entry(s).`),a("It reaches the boxes on the next deploy. `cloud protect:apply` shows exactly what will land.")}catch(n){Jt(n)}}),r.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(e,t)=>{try{let n=await Ht(t.env),i=t.remove?n.controls.removeIpRule("block",e):n.controls.addIpRule("block",e);g(`Blocklist now has ${i.block.length} entry(s).`),a("It reaches the boxes on the next deploy. `cloud protect:apply` shows exactly what will land.")}catch(n){Jt(n)}}),r.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(e)=>{try{let t=await Ht(e.env),n=dc(t.config);if(e.waf){if(!n.waf)throw Error("The WAF is disabled in this configuration.");let i=xi(n.waf);for(let o of i.warnings)I(o);a(i.config);return}if(!n.ddos)throw Error("L3/L4 mitigation is disabled in this configuration.");a(Pi(Ti(n.ddos,t.controls.current())))}catch(t){Jt(t)}})}async function Se(r){let e=await P(),t=r??Object.keys(e.environments??{})[0]??"production";if(!Object.hasOwn(e.environments??{},t))throw Error(`Environment ${t} was not found`);let n=q(process.cwd(),e),i=n.environments.get(t),o=new gc(n.store),s=new bn(n.store,{encryptionKey:le(process.cwd())});return{config:e,env:t,controlPlane:n,environmentRecord:i,store:o,alerts:s,service:new yc(o),gate:new vc(n.store),organizationId:n.organization.id,projectId:n.project.id}}function Xn(r){if(r==null)return;let e=String(r).trim().replace(/[$,\s]/g,"");if(!e)return;if(/^\d+c$/i.test(e))return Number.parseInt(e,10);let t=Number(e);if(!Number.isFinite(t)||t<0)throw Error(`Invalid amount: ${r}`);return Math.round(t*100)}function uc(r){if(!r)return;let e=r.split(",").map((t)=>t.trim()).filter(Boolean);for(let t of e)if(!Oi.includes(t))throw Error(`Unknown enforcement action '${t}'. Valid: ${Oi.join(", ")}`);return e}function mc(r){if(r==null)return"monthly";if(r==="daily"||r==="weekly"||r==="monthly")return r;throw Error(`Unknown period '${r}'. Valid: daily, weekly, monthly`)}function Sm(r){return pc[r]?.label??r}function $m(r,e=24){let t=Math.max(0,Math.min(e,Math.round(r/100*e)));return`${"█".repeat(t)}${"░".repeat(e-t)}`}function $e(r){p(r instanceof Error?r.message:String(r)),process.exitCode=1}function Fi(r){r.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(e)=>{try{let t=await Se(e.env),n=t.service.usageReport({organizationId:t.organizationId,projectId:t.projectId,period:mc(e.period),timezone:e.timezone});if(e.json){a(JSON.stringify(n,null,2));return}if(w(`Usage — ${n.window.label}`),a(`Total: ${Ge(n.totalCents,n.currency)}`),n.byMeter.length===0)a("No metered usage in this window yet.");else x(["Meter","Provider","Quantity","Cost"],n.byMeter.map((i)=>[Sm(i.meter),i.provider||"-",i.quantity.toLocaleString(void 0,{maximumFractionDigits:3}),Ge(i.costCents,n.currency)]));if(n.budgets.length===0){I("No budgets are configured. Nothing is capping this project."),a("Create one with: cloud budget:create --name 'Monthly' --hard 200");return}if(w("Budgets"),x(["Name","Period","Spent","Limit","Used","Projected","Time to cap","State"],n.budgets.map((i)=>[i.name+(i.dryRun?" (dry run)":""),i.period,Ge(i.spentCents,n.currency),i.limitCents==null?"-":Ge(i.limitCents,n.currency),`${$m(i.usedPercent)} ${i.usedPercent.toFixed(0)}%`,`${Ge(i.projectedCents,n.currency)} (${(i.projectionConfidence*100).toFixed(0)}% conf)`,i.timeToCap||"-",i.level])),n.enforcement.strongestAction)I(`Enforcement in force: ${n.enforcement.strongestAction}`)}catch(t){$e(t)}}),r.command("budget:list","List budgets governing this project").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>{try{let t=await Se(e.env),n=t.store.listBudgets({organizationId:t.organizationId,projectId:t.projectId});if(e.json){a(JSON.stringify(n,null,2));return}if(n.length===0){I("No budgets configured.");return}x(["ID","Name","Scope","Period","Soft","Hard","Mode","Enabled"],n.map((i)=>[i.id.slice(0,8),i.name,i.environmentId?"environment":i.projectId?"project":"organization",i.period,i.softLimitCents==null?"-":Ge(i.softLimitCents,i.currency),i.hardLimitCents==null?"-":Ge(i.hardLimitCents,i.currency),i.dryRun?"dry run":"enforcing",i.enabled?"yes":"no"]))}catch(t){$e(t)}}),r.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(e)=>{try{if(!e.name)throw Error("--name is required.");let t=await Se(e.env),n=e.scope??"project";if(!["organization","project","environment"].includes(n))throw Error(`Unknown scope '${n}'. Valid: organization, project, environment`);let i=uc(e.actions),o=t.store.createBudget({organizationId:t.organizationId,projectId:n==="organization"?void 0:t.projectId,environmentId:n==="environment"?t.environmentRecord?.id:void 0,name:e.name,period:mc(e.period),timezone:e.timezone,softLimitCents:Xn(e.soft),hardLimitCents:Xn(e.hard),graceSeconds:e.grace==null?void 0:Number(e.grace),thresholds:i?[{atPercent:80,actions:["notify"]},{atPercent:100,actions:i}]:[...fc],dryRun:e.dryRun===!0});g(`Created budget '${o.name}' (${o.id}).`),a(`Mode: ${o.dryRun?"dry run — reports only, enforces nothing":"enforcing"}`),a(`Ladder: ${o.thresholds.map((s)=>`${s.atPercent}% → ${s.actions.join("+")}`).join(", ")}`)}catch(t){$e(t)}}),r.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(e,t)=>{try{let n=await Se(t.env),i=n.store.getBudget(e);if(!i)throw Error(`Budget '${e}' was not found.`);let o=n.store.updateBudget(e,{softLimitCents:Xn(t.soft)??i.softLimitCents,hardLimitCents:Xn(t.hard)??i.hardLimitCents,enabled:t.disable?!1:t.enable?!0:i.enabled,dryRun:t.enforce?!1:t.dryRun?!0:i.dryRun});g(`Updated budget '${o.name}'.`),a(`Mode: ${o.dryRun?"dry run":"enforcing"} · Enabled: ${o.enabled?"yes":"no"}`)}catch(n){$e(n)}}),r.command("budget:delete <budgetId>","Delete a budget and lift anything it was enforcing").option("--env <environment>","Target environment").action(async(e,t)=>{try{let n=await Se(t.env),i=n.store.getBudget(e);if(!i)throw Error(`Budget '${e}' was not found.`);let o=n.gate.closeBudget(e);if(n.store.deleteBudget(e),g(`Deleted budget '${i.name}'.`),o>0)a(`Lifted ${o} enforcement action(s) it had in force.`)}catch(n){$e(n)}}),r.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(e)=>{try{let t=await Se(e.env),n=new Li({controlPlane:t.controlPlane.store,store:t.store,alerts:e.apply?t.alerts:void 0});if(!e.apply){let s=t.store.budgetsForScope(t.organizationId,t.projectId).map((c)=>t.service.status(c));if(e.json){a(JSON.stringify(s,null,2));return}if(w("Spend check (preview — nothing was applied)"),s.length===0){I("No budgets govern this project.");return}x(["Budget","Used","Projected","Level","Would apply"],s.map((c)=>[c.budget.name,`${c.decision.usedPercent.toFixed(1)}%`,`${c.decision.projectedPercent.toFixed(1)}%`,c.decision.level,c.decision.actions.join(", ")||"-"])),a(`
361
+ Run with --apply to enforce.`);return}let i=await n.run({organizationId:t.organizationId,projectId:t.projectId,environmentId:t.environmentRecord?.id,environmentKind:t.environmentRecord?.kind});if(e.json){a(JSON.stringify(i,null,2));return}if(w("Spend cycle"),a(`Evaluated ${i.decisions.length} budget(s) in ${i.durationMs}ms.`),i.applied.length>0)I(`Applied: ${i.applied.join(", ")}`);if(i.released.length>0)g(`Released: ${i.released.join(", ")}`);if(i.withheld.length>0)I(`Withheld pending approval: ${i.withheld.join(", ")}`);if(i.anomalies.length>0)I(`${i.anomalies.length} anomaly(ies) detected.`);a(`Notifications queued: ${i.notificationsSent}`);for(let o of i.warnings)I(o);if(i.applied.length===0&&i.released.length===0)g("Nothing to change.")}catch(t){$e(t)}}),r.command("spend:work","Run the spend loop continuously: meter, evaluate, enforce, notify").option("--env <environment>","Target environment").option("--interval <seconds>",`Seconds between cycles (default ${ji})`).option("--once","Run a single cycle and exit").action(async(e)=>{try{let t=await Se(e.env),n=new Li({controlPlane:t.controlPlane.store,store:t.store,alerts:t.alerts}),i={organizationId:t.organizationId,projectId:t.projectId,environmentId:t.environmentRecord?.id,environmentKind:t.environmentRecord?.kind};if(e.once){let d=await n.run(i);g(`Cycle finished in ${d.durationMs}ms.`);for(let u of d.warnings)I(u);return}let o=Math.max(10,Number(e.interval??ji)),s=new bc(t.controlPlane.store,{owner:`cli:${process.pid}`});w(`Spend worker (every ${o}s)`),a("Press Ctrl+C to stop.");let c=wc(n,{intervalSeconds:o,lease:s,immediate:!0,onResult:(d)=>{let u=d.flatMap((h)=>h.applied),m=d.flatMap((h)=>h.released),f=d.flatMap((h)=>h.warnings);if(u.length>0)I(`Applied: ${u.join(", ")}`);if(m.length>0)g(`Released: ${m.join(", ")}`);for(let h of f)I(h)},onSkip:(d)=>a(`Skipped: ${d} holds the loop lease.`),onError:(d)=>p(d instanceof Error?d.message:String(d))}),l=()=>{c(),a(`
362
+ Spend worker stopped; the loop lease was released.`),process.exit(0)};process.on("SIGINT",l),process.on("SIGTERM",l),await new Promise(()=>{})}catch(t){$e(t)}}),r.command("spend:status","Show enforcement currently in force").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>{try{let t=await Se(e.env),n=t.gate.listUnder({organizationId:t.organizationId});if(e.json){a(JSON.stringify(n,null,2));return}if(n.length===0){g("No spend enforcement is in force.");return}x(["Action","Budget","Scope","Mode","Since","Reason"],n.map((i)=>[i.action,i.budgetId.slice(0,8),i.environmentId?"environment":i.projectId?"project":"organization",i.simulated?"dry run":"enforcing",i.appliedAt,i.reason]))}catch(t){$e(t)}}),r.command("spend:release <budgetId> <action>","Manually lift one enforcement action").option("--env <environment>","Target environment").action(async(e,t,n)=>{try{let i=await Se(n.env),o=uc(t)?.[0];if(!o)throw Error("An action is required.");if(!i.gate.close(e,o))throw Error(`${o} was not in force for budget ${e}.`);g(`Lifted ${o} for budget ${e}.`),I("The next spend cycle will re-apply this if spend is still over the threshold.")}catch(i){$e(i)}}),r.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(e)=>{try{if(e.signals){x(["Signal","Source","Empty hour","Min samples","Unit"],hc.map((s)=>[s.key,s.source,s.gapPolicy==="zero"?"counts as zero":"ignored",String(s.minSamples),s.unit]));return}let t=await Se(e.env),n=t.service.anomalyConfigs;if(e.list){let s=n.list({organizationId:t.organizationId,projectId:t.projectId});if(s.length===0){a("No signals are configured; every one uses its shipped defaults.");return}x(["Signal","Enabled","Sensitivity","Season","Min delta","Severity"],s.map((c)=>[c.signal,c.enabled?"yes":"no",c.sensitivity,String(c.seasonLength),String(c.minAbsoluteDelta),c.severity]));return}let i=e.sensitivity;if(i&&!["low","medium","high"].includes(i))throw Error("--sensitivity must be low, medium, or high.");let o=n.upsert({organizationId:t.organizationId,projectId:t.projectId,signal:e.signal??"cost",sensitivity:i,seasonLength:e.season==null?void 0:Number(e.season),minAbsoluteDelta:e.minDelta==null?void 0:Number(e.minDelta),severity:e.severity,enabled:e.disable?!1:e.enable?!0:void 0});g(`Anomaly detection for '${o.signal}' updated.`),a(`${o.enabled?"Enabled":"Disabled"} · ${o.sensitivity} sensitivity · season ${o.seasonLength} · floor ${o.minAbsoluteDelta} · ${o.severity}`)}catch(t){$e(t)}}),r.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(e)=>{try{let t=await Se(e.env),n=t.service.anomalyConfigs;if(e.remove){g(n.removeSilence(e.remove)?"Silence removed.":"That silence was not found.");return}if(e.list){let o=n.listSilences({organizationId:t.organizationId,projectId:t.projectId});if(o.length===0){g("No active silences.");return}x(["ID","Signal","Route","Status","Expires","Reason"],o.map((s)=>[s.id.slice(0,8),s.signal??"-",s.routePattern??"-",s.statusCode==null?"-":String(s.statusCode),s.expiresAt??"never",s.reason]));return}if(!e.reason)throw Error("--reason is required: someone will ask why this stopped alerting.");let i=n.silence({organizationId:t.organizationId,projectId:t.projectId,signal:e.signal,routePattern:e.route,statusCode:e.status==null?void 0:Number(e.status),reason:e.reason,expiresAt:e.until});if(g(`Silenced (${i.id.slice(0,8)}).`),!i.expiresAt)I("This silence never expires. Pass --until to bound it, or it will outlive the reason for it.")}catch(t){$e(t)}}),r.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(e)=>{try{let t=await Se(e.env),n=t.store.listAnomalies({organizationId:t.organizationId,projectId:t.projectId,unacknowledgedOnly:e.unacknowledged===!0});if(e.json){a(JSON.stringify(n,null,2));return}if(n.length===0){g("No anomalies detected.");return}x(["Hour","Signal","Direction","Observed","Expected","Score","Severity"],n.map((i)=>[i.bucketStart,i.signal,i.direction,Ge(i.observed),Ge(i.expected),i.score.toFixed(1),i.severity]))}catch(t){$e(t)}})}import{readFileSync as Cm}from"node:fs";var Am={github:"GITHUB_TOKEN",gitlab:"GITLAB_TOKEN",bitbucket:"BITBUCKET_TOKEN",gitea:"GITEA_TOKEN",generic_https:"GIT_TOKEN"},km={github:"https://github.com",gitlab:"https://gitlab.com",bitbucket:"https://bitbucket.org",gitea:"https://gitea.com"};function Bi(r,e={}){if(e.provider&&!["github","gitlab","bitbucket","gitea","generic_https","generic_ssh"].includes(e.provider))throw Error(`Unsupported Git provider: ${e.provider}`);let t=e.cloneUrl??(/^(?:https?:\/\/|ssh:\/\/|git@)/.test(r)?r:void 0),n="",i=t??r;if(t?.startsWith("git@")){let c=/^git@([^:]+):(.+)$/.exec(t);if(!c)throw Error("SSH repository URL must use git@host:owner/name.git");n=c[1],i=c[2]}else if(t){let c=new URL(t);n=c.hostname,i=c.pathname}if(i=i.replace(/^\/+|\/+$/g,"").replace(/\.git$/,""),!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(i))throw Error("Repository must use owner/name or a supported clone URL");let o=e.provider??(n.includes("github")?"github":n.includes("gitlab")?"gitlab":n.includes("bitbucket")?"bitbucket":n.includes("gitea")?"gitea":t?.startsWith("git@")||t?.startsWith("ssh://")?"generic_ssh":t?"generic_https":"github"),s=e.host??km[o]??(n?`https://${n}`:"");if(!s)throw Error("A provider host is required");return{provider:o,host:s,fullName:i,cloneUrl:t}}async function Rm(){let r=await P(),e=q(process.cwd(),r);return{config:r,controlPlane:e,sources:new er(e.store,{encryptionKey:le(process.cwd())})}}async function rt(r){let e=await Rm();try{return await r(e)}finally{e.controlPlane.store.close()}}function wn(r,e,t){let n=r.listConnections(e);if(!t)return n.length===1?n[0]:void 0;return n.find((i)=>i.id===t||i.name.toLowerCase()===t.toLowerCase())}function Sc(r,e){if(!r)throw Error(`${e} file is required`);return Cm(r,"utf8")}async function Im(r,e,t,n){if(["github","gitlab","bitbucket","gitea"].includes(e.provider)){let o=r.listRepositories(e.id).find((l)=>l.fullName.toLowerCase()===t.fullName.toLowerCase());if(o)return o;let c=(await Ac(r,e.id,{search:t.fullName,maxPages:20})).find((l)=>l.fullName.toLowerCase()===t.fullName.toLowerCase());if(!c)throw Error(`Repository ${t.fullName} is not granted to this connection`);return c}let i=n.url??t.cloneUrl;if(!i)throw Error("Generic Git connections require --url <clone-url>");return r.upsertRepository({connectionId:e.id,providerRepositoryId:`manual:${t.fullName}`,fullName:t.fullName,cloneUrl:i,defaultBranch:n.branch??"main",visibility:"unknown",archived:!1,metadata:{source:"cli"}})}async function $c(r,e,t){let n=Bi(e,{provider:t.provider,host:t.host,cloneUrl:t.url}),i=wn(r.sources,r.controlPlane.organization.id,t.connection);if(!i){if(t.connection)throw Error(`Source connection ${t.connection} was not found`);let s=t.tokenEnv??Am[n.provider],c=s?process.env[s]:void 0;if(["github","gitlab","bitbucket","gitea"].includes(n.provider)&&!c)throw Error(`Set ${s} or pass --token-env <name>; secrets are never accepted as command arguments`);let l=n.provider==="generic_ssh"?{publicKey:Sc(t.publicKey,"Public key"),privateKey:Sc(t.privateKey,"Private key"),hostKey:t.hostKey??""}:void 0;r.controlPlane.store.database.transaction(()=>{if(i=r.sources.createConnection({organizationId:r.controlPlane.organization.id,provider:n.provider,name:t.name??`${n.provider.replace("_"," ")} · ${new URL(n.host).host}`,host:n.host,owner:n.fullName.split("/")[0],authKind:n.provider==="generic_ssh"?"deploy_key":c?"access_token":"none",credential:c?{token:c}:void 0}),l)r.sources.createDeployKey({connectionId:i.id,name:t.deployKey??`${n.fullName} deploy key`,...l,host:t.sshHost??new URL(n.host).hostname,actorId:void 0})})()}if(!i)throw Error("Source connection could not be created");if(i.status==="disconnected")throw Error("The selected source connection is disconnected");let o=await Im(r.sources,i,n,t);if(t.site){let s=r.controlPlane.environments.get("production")??[...r.controlPlane.environments.values()][0],c=r.controlPlane.store.listResources(r.controlPlane.project.id,s?.id).find((d)=>d.kind==="application"&&d.slug===t.site);if(!c)throw Error(`Application ${t.site} was not found`);let l=i.provider==="generic_ssh"?r.sources.listDeployKeys(i.id)[0]:void 0;r.sources.createBinding({projectId:r.controlPlane.project.id,environmentId:s?.id,resourceId:c.id,connectionId:i.id,repositoryId:o.id,repositoryFullName:o.fullName,defaultBranch:t.branch??o.defaultBranch,branchRule:t.tag?void 0:t.branch??o.defaultBranch,tagRule:t.tag,monorepoRoot:t.root??".",includePaths:t.include?.split(",").map((d)=>d.trim()).filter(Boolean),excludePaths:t.exclude?.split(",").map((d)=>d.trim()).filter(Boolean),submodules:t.submodules,cloneDepth:t.fullClone?void 0:20,deployKeyId:l?.id})}return{connection:i,repository:o}}function it(r){p(r instanceof Error?r.message:"Git integration command failed")}function _i(r){r.command("git:connections","List encrypted Git provider connections").action(async()=>{try{await rt(async({sources:e,controlPlane:t})=>{let n=e.listConnections(t.organization.id);x(["ID","Name","Provider","Status","Credential","Last sync"],n.map((i)=>[i.id,i.name,i.provider,i.status,i.credentialConfigured?"encrypted":"none",i.lastSyncedAt??"-"]))})}catch(e){it(e)}}),r.command("git:repositories","List repositories granted to a connection").option("--connection <id>","Connection ID or name").action(async(e)=>{try{await rt(async({sources:t,controlPlane:n})=>{let i=wn(t,n.organization.id,e.connection);if(!i)throw Error("Choose a connection with --connection <id>");let o=t.listRepositories(i.id);x(["ID","Repository","Default branch","Visibility","Archived"],o.map((s)=>[s.id,s.fullName,s.defaultBranch,s.visibility,s.archived?"yes":"no"]))})}catch(t){it(t)}}),r.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(e,t)=>{try{if(!t.yes&&!await O(`Connect ${e}?`,!0))return;let n=await rt((i)=>$c(i,e,t));if(g(`Connected ${n.repository.fullName}`),a(`Connection: ${n.connection.name} (${n.connection.id})`),t.site)a(`Application binding: ${t.site}`)}catch(n){it(n)}}),r.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(e)=>{try{await rt(async(t)=>{let n=Object.entries(t.config.sites??{}).filter(([,i])=>i.repository?.url);if(!n.length)throw Error("No site.repository entries were found");if(!e.yes&&!await O(`Import ${n.length} repository configuration${n.length===1?"":"s"}?`,!0))return;for(let[i,o]of n){let s=o.repository,c=s.provider==="custom"?void 0:s.provider;await $c(t,s.url,{connection:e.connection,tokenEnv:e.tokenEnv,site:i,provider:c,branch:s.branch??"main",tag:s.strategy==="tag"?s.tag??s.tagPattern??"v*":void 0,url:s.url}),g(`Imported ${i} · ${s.url}`)}})}catch(t){it(t)}}),r.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(e,t)=>{try{await rt(async({sources:n,controlPlane:i})=>{let o=wn(n,i.organization.id,t.connection);if(!o)throw Error("Choose a connection with --connection <id>");let s=n.listRepositories(o.id).find((l)=>l.id===e||l.fullName.toLowerCase()===e.toLowerCase());if(!s)throw Error(`Repository ${e} was not found`);let c=await Mi(n,{connectionId:o.id,repository:s.fullName,repositoryId:s.id,type:t.tags?"tags":"branches",deployKeyId:t.deployKey??n.listDeployKeys(o.id)[0]?.id});x([t.tags?"Tag":"Branch","Commit","Protected"],c.refs.map((l)=>[l.name,l.commitSha,l.protected?"yes":"no"]))})}catch(n){it(n)}}),r.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(e,t)=>{try{await rt(async({sources:n,controlPlane:i})=>{let o=i.environments.get(t.env??"production");if(!o)throw Error(`Environment ${t.env} was not found`);let s=n.listBindings({projectId:i.project.id,status:"active"}).find((u)=>(!t.binding||u.id===t.binding)&&(!t.repo||u.repositoryFullName.toLowerCase()===t.repo.toLowerCase())&&(!u.environmentId||u.environmentId===o.id));if(!s)throw Error("No active source binding matched; use --binding or --repo");let l=(await Mi(n,{connectionId:s.connectionId,repository:s.repositoryFullName,repositoryId:s.repositoryId,type:"branches",deployKeyId:s.deployKeyId})).refs.find((u)=>u.name===e);if(!l)throw Error(`Branch ${e} was not found`);let d=i.store.createOperation({projectId:s.projectId,environmentId:o.id,resourceId:s.resourceId,kind:"deploy.source",idempotencyKey:`cli-source:${s.id}:${l.commitSha}`,correlationId:`cli-source:${s.id}:${l.commitSha}`,input:{source:{connectionId:s.connectionId,bindingId:s.id,repository:s.repositoryFullName,commitSha:l.commitSha,branch:e,monorepoRoot:s.monorepoRoot,submodules:s.submodules,cloneDepth:s.cloneDepth??null}}});g(`Deployment queued: ${d.id}`),a(`${s.repositoryFullName}@${e} · ${l.commitSha}`)})}catch(n){it(n)}}),r.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(e,t)=>{try{await rt(async({sources:n,controlPlane:i})=>{let o=wn(n,i.organization.id,t.connection);if(!o)throw Error("Choose a connection with --connection <id>");let s=n.listRepositories(o.id).find((m)=>m.id===e||m.fullName.toLowerCase()===e.toLowerCase());if(!s)throw Error(`Repository ${e} was not found`);let c=t.baseUrl??process.env.TS_CLOUD_WEBHOOK_BASE_URL;if(!c)throw Error("Pass --base-url or set TS_CLOUD_WEBHOOK_BASE_URL");let l=n.createWebhook({connectionId:o.id,repositoryId:s.id,repositoryFullName:s.fullName}),d=Cc(c,l.webhook),u=await kc(n,l.webhook.id,c);g(`Webhook ${u.status}: ${d}`),a("The signing secret is encrypted and is not printed.")})}catch(n){it(n)}}),r.command("git:webhook:remove <webhook>","Disable and remove a provider webhook").option("--yes","Skip confirmation").action(async(e,t)=>{try{await rt(async({sources:n,controlPlane:i})=>{let o=n.listConnections(i.organization.id).flatMap((c)=>n.listWebhooks(c.id)).find((c)=>c.id===e||c.repositoryFullName.toLowerCase()===e.toLowerCase());if(!o)throw Error(`Webhook ${e} was not found`);if(!t.yes&&!await O(`Remove webhook for ${o.repositoryFullName}?`,!0))return;let s=await Rc(n,o.id);g(`Webhook disabled: ${s.id}`)})}catch(n){it(n)}}),r.command("git:disconnect <connection>","Preview and disconnect a Git provider connection").option("--yes","Skip confirmation").action(async(e,t)=>{try{await rt(async({sources:n,controlPlane:i})=>{let o=wn(n,i.organization.id,e);if(!o)throw Error(`Source connection ${e} was not found`);let s=n.listBindings({connectionId:o.id,status:"active"});a(`${s.length} active binding${s.length===1?"":"s"} will be disabled.`);for(let c of s)a(` ${c.repositoryFullName} · ${c.branchRule??c.defaultBranch} · ${c.monorepoRoot}`);if(!t.yes&&!await O(`Disconnect ${o.name}?`,!1))return;n.disconnectConnection(o.id),g(`Disconnected ${o.name}`)})}catch(n){it(n)}})}import{readFileSync as Oc,writeFileSync as Pm}from"node:fs";import{resolve as tr}from"node:path";async function xm(){let r=await P(),e=q(process.cwd(),r),t=le(process.cwd());return{config:r,controlPlane:e,drafts:new Ec(e.store),artifacts:new Tc(e.store,{cwd:process.cwd()}),registries:new Dc(e.store,{encryptionKey:t}),sources:new er(e.store,{encryptionKey:t})}}async function xe(r){let e=await xm();try{return await r(e)}finally{e.controlPlane.store.close()}}function pe(r){p(r instanceof Error?r.message:"Application onboarding command failed")}function Ic(r){return JSON.parse(Oc(tr(r),"utf8"))}function Em(r){let e=r.site,t=["laravel","php","statamic","wordpress"].includes(e.type),n=["static","spa"].includes(e.type)||!e.start&&!t,i=n?{kind:"static",publishDirectory:e.root??e.webDirectory??"dist",buildCommand:e.build}:{kind:"server",runtime:t?"php":"bun",runtimeVersion:t?e.phpVersion:void 0,installCommand:t?"composer install --no-dev --prefer-dist --optimize-autoloader":void 0,buildCommand:e.build,startCommand:e.start??(t?"php -S 0.0.0.0:$PORT -t public":"bun run start")};return{schemaVersion:1,name:e.name??r.siteSlug,slug:r.siteSlug,projectId:r.projectId,environmentId:r.environmentId,source:r.source??{kind:"local",root:e.root??"."},build:i,runtime:{target:"server",architecture:e.architecture==="arm64"?"arm64":"x86_64",port:n?void 0:Number(e.port??3000),healthCheck:n?void 0:{protocol:"http",path:e.healthCheck?.path??"/health"}},domain:e.domain?{hostname:e.domain,tls:e.ssl?.provider!=="none"}:void 0}}function Ui(r){r.command("app:detect [root]","Detect application frameworks without executing project code").action(async(e=".")=>{try{let t=Pc(xc(tr(e)));x(["Framework","Strategy","Confidence","Evidence"],t.map((n)=>[n.framework,n.strategy,`${Math.round(n.confidence*100)}%`,n.evidence.map((i)=>i.path).join(", ")||"manual"]));for(let n of t)a(`${n.framework}: ${n.description}`)}catch(t){pe(t)}}),r.command("app:plan <draft>","Validate and preview a secret-free application draft").option("--secrets <names>","Comma-separated configured secret names").action(async(e,t)=>{try{let n=zi(Ic(e),t.secrets?.split(",").map((i)=>i.trim()).filter(Boolean));if(a(JSON.stringify(n,null,2)),!n.valid)pe(Error("Plan is not deployable; resolve the reported issues and missing secrets."))}catch(n){pe(n)}}),r.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(e,t)=>{try{await xe(async({drafts:n,controlPlane:i})=>{let o=Ic(e),s=t.secrets?.split(",").map((l)=>l.trim()).filter(Boolean)??[],c=t.id?n.update(t.id,Number(t.version),{draft:o,step:t.step,suppliedSecretNames:s}):n.create({organizationId:i.organization.id,projectId:i.project.id,name:o.name,draft:{...o,projectId:i.project.id},step:t.step,suppliedSecretNames:s});g(`Draft saved: ${c.id} · v${c.version} · ${c.status}`)})}catch(n){pe(n)}}),r.command("app:drafts","List resumable application drafts").action(async()=>{try{await xe(async({drafts:e,controlPlane:t})=>{let n=e.list(t.project.id);x(["ID","Application","Step","Status","Version","Updated"],n.map((i)=>[i.id,i.input.name,i.step,i.status,String(i.version),i.updatedAt]))})}catch(e){pe(e)}}),r.command("app:export <draft>","Export a deterministic application manifest").option("--output <path>","Output path",{default:"application.manifest.json"}).action(async(e,t)=>{try{await xe(async({drafts:n})=>{let i=n.get(e);if(!i)throw Error(`Draft ${e} was not found`);let o=zi(i.input,i.suppliedSecretNames),s=tr(t.output??"application.manifest.json");Pm(s,o.serializedManifest,{mode:384}),g(`Manifest exported: ${s}`)})}catch(n){pe(n)}}),r.command("app:deploy <draft>","Confirm and queue a validated application plan").option("--confirm <environment>","Exact target environment slug").action(async(e,t)=>{try{await xe(async({drafts:n,controlPlane:i})=>{let o=n.get(e);if(!o)throw Error(`Draft ${e} was not found`);if(!t.confirm)throw Error("Pass --confirm <environment> to acknowledge the target and recurring cost drivers");let s=Nc({controlPlane:i.store,drafts:n,draftId:e,expectedVersion:o.version,confirmEnvironment:t.confirm});g(`Application queued: ${s.operation.id}`),a(`Cost drivers: ${s.plan.costDrivers.join(", ")}`)})}catch(n){pe(n)}}),r.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(e,t)=>{try{await xe(async({config:n,controlPlane:i,sources:o,drafts:s})=>{let c=n.sites?.[e];if(!c)throw Error(`Site ${e} was not found`);let l=i.environments.get(t.env??"production");if(!l)throw Error(`Environment ${t.env} was not found`);let d;if(c.repository?.url){if(!t.connection)throw Error("Repository-backed imports require --connection <id>");let f=o.getConnection(t.connection);if(!f)throw Error("Git connection was not found");let h=Bi(c.repository.url),S=o.listRepositories(f.id).find((y)=>y.fullName.toLowerCase()===h.fullName.toLowerCase());if(!S)throw Error(`Repository ${h.fullName} is not synchronized`);d={kind:"git",connectionId:f.id,repositoryId:S.id,repositoryFullName:S.fullName,ref:c.repository.branch??"main"}}let u=Em({site:c,siteSlug:e,projectId:i.project.id,environmentId:l.id,source:d}),m=s.create({organizationId:i.organization.id,projectId:i.project.id,name:`Import ${e}`,draft:u,step:"review"});g(`Imported draft: ${m.id}`)})}catch(n){pe(n)}}),r.command("app:artifact:add <file>","Inspect and store a bounded ZIP or TAR artifact").action(async(e)=>{try{await xe(async({artifacts:t,controlPlane:n})=>{let i=tr(e),o=t.create({organizationId:n.organization.id,projectId:n.project.id,filename:i.split("/").pop(),bytes:Oc(i)});g(`Artifact stored: ${o.id} · ${o.entryCount} entries · ${o.sha256}`)})}catch(t){pe(t)}}),r.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(e,t)=>{try{await xe(async({registries:n,controlPlane:i})=>{let o={username:process.env[t.usernameEnv],password:process.env[t.passwordEnv],token:process.env[t.tokenEnv]},s=n.create({organizationId:i.organization.id,provider:t.provider,name:t.name,host:e,credential:Object.values(o).some(Boolean)?o:void 0});g(`Registry connection created: ${s.id}`)})}catch(n){pe(n)}}),r.command("app:registries","List safe registry connection metadata").action(async()=>{try{await xe(async({registries:e,controlPlane:t})=>x(["ID","Name","Provider","Host","Status"],e.list(t.organization.id).map((n)=>[n.id,n.name,n.provider,n.host,n.status])))}catch(e){pe(e)}}),r.command("app:registry:test <id>","Test registry and optional image pull access").option("--image <image>","Image reference").action(async(e,t)=>{try{await xe(async({registries:n})=>{let i=await n.test(e,{image:t.image});a(`${i.status}: ${i.healthMessage}`)})}catch(n){pe(n)}}),r.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(e,t)=>{try{await xe(async({registries:n})=>{let i={username:process.env[t.usernameEnv],password:process.env[t.passwordEnv],token:process.env[t.tokenEnv]};if(!Object.values(i).some(Boolean))throw Error("Set at least one configured registry credential environment variable");let o=n.rotate(e,i,{expiresAt:t.expires?new Date(t.expires).toISOString():void 0});g(`Registry credential rotated: ${o.id} · ${o.status}`)})}catch(n){pe(n)}}),r.command("app:registry:disconnect <id>","Disconnect and erase registry credentials").option("--yes","Skip confirmation").action(async(e,t)=>{try{await xe(async({registries:n,drafts:i,controlPlane:o})=>{let s=n.get(e);if(!s)throw Error(`Registry ${e} was not found`);let c=i.list(o.project.id).filter((l)=>l.input.source.kind==="image"&&l.input.source.registryConnectionId===e||l.input.build.kind==="prebuilt_image"&&l.input.build.registryConnectionId===e);if(a(`${c.length} application draft${c.length===1?"":"s"} reference this registry.`),!t.yes&&!await O(`Disconnect ${s.name}?`,!1))return;n.disconnect(e),g(`Disconnected ${s.name}`)})}catch(n){pe(n)}})}import{existsSync as Dm,readFileSync as Tm,writeFileSync as Nm}from"node:fs";function Vi(r,e){p(`'${r}' is not implemented yet.`),a(e),process.exitCode=1}function Om(r){let e={};for(let t of r.split(`
363
+ `)){let n=t.trim();if(!n||n.startsWith("#"))continue;let i=n.indexOf("=");if(i===-1)continue;let o=n.slice(0,i).trim(),s=n.slice(i+1).trim();if(s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'"))s=s.slice(1,-1);e[o]=s}return e}function Lm(r){return Object.entries(r).sort(([t],[n])=>t.localeCompare(n)).map(([t,n])=>{if(!/[\s#"']/.test(n))return`${t}=${n}`;let i='"'+n.replace(/"/g,"\\\"")+'"';return`${t}=${i}`}).join(`
354
364
  `)+`
355
- `}function PJ(G){G.command("env:list","List configured environments and their deployed stack status").action(async()=>{R("Environments");try{let $=await x(),J=Object.keys($.environments??{});if(!J.length){W("No environments configured. Add environments.<env> to your cloud.config.");return}let Y=[];for(let X of J){let Q=$.environments[X],Z=Q?.region||$.project.region||"us-east-1",z=Q?.app?`serverless (${Q.app.kind})`:"server/static",F="not deployed";if(Q?.app)try{let U=new Y$(Z),{Stacks:M}=await U.describeStacks({stackName:J2($,X)});F=M?.[0]?.StackStatus??"not deployed"}catch{F="not deployed"}Y.push([X,Q?.type??X,z,Z,F])}w(["Environment","Type","Kind","Region","Stack status"],Y)}catch($){K(`Failed to list environments: ${$.message}`),process.exitCode=1}}),G.command("env:compare <env1> <env2>","Compare two environments' app configuration").action(async($,J)=>{R(`Comparing ${$} ↔ ${J}`);try{let Y=await x(),X=Y.environments?.[$],Q=Y.environments?.[J];if(!X||!Q){K(`Both environments must exist in config (have: ${Object.keys(Y.environments??{}).join(", ")||"none"}).`),process.exitCode=1;return}let Z=(H,j="")=>{let V={};for(let[T,A]of Object.entries(H??{})){let q=j?`${j}.${T}`:T;if(A&&typeof A==="object"&&!Array.isArray(A))Object.assign(V,Z(A,q));else V[q]=Array.isArray(A)?JSON.stringify(A):String(A)}return V},z=Z(X),F=Z(Q),M=[...new Set([...Object.keys(z),...Object.keys(F)])].sort().filter((H)=>z[H]!==F[H]).map((H)=>[H,z[H]??"—",F[H]??"—"]);if(!M.length){B("Identical — no configuration differences.");return}w(["Setting",$,J],M),W(`
356
- ${M.length} difference(s)`)}catch(Y){K(`Comparison failed: ${Y.message}`),process.exitCode=1}}),G.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($)=>{try{let J=await x(),Y=$?.env||"production",{region:X,functions:Q}=V0(J,Y),Z=$?.function??"http",U=(await new e$(X).getFunction(Q[Z])).Configuration?.Environment?.Variables??{},M=$?.file??`.env.${Y}`;nG(M,iG(U)),B(`Wrote ${Object.keys(U).length} variable(s) from ${Q[Z]} → ${M}`)}catch(J){K(`env:pull failed: ${J.message}`),process.exitCode=1}}),G.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($)=>{try{let J=await x(),Y=$?.env||"production",{region:X,functions:Q}=V0(J,Y),Z=$?.file??`.env.${Y}`;if(!aG(Z)){K(`File not found: ${Z}`),process.exitCode=1;return}let z=sG(pG(Z,"utf8")),F=($?.function??"all").toLowerCase();if(F!=="all"&&!["http","queue","cli"].includes(F)){K("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let U=F==="all"?["http","queue","cli"]:[F],M=(J.environments?.[Y]?.app?.provisionedConcurrency??0)>0,H=new e$(X);for(let j of U){let V=Q[j],T=z;if(!$?.replace)T={...(await H.getFunction(V)).Configuration?.Environment?.Variables??{},...z};if(Y2(V,T),await H.updateFunctionConfiguration({FunctionName:V,Environment:{Variables:T}}),await H.waitForFunctionActive(V,120),M){let A=await H.publishVersion({FunctionName:V});if(A.Version)await H.updateAlias({FunctionName:V,Name:"live",FunctionVersion:A.Version})}B(`Pushed ${Object.keys(z).length} var(s) → ${V}${$?.replace?" (replaced)":" (merged)"}${M?" (published + flipped live alias)":""}`)}W("Note: a subsequent `cloud deploy:serverless` re-injects infra/secret env from config.")}catch(J){K(`env:push failed: ${J.message}`),process.exitCode=1}}),G.command("env:create <name>","Create new environment").action(async($)=>{NJ(`env:create ${$}`,"Add an `environments.<name>` block to your cloud.config and run `cloud deploy:serverless --env <name>`.")}),G.command("env:switch <name>","Switch active environment").action(async($)=>{NJ(`env:switch ${$}`,'ts-cloud has no persistent "active" environment — pass --env <name> to each command.')});for(let $ of["clone","promote","sync"])G.command(`env:${$} <source> <target>`,`(${$}) environment configuration`).action(async(J,Y)=>{NJ(`env:${$} ${J} ${Y}`,`Copy the \`environments.${J}\` block to \`environments.${Y}\` in cloud.config, then \`cloud deploy:serverless --env ${Y}\`.`)});G.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($,J)=>{let Y;try{let X=await x(),Q=J.env||"production",Z=J.site||Object.keys(X.sites??{})[0];if(!Z)throw Error("No application site is configured; pass --site after adding one");Y=u(process.cwd(),X);let z=Y.environments.get(Q),F=z?Y.store.listResources(Y.project.id,z.id).find((A)=>A.kind==="application"&&A.slug===Z):void 0;if(!z||!F)throw Error(`Application ${Z} was not found in base environment ${Q}`);let U=new r1(Y.store),M=U.previews.getDefinitionForResource(F.id);if(!M){let A=X.sites?.[Z]?.domain,q=J.domain||(A?`https://{name}.preview.${A}`:void 0);if(!q)throw Error("Pass --domain with an HTTPS pattern containing {name} to configure previews");M=U.previews.createDefinition({projectId:Y.project.id,resourceId:F.id,baseEnvironmentId:z.id,domainPattern:q,ttlHours:Number(J.ttl)||void 0})}let H=Number(J.pr)||void 0,j=H?U.previews.findForPullRequest(M.id,"local",H):U.previews.findForBranch(M.id,"local",$);if(J.getUrl){if(!j)throw Error("Preview was not found");W(j.url??"URL unavailable");return}if(J.extend){if(!j)throw Error("Preview was not found");let A=U.previews.extend(j.id,Number(J.extend));B(`Extended ${A.name} until ${A.expiresAt}`);return}if(J.destroy){if(!j)throw Error("Preview was not found");if(!J.yes&&!await k(`Destroy ${j.name} and only its tagged resources?`,!1))return;let A=U.enqueueDestroy(j,"manual");B(`Preview teardown queued: ${A.id}`);return}if(J.rebuild){if(!j)throw Error("Preview was not found");let A=U.enqueueDeploy(j,{reason:"manual_rebuild"});B(`Preview rebuild queued: ${A.id}`);return}if(!J.sha)throw Error("Pass --sha with the exact immutable commit to deploy");let V=U.previews.upsert({definitionId:M.id,repository:"local",branch:$,pullRequestNumber:H,commitSha:J.sha}),T=U.enqueueDeploy(V.preview,{created:V.created});B(`${V.created?"Preview creation":"Preview update"} queued: ${T.id}`),W(`${V.preview.url} · expires ${V.preview.expiresAt}`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y?.store.close()}}),G.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($)=>{let J;try{let Y=await x();J=u(process.cwd(),Y);let Q=new r1(J.store).cleanup({dryRun:$.dryRun,maxAgeHours:Number($.maxAge)||void 0,keepCount:Number($.keep)||void 0});w(["Preview","Status","Expires","Reason"],Q.candidates.map((Z)=>[Z.preview.name,Z.preview.status,Z.preview.expiresAt,Z.reasons.join(", ")])),B($.dryRun?`${Q.candidates.length} cleanup candidate(s)`:`${Q.operations.length} teardown job(s) queued`)}catch(Y){K(Y instanceof Error?Y.message:String(Y)),process.exitCode=1}finally{J?.store.close()}}),G.command("env:previews","List persistent preview environments").option("--site <name>","Filter application site").action(async($)=>{let J;try{let Y=await x();J=u(process.cwd(),Y);let X=new r1(J.store),Q=$.site?new Set(J.store.listResources(J.project.id).filter((z)=>z.slug===$.site).map((z)=>z.id)):void 0,Z=X.previews.listInstances({projectId:J.project.id}).filter((z)=>!Q||Q.has(z.resourceId));w(["ID","Name","Status","Branch / PR","Commit","URL","Expires","Cost"],Z.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(Y){K(Y instanceof Error?Y.message:String(Y)),process.exitCode=1}finally{J?.store.close()}})}import{readFileSync as a1}from"node:fs";async function N$(){let G=await x(),$=u(process.cwd(),G);return{config:G,controlPlane:$,applications:new H2($.store)}}async function SJ(G,$,J){let Y=await N$();try{let X=Y.applications.get(G);if(!X)throw Error("Compose application was not found");let Q=$??Y.controlPlane.store.listEnvironments(X.projectId).find((z)=>z.id===X.environmentId)?.slug;if(!Q)throw Error("Compose environment was not found");let Z=await K2(Y.config,Q,J(X));if(!Z.ok)throw Error(Z.stderr||Z.error||"Remote Compose command failed");if(Z.stdout)W(Z.stdout)}finally{Y.controlPlane.store.close()}}function _J(G){G.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($,J)=>{let Y=await N$();try{let X=Y.controlPlane.environments.get(J.env??"production");if(!X)throw Error("Environment was not found");let Q=Y.applications.preview(a1($,"utf8"),{name:J.name??"Compose application",projectId:Y.controlPlane.project.id,environmentId:X.id});if(w(["Severity","Path","Code","Message"],Q.diagnostics.map((Z)=>[Z.severity,Z.path,Z.code,Z.message])),!Q.valid)process.exitCode=1;else B(`${Object.keys(Q.manifest.spec.services).length} services are valid`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.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($,J)=>{let Y=await N$();try{let X=Y.controlPlane.environments.get(J.env??"production");if(!X)throw Error("Environment was not found");let Q=J.name??$.split("/").at(-2)??"Compose application",Z=Y.applications.import(a1($,"utf8"),{name:Q,projectId:Y.controlPlane.project.id,environmentId:X.id});B(`Imported ${Z.application.name} (${Z.application.id})`),W(`${Object.keys(Z.application.manifest.spec.services).length} services · ${Z.parsed.diagnostics.length} diagnostic(s)`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.command("compose:templates","List pinned verified Compose templates").action(()=>w(["ID","Version","Category","Architecture","Verified"],Z2().map(($)=>[$.id,$.version,$.category,$.architecture,$.lastVerifiedAt]))),G.command("compose:catalog <file>","Validate and inspect a local/custom template catalog").option("--env <environment>","Target environment",{default:"production"}).action(async($,J)=>{let Y=await N$();try{let X=Y.controlPlane.environments.get(J.env??"production");if(!X)throw Error("Environment was not found");let Q=W2(a1($,"utf8"),{projectId:Y.controlPlane.project.id,environmentId:X.id});if(w(["ID","Version","Category","Services","Checksum"],Q.templates.map((Z)=>[Z.template.id,Z.template.version,Z.template.category,String(Object.keys(Z.parsed.manifest.spec.services).length),Z.template.checksum.slice(0,12)])),Q.diagnostics.length)w(["Severity","Path","Message"],Q.diagnostics.map((Z)=>[Z.severity,Z.path,Z.message]));if(!Q.valid)process.exitCode=1}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.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($,J)=>{let Y=await N$();try{let X=Y.controlPlane.environments.get(J.env??"production");if(!X)throw Error("Environment was not found");let Q=Y.applications.fromTemplate($,{domain:J.domain??""},{name:J.name??$,projectId:Y.controlPlane.project.id,environmentId:X.id,version:J.version});B(`Created ${Q.application.name} from ${$}@${Q.application.templateVersion}`),W(`Export with cloud compose:export ${Q.application.id}`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.command("compose:list","List Compose applications").action(async()=>{let $=await N$();try{w(["ID","Name","Status","Services","Source","Updated"],$.applications.list({projectId:$.controlPlane.project.id}).map((J)=>[J.id,J.name,J.status,String(Object.keys(J.manifest.spec.services).length),J.templateId?`${J.templateId}@${J.templateVersion}`:"compose",J.updatedAt]))}finally{$.controlPlane.store.close()}}),G.command("compose:export <application>","Print a normalized editable Compose file").action(async($)=>{let J=await N$();try{let Y=J.applications.get($);if(!Y)throw Error("Compose application was not found");W(X2(Y.manifest))}finally{J.controlPlane.store.close()}}),G.command("compose:diff <application> <file>","Compare persisted and proposed Compose manifests").action(async($,J)=>{let Y=await N$();try{let X=Y.applications.get($);if(!X)throw Error("Compose application was not found");let Q=G2(a1(J,"utf8"),X.manifest.metadata);w(["Path","Before","After"],Q2(X.manifest,Q.manifest).map((Z)=>[Z.path,JSON.stringify(Z.before),JSON.stringify(Z.after)]))}finally{Y.controlPlane.store.close()}});for(let $ of["deploy","redeploy","start","stop"])G.command(`compose:${$} <application>`,`${$} a Compose application through the durable queue`).action(async(J)=>{let Y=await N$();try{let X=Y.applications.get(J);if(!X)throw Error("Compose application was not found");let Q=new p1(Y.controlPlane.store).enqueue(X,$);B(`${$} queued: ${Q.id}`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}});G.command("compose:scale <application> <service> <replicas>","Scale one capable Compose service").action(async($,J,Y)=>{let X=await N$();try{let Q=X.applications.get($);if(!Q)throw Error("Compose application was not found");let Z=new p1(X.controlPlane.store).enqueue(Q,"scale",{service:J,replicas:Number(Y)});B(`Scale queued: ${Z.id}`)}catch(Q){K(Q instanceof Error?Q.message:String(Q)),process.exitCode=1}finally{X.controlPlane.store.close()}}),G.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($,J)=>{let Y=await N$();try{let X=Y.applications.get($);if(!X)throw Error("Compose application was not found");let Q=new p1(Y.controlPlane.store).enqueue(X,"delete",{removeVolumes:J.removeVolumes,confirmation:J.confirm});B(`Deletion queued: ${Q.id}`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.command("compose:logs <application> <service>","Read ordered service logs").option("--lines <count>","Tail line count",{default:"200"}).option("--env <environment>","Target environment").action(($,J,Y)=>SJ($,Y.env,(X)=>U2(X.manifest,J,Number(Y.lines)||200))),G.command("compose:shell <application> <service> [command...]","Run a command in one service").option("--env <environment>","Target environment").action(($,J,Y,X)=>SJ($,X.env,(Q)=>M2(Q.manifest,J,Y?.length?Y:["sh"]))),G.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(($,J)=>SJ($,J.env,(Y)=>{if(J.action==="scale")return F2(Y.manifest,J.service??"",Number(J.replicas));return z2(Y.manifest,J.action??"deploy",{service:J.service,removeVolumes:J.removeVolumes})}))}import{readFileSync as tG}from"node:fs";async function P$(){let G=await x(),$=u(process.cwd(),G);return{controlPlane:$,releases:new B2($.store)}}function bJ(G){if(!G)return{};return JSON.parse(tG(G,"utf8"))}function IJ(G){G.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($,J)=>{let Y=await P$();try{let X=Y.releases.releases.registerArtifact({organizationId:Y.controlPlane.organization.id,digest:String(J.digest??""),kind:J.kind??"compute",uri:$,size:Number(J.size),mediaType:J.mediaType});B(`Artifact registered: ${X.id}`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.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($,J,Y)=>{let X=await P$();try{let Q=X.controlPlane.environments.get(Y.env??"production"),Z=Q?X.controlPlane.store.listResources(X.controlPlane.project.id,Q.id).find((F)=>F.slug===$):void 0;if(!Q||!Z)throw Error("Release resource was not found");let z=X.releases.releases.create({organizationId:X.controlPlane.organization.id,projectId:X.controlPlane.project.id,environmentId:Q.id,resourceId:Z.id,artifactId:J,kind:Y.kind??"compute",sourceSha:Y.sourceSha,config:bJ(Y.config),manifest:bJ(Y.manifest),strategy:Y.strategy??"atomic",healthGate:Y.healthPath?{protocol:"http",path:Y.healthPath,timeoutSeconds:5,intervalSeconds:5,healthyThreshold:2,unhealthyThreshold:2}:void 0,approvalRequired:Y.approval,trigger:Y.trigger});B(`Release created: ${z.id} (${z.status})`)}catch(Q){K(Q instanceof Error?Q.message:String(Q)),process.exitCode=1}finally{X.controlPlane.store.close()}}),G.command("release:list","List unified release history").option("--env <environment>","Filter environment").action(async($)=>{let J=await P$();try{let Y=$.env?J.controlPlane.environments.get($.env):void 0;w(["ID","Resource","Status","Kind","Artifact","Strategy","Source","Created"],J.releases.releases.list({projectId:J.controlPlane.project.id,environmentId:Y?.id}).map((X)=>[X.id,J.controlPlane.store.getResource(X.resourceId)?.slug??X.resourceId,X.status,X.kind,X.artifactId.slice(0,12),X.strategy,X.sourceSha?.slice(0,12)??"—",X.createdAt]))}finally{J.controlPlane.store.close()}}),G.command("release:show <release>","Show provenance, health, approvals, and transitions").action(async($)=>{let J=await P$();try{let Y=J.releases.releases.get($);if(!Y)throw Error("Release was not found");W(JSON.stringify({release:Y,artifact:J.releases.releases.getArtifact(Y.artifactId),approvals:J.releases.releases.approvals($),transitions:J.releases.releases.transitions($)},null,2))}finally{J.controlPlane.store.close()}}),G.command("release:compare <left> <right>","Compare artifact, source, config, manifest, and data caveats").action(async($,J)=>{let Y=await P$();try{let X=Y.releases.releases.compare($,J);W(JSON.stringify(X,null,2))}finally{Y.controlPlane.store.close()}}),G.command("release:capabilities <kind>","Explain supported deployment strategies").option("--health","Readiness gate is configured").option("--replicas <count>","Replica count",{default:"1"}).action(($,J)=>w(["Strategy","Supported","Capacity","Cost","Explanation","Rollback"],j2({kind:$,hasHealthGate:!!J.health,replicas:Number(J.replicas)}).map((Y)=>[Y.strategy,Y.supported?"yes":"no",`${Y.capacityMultiplier}x`,Y.costImpact,Y.explanation,Y.rollback]))),G.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($,J,Y)=>{let X=await P$();try{let Q=X.controlPlane.environments.get(String(Y.env??"")),Z=Q?X.controlPlane.store.listResources(X.controlPlane.project.id,Q.id).find((F)=>F.slug===J):void 0;if(!Q||!Z)throw Error("Promotion target was not found");let z=X.releases.releases.promote($,{targetEnvironmentId:Q.id,targetResourceId:Z.id,config:bJ(Y.config),approvalRequired:Y.approval});B(`Promoted without rebuild: ${z.id} (${z.status})`)}catch(Q){K(Q instanceof Error?Q.message:String(Q)),process.exitCode=1}finally{X.controlPlane.store.close()}}),G.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($,J)=>{let Y=await P$();try{if(!J.actor)throw Error("--actor is required");let X=Y.releases.releases.approve($,{actorId:J.actor,decision:J.reject?"rejected":"approved",comment:J.comment});B(`Release is ${X.status}`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.command("release:activate <release>","Queue immutable artifact activation").action(async($)=>{let J=await P$();try{let Y=J.releases.releases.get($);if(!Y)throw Error("Release was not found");let X=J.releases.enqueueActivation(Y);B(`Activation queued: ${X.id}`)}catch(Y){K(Y instanceof Error?Y.message:String(Y)),process.exitCode=1}finally{J.controlPlane.store.close()}}),G.command("release:rollback <release>","Restore the preserved prior immutable release").option("--to <release>","Explicit preserved release ID").action(async($,J)=>{let Y=await P$();try{let X=Y.releases.releases.get($);if(!X)throw Error("Release was not found");let Q=Y.releases.enqueueRollback(X,{targetReleaseId:J.to});B(`Rollback queued: ${Q.id}`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.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($,J)=>{let Y=await P$();try{let X=Y.releases.completeHealthGate($,{healthy:!!J.healthy,operationId:J.operation,message:J.message});B(X.rollbackOperation?`Health failed; rollback queued: ${X.rollbackOperation.id}`:`Release is ${X.release.status}`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}finally{Y.controlPlane.store.close()}}),G.command("release:pin <release>","Pin or unpin a release against retention").option("--remove","Remove pin").option("--reason <text>","Pin reason").action(async($,J)=>{let Y=await P$();try{let X=Y.releases.releases.pin($,!J.remove,J.reason);B(X.pinned?"Release pinned.":"Release unpinned.")}finally{Y.controlPlane.store.close()}})}import{existsSync as eG}from"node:fs";function kJ(G){G.command("assets:build","Run the configured build hooks for an environment").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).action(async($)=>{R("Building Assets");try{let J=await x(),Y=$?.env||"production",Q=J.environments?.[Y]?.app?.build??[];if(!Q.length){K(`No build hooks defined for '${Y}'. Set environments.${Y}.app.build = ['bun run build', …] in cloud.config.`),process.exitCode=1;return}let{execSync:Z}=await import("node:child_process");for(let z of Q)y(`$ ${z}`),Z(z,{stdio:"inherit"});B("Build hooks complete")}catch(J){K(`Build failed: ${J.message}`),process.exitCode=1}});for(let $ of["assets:optimize:images","images:optimize"])G.command($,"Optimize images (not implemented)").option("--dir <directory>","Directory to optimize").action(async()=>{K(`'${$}' is not implemented — ts-cloud has no built-in image optimizer.`),W("Optimize images in your own build step (e.g. sharp/squoosh), then `cloud assets:deploy`."),process.exitCode=1});G.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($)=>{R("Deploying Assets to S3");try{let Y=(await x()).project.region||"us-east-1",X=$?.source||"dist",Q=$?.bucket,Z=$?.prefix,z=$?.delete||!1,F=$?.cacheControl||"public, max-age=31536000";if(!Q){K("--bucket is required");return}if(!eG(X)){K(`Source directory not found: ${X}`);return}if(W(`Source: ${X}`),W(`Bucket: s3://${Q}${Z?`/${Z}`:""}`),W(`Cache-Control: ${F}`),z)N("Delete mode enabled - files not in source will be removed");if(!await k(`
357
- Deploy assets now?`,!0)){W("Deployment cancelled");return}let M=new Z$(Y),H=new E("Uploading assets to S3...");H.start(),await M.sync({source:X,bucket:Q,prefix:Z,delete:z,cacheControl:F,acl:"public-read"}),H.succeed("Assets deployed successfully!");let V=(await M.getBucketSize(Q,Z)/1024/1024).toFixed(2);B(`
358
- Deployment complete!`),W(`Total size: ${V} MB`),W(`
359
- Assets URL: https://${Q}.s3.${Y}.amazonaws.com${Z?`/${Z}`:""}`)}catch(J){K(`Deployment failed: ${J.message}`)}}),G.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($)=>{R("Invalidating CloudFront Cache");try{let J=$?.distribution;if(!J){K("--distribution is required");return}let X=($?.paths||"/*").split(",").map((M)=>M.trim()),Q=$?.wait||!1;if(W(`Distribution: ${J}`),W(`Paths: ${X.join(", ")}`),!await k(`
360
- Invalidate cache now?`,!0)){W("Invalidation cancelled");return}let z=new $$,F=new E("Creating invalidation...");F.start();let U=await z.invalidatePaths(J,X);if(F.succeed("Invalidation created"),B(`
361
- Invalidation ID: ${U.Id}`),W(`Status: ${U.Status}`),W(`Created: ${new Date(U.CreateTime).toLocaleString()}`),Q){let M=new E("Waiting for invalidation to complete...");M.start(),await z.waitForInvalidation(J,U.Id),M.succeed("Invalidation completed!")}else W(`
362
- Invalidation is in progress. Use --wait to wait for completion.`)}catch(J){K(`Invalidation failed: ${J.message}`)}})}function hJ(G){for(let[$,J]of[["team:add <email> <role>","Invite an organization member"],["team:list","List organization members"],["team:remove <email>","Revoke organization membership"]])G.command($,J).action(async(...Y)=>V$($.split(" ")[0],{target:String(Y[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 ZX,execSync as H5}from"node:child_process";import{copyFileSync as K1,existsSync as H$,mkdirSync as C2,readFileSync as tJ,writeFileSync as WX}from"node:fs";import{tmpdir as f2}from"node:os";import{dirname as zX,join as g2,resolve as FX}from"node:path";import{existsSync as yJ,readdirSync as V2,statSync as CJ}from"node:fs";import{join as n1}from"node:path";var oG=new Set([".DS_Store","Thumbs.db",".gitkeep",".keep"]);function A2(G){return oG.has(G)}function q2(G,$=0){if($>8)return!0;let J;try{J=V2(G)}catch{return!1}for(let Y of J){if(A2(Y))continue;let X=n1(G,Y),Q;try{Q=CJ(X)}catch{continue}if(Q.isFile())return!0;if(Q.isDirectory()&&q2(X,$+1))return!0}return!1}function $X(G){let $;try{$=V2(G)}catch{return[]}let J=[];for(let Y of $){if(A2(Y))continue;let X=n1(G,Y);try{if(CJ(X).isDirectory()&&yJ(n1(X,"index.html")))J.push(Y)}catch{}}return J.sort((Y,X)=>Y.localeCompare(X))}function R2(G){let{root:$,siteName:J,expectIndex:Y}=G,X=[];if(!yJ($))return X.push({level:"error",message:`Build output not found at ${$} for site '${J}'`}),X;if(!CJ($).isDirectory())return X.push({level:"error",message:`Site '${J}' has root ${$}, which is a file, not a directory`}),X;if(!q2($))return X.push({level:"error",message:`Site '${J}' would ship an empty release: ${$} contains no files. Check that the build ran and wrote where 'root' points.`}),X;if(!Y)return X;if(yJ(n1($,"index.html")))return X;let Q=$X($);if(Q.length===1){let Z=`${$.replace(/\/+$/,"")}/${Q[0]}`;return X.push({level:"error",message:`Site '${J}' has no index.html at the top of ${$}, but ${Z} has one. The generator wrote one level down, so this release would serve 404s at the domain root. Set root: '${Z}'.`}),X}if(Q.length>1)return X.push({level:"warning",message:`Site '${J}' has no index.html at the top of ${$}. These subdirectories have one: ${Q.join(", ")}. The domain root will 404.`}),X;return X.push({level:"warning",message:`Site '${J}' has no index.html anywhere under ${$}. The domain root will 404.`}),X}function JX(G){if(!G)return[];if(Array.isArray(G))return G.map(String);let $=G,J=$.Items??$.Item;if(!J)return[];if(Array.isArray(J))return J.map(String);if(typeof J==="object"&&J!==null){let Y=J;if("Item"in Y){let X=Y.Item;if(!X)return[];return Array.isArray(X)?X.map(String):[String(X)]}if("CNAME"in Y){let X=Y.CNAME;if(!X)return[];return Array.isArray(X)?X.map(String):[String(X)]}return Object.values(Y).flatMap((X)=>{if(Array.isArray(X))return X.map(String);return typeof X==="string"?[X]:[]})}return[String(J)]}async function s1(G){if(G.length===0)return;let $=new $$,J=await $.listDistributions(),Y=[...new Set(G.map((X)=>X.toLowerCase()))];for(let X of Y){let Q=J.find((z)=>{return JX(z.Aliases).some((U)=>U.toLowerCase()===X)});if(!Q?.Id){N(`No CloudFront distribution found for ${X} — skipping dynamic HTTP method sync`);continue}if(await $.ensureDynamicHttpMethods(Q.Id))B(`CloudFront ${Q.Id} (${X}): enabled POST/PUT/PATCH/DELETE on dynamic paths`);else W(`CloudFront ${Q.Id} (${X}): already allows dynamic HTTP methods`)}}import{execSync as YX}from"node:child_process";async function GX(G,$,J,Y={},X=process.cwd()){if(!G)return;if(Y.step?.(`Running ${J} hook`),typeof G==="string"){YX(G,{stdio:"inherit",cwd:X});return}await G($)}async function M1(G,$,J={},Y){try{return await GX(G.hooks?.[$],G,$,J,Y),!0}catch(X){return J.error?.(`${$} hook failed: ${X?.message||X}`),!1}}var L2=["js","mjs","cjs","css","map","woff","woff2","ttf","otf","eot","png","jpg","jpeg","gif","webp","avif","svg","ico","mp4","webm","ogg","mp3","wav","pdf","wasm"];function fJ(G){return`"${G.replace(/\\/g,"\\\\").replace(/"/g,"\\\"")}"`}function E2(G){return`{${G.map(fJ).join(" ")}}`}function gJ(G){if(G.length===1)return`http.host eq ${fJ(G[0])}`;return`http.host in ${E2(G)}`}function vJ(G,$={}){if($.enabled===!1||G.length===0)return[];let J=gJ(G),Y=[],X=$.bypassPaths??[],Q=X.length>0?`(${X.map((U)=>`starts_with(http.request.uri.path, ${fJ(U)})`).join(" or ")})`:void 0;if(Q)Y.push({action:"set_cache_settings",description:"bypass cache",expression:`(${J} and ${Q})`,enabled:!0,action_parameters:{cache:!1}});let Z=Q?`${J} and not ${Q}`:J,z=$.assetExtensions??L2,F=z.length>0?`lower(http.request.uri.path.extension) in ${E2([...z])}`:void 0;if(F)Y.push({action:"set_cache_settings",description:"cache fingerprinted assets",expression:`(${Z} and ${F})`,enabled:!0,action_parameters:{cache:!0,edge_ttl:{mode:"override_origin",default:$.assetEdgeTtl??2592000},browser_ttl:{mode:"override_origin",default:$.assetBrowserTtl??31536000}}});return Y.push({action:"set_cache_settings",description:"cache documents",expression:F?`(${Z} and not ${F})`:`(${Z})`,enabled:!0,action_parameters:{cache:!0,edge_ttl:{mode:"override_origin",default:$.documentEdgeTtl??3600},browser_ttl:{mode:"override_origin",default:$.documentBrowserTtl??0}}}),Y}function mJ(G,$,J){if(G.length===0||!$||!J)return[];return[{action:"rewrite",description:"origin guard header",expression:`(${gJ(G)})`,enabled:!0,action_parameters:{headers:{[$]:{operation:"set",value:J}}}}]}var Y0=(G)=>G===void 0?void 0:G?"on":"off";function dJ(G){let $={ssl:G.ssl,always_use_https:Y0(G.alwaysUseHttps),automatic_https_rewrites:Y0(G.automaticHttpsRewrites),min_tls_version:G.minTlsVersion,tls_1_3:Y0(G.tls13),brotli:Y0(G.brotli),http3:Y0(G.http3),"0rtt":Y0(G.zeroRtt),early_hints:Y0(G.earlyHints),browser_cache_ttl:G.browserCacheTtl,always_online:Y0(G.alwaysOnline),websockets:Y0(G.websockets)};if(G.hsts)$.security_header={strict_transport_security:{enabled:G.hsts.enabled,max_age:G.hsts.maxAge??31536000,include_subdomains:G.hsts.includeSubdomains??!1,preload:G.hsts.preload??!1,nosniff:G.hsts.noSniff??!0}};for(let J of Object.keys($))if($[J]===void 0)delete $[J];return{...$,...G.raw??{}}}var H1={ssl:"strict",alwaysUseHttps:!0,automaticHttpsRewrites:!0,minTlsVersion:"1.2",tls13:!0,brotli:!0,http3:!0,earlyHints:!0,alwaysOnline:!0,browserCacheTtl:0};import{connect as XX}from"node:tls";async function cJ(G){let{address:$,serverName:J,port:Y=443,timeoutMs:X=5000}=G;return new Promise((Q)=>{let Z=!1,z=(U)=>{if(Z)return;Z=!0;try{F.destroy()}catch{}Q(U)},F=XX({host:$,port:Y,servername:J,rejectUnauthorized:!1,timeout:X});F.once("secureConnect",()=>{if(F.authorized){z({reachable:!0,trusted:!0});return}z({reachable:!0,trusted:!1,reason:F.authorizationError?String(F.authorizationError):"origin certificate is not trusted for this hostname"})}),F.once("timeout",()=>{z({reachable:!1,trusted:!1,reason:`no TLS handshake within ${X}ms`})}),F.once("error",(U)=>{z({reachable:!1,trusted:!1,reason:U.message})})})}async function lJ(G){let{provider:$,zone:J,hosts:Y,ipv4:X,ipv6:Q,proxied:Z=!0,settings:z=H1,cache:F,originGuard:U,purge:M=!0,ttl:H=300,skipOriginProbe:j=!1}=G,V={records:[],settingsChanged:[],cacheRules:0,originGuard:!1,purged:!1,deferredProxy:[],warnings:[]};if(Y.length===0)return V;let T=[];for(let q of Y){let O=Z;if(Z&&!j){let D=await cJ({address:X,serverName:q});if(!D.trusted){O=!1;let P=D.reason||"origin is not serving a trusted certificate";V.deferredProxy.push({host:q,reason:P}),V.warnings.push(`${q}: publishing DNS-only for now — the origin does not yet serve a certificate this proxy can verify (${P}). 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(O)T.push(q);let L=[{type:"A",content:X}];if(Q&&uJ(q))L.push({type:"AAAA",content:Q});for(let{type:D,content:P}of L){let b=await $.upsertRecord(J,{name:q,type:D,content:P,ttl:H,proxied:O});if(b.success)V.records.push({host:q,type:D,content:P,proxied:O});else V.warnings.push(`${q} ${D} → ${P} failed: ${b.message||"unknown provider error"}`)}}if(T.length===0){if(Z)V.warnings.push("No host is proxied yet, so zone settings, cache rules and purge were skipped — they only take effect through the proxy.");return V}try{let q=dJ(z),O=await $.applyZoneSettings(J,q);V.settingsChanged=O.changed;for(let L of O.failed)V.warnings.push(`zone setting '${L.id}' could not be applied: ${L.error}`)}catch(q){V.warnings.push(`zone settings could not be read: ${q instanceof Error?q.message:String(q)}`)}let A=vJ(T,F);if(A.length>0){let q=await $.putManagedPhaseRules(J,"http_request_cache_settings",A);if(q.success)V.cacheRules=A.length;else V.warnings.push(`cache rules could not be written: ${q.message}`)}if(U){let q=mJ(T,U.header,U.value);if(q.length>0){let O=await $.putManagedPhaseRules(J,"http_request_late_transform",q);if(O.success)V.originGuard=!0;else V.warnings.push(`origin guard header could not be written: ${O.message}. `+"The gateway will reject requests that lack it — disable the guard or grant the token Transform Rules: Edit.")}}if(M){if(V.purged=await $.purgeCache(J,{hosts:T}),!V.purged)V.warnings.push("edge cache purge failed — new content may take until TTL expiry to appear.")}return V}var QX=(G)=>process.env[G];function T2(G,$){let J=G.replace(/\.$/,"").toLowerCase();if($){let Y=$.replace(/\.$/,"").toLowerCase();if(J===Y||J.endsWith(`.${Y}`))return Y}return J.split(".").slice(-2).join(".")}function aJ(G,$={}){let J=$.env??QX,Y=G.infrastructure?.compute?.proxy?.cdn,X=[];if(!Y||Y.provider!=="cloudflare")return{plan:null,errors:X};if(Y.originDomain)X.push(`compute.proxy.cdn.originDomain (${Y.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 Q=J("CLOUDFLARE_API_TOKEN");if(!Q)return X.push("CLOUDFLARE_API_TOKEN is not set — the Cloudflare CDN cannot be reconciled."),{plan:null,errors:X};let Z=G.sites??{},z=Y.frontedHosts?.length?[...new Set(Y.frontedHosts.map((j)=>j.toLowerCase()))]:rJ(Z,{autoWww:G.infrastructure?.compute?.proxy?.autoWww!==!1});if(z.length===0)return X.push("No hostnames to front — set compute.proxy.cdn.frontedHosts, or give a site a domain."),{plan:null,errors:X};let F=G.infrastructure?.dns?.domain,U=new Set(z.map((j)=>T2(j,F)));if(U.size>1)return X.push(`Fronted hosts span more than one zone (${[...U].join(", ")}). A Cloudflare zone is configured as a unit, so run one CDN config per zone.`),{plan:null,errors:X};let M=Y.cloudflare??{},H=Y.secret?{header:Y.secretHeader??"X-Origin-Verify",value:Y.secret}:void 0;return{plan:{zone:[...U][0],hosts:z,apiToken:Q,zoneId:M.zoneId??J("CLOUDFLARE_ZONE_ID"),accountId:M.accountId??J("CLOUDFLARE_ACCOUNT_ID"),proxied:M.proxied!==!1,settings:M.settings??H1,cache:M.cache,originGuard:H,purge:M.purgeOnDeploy!==!1,skipOriginProbe:M.skipOriginProbe===!0},errors:X}}async function UX(G){let $=O2();if($.emptyEnvKey||$.emptyEnvSecret)N("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.");W(`AWS credentials: ${$.description}`);try{let Y=await new P0(G).getCallerIdentity();if(Y.Arn)W(`AWS identity: ${Y.Arn}`)}catch(J){N(`Could not verify AWS identity: ${J.message}`)}}function MX(){try{return H5("git rev-parse --short HEAD",{stdio:["ignore","pipe","ignore"]}).toString().trim()}catch{return Date.now().toString(36)}}async function pJ(G,$,J,Y){let{ensureManagementDashboard:X}=await import("./chunk-e0rkrq70.js");X(G,{logger:{info:W,warn:N}});let Q=G.sites||{},Z=Object.entries(Q);if(Z.length===0)return N("infrastructure.compute is set but no sites are configured — nothing to deploy. Add sites to `cloud.config.ts`."),!0;if(Y&&!Q[Y])return K(`Site '${Y}' was not found. Configured sites: ${Z.map(([O])=>O).join(", ")}`),!1;let z=Z.filter(([O,L])=>{if(!L)return!1;if(Y&&O!==Y)return!1;if(B1(L)==="bucket")return N(`Site '${O}' targets a bucket — skipping compute deploy (handled by the static-site path).`),!1;return Y5(L)});if(Y)W(`Deploying only '${Y}'. Other sites keep their current release; proxy routes are still regenerated in full.`);let F=G.project.slug,M=(G.infrastructure?.compute||{}).runtime||"bun",H=c$({config:G}),j=MX(),V=new Map,T=new Map,A={step:(O)=>y(O),error:(O)=>K(O)};if(!await M1(G,"beforeDeploy",A))return!1;if(!await M1(G,"beforeBuild",A))return!1;for(let[O,L]of z){let D=B1(L);if(R(`Preparing site: ${O}`),W(`Domain: ${L.domain||"(none)"}`),W(`Source: ${L.root}`),W(`Kind: ${D==="server-static"?"static (shipped to /var/www)":"app (systemd service)"}`),D==="server-app")W(`Start: ${L.start}`),W(`Port: ${L.port??"(unset)"}`);if(L.build){y(`Running build: ${L.build}`);try{H5(L.build,{stdio:"inherit",cwd:process.cwd()})}catch(I){return K(`Build failed for site '${O}': ${I.message}`),!1}}let P=R2({root:L.root,siteName:O,expectIndex:D==="server-static"});for(let I of P)if(I.level==="error")K(I.message);else N(I.message);if(P.some((I)=>I.level==="error"))return!1;let b=L.build?void 0:JSON.stringify([FX(L.root),[...L.exclude||[]].sort()]),_=b?T.get(b):void 0;if(_){y(`Reusing package for ${L.root} → ${_}`),V.set(O,_);continue}let S=g2(f2(),`${F}-${O}-${j}.tar.gz`);y(`Packaging ${L.root} → ${S}`);try{let I=(L.exclude||[]).map((g)=>`--exclude=${g}`);ZX("tar",["czf",S,"-C",L.root,...I,"."],{stdio:"inherit",env:{...process.env,COPYFILE_DISABLE:"1"}})}catch(I){return K(`Failed to package '${O}': ${I.message}`),!1}if(V.set(O,S),b)T.set(b,S)}if(!await M1(G,"afterBuild",A))return!1;let q=await Q5({config:Y?{...G,sites:Object.fromEntries(z)}:G,rpxConfig:Y?G:void 0,environment:$,driver:H,sha:j,runtime:M,tarballForSite:(O)=>{let L=V.get(O);if(!L)throw Error(`Missing tarball for site '${O}'`);return L},logger:{info:(O)=>W(O),warn:(O)=>N(O),error:(O)=>K(O),step:(O)=>y(O),success:(O)=>B(O)}});if(q&&!await M1(G,"afterDeploy",A))return!1;return q}async function i1(G){let $=new I2;if(y("Running pre-deployment security scan..."),G.exclude?.length)W(`Excluding from the scan: ${G.exclude.join(", ")}`);let J=await $.scan({directory:G.sourceDir,failOnSeverity:G.failOnSeverity||"critical",skipPatterns:G.skipPatterns,exclude:G.exclude});return{passed:J.passed,result:J}}function HX(){let G=g2(f2(),`ts-cloud-empty-scan-${process.pid}`);return C2(G,{recursive:!0}),G}function t1(G){let{summary:$,findings:J,scannedFiles:Y,duration:X}=G;if(W(`
363
- Scanned ${Y} files in ${X}ms`),$.critical>0)K(` Critical: ${$.critical}`);else W(` Critical: ${$.critical}`);if($.high>0)N(` High: ${$.high}`);else W(` High: ${$.high}`);if(W(` Medium: ${$.medium}`),W(` Low: ${$.low}`),J.length>0){W(`
364
- Findings:`);let Q=J.filter((M)=>M.pattern.severity==="critical"),Z=J.filter((M)=>M.pattern.severity==="high"),z=J.filter((M)=>M.pattern.severity==="medium"),F=J.filter((M)=>M.pattern.severity==="low"),U=(M,H,j)=>{if(M.length>0){console.log(`
365
- ${w2(`[${H}]`,j)}`);for(let V of M.slice(0,10))W(` ${V.pattern.name}`),W(` File: ${V.file}:${V.line}`),W(` Match: ${V.match}`);if(M.length>10)W(` ... and ${M.length-10} more ${H.toLowerCase()} findings`)}};U(Q,"CRITICAL","red"),U(Z,"HIGH","yellow"),U(z,"MEDIUM","blue"),U(F,"LOW","gray")}}function v2(G,$){let J=Object.keys(G.environments??{});if($&&J.includes($))return $;if(J.includes("production"))return"production";if(J[0])return J[0];throw Error("A configured environment is required for security policy evaluation")}function nJ(G,$,J){let Y=v2(G,$),X=u(process.cwd(),G);try{let Q=new z5(X.store);U5(X);let Z=F5(X,Y);if(J)k2(Q,Z,J);else h2(Q,Z);let z=M5(Q,{scope:Z,desiredConfigHash:X.project.desiredConfigHash});return W(`Security policy: ${z.decision.policyId} v${z.decision.policyVersion} — ${z.decision.outcome.toUpperCase()}`),W(`Security decision: ${z.decision.explanation}`),{allowed:z.decision.outcome!=="block",explanation:z.decision.explanation}}finally{X.store.close()}}async function KX(G,$){let J=v2(G),Y=u(process.cwd(),G);try{let X=new z5(Y.store);U5(Y);let Q=F5(Y,J),Z=$.imageSha256?`${$.imageRef}@sha256:${$.imageSha256.replace(/^sha256:/,"")}`:$.imageRef;y("Scanning the built image and producing release security artifacts...");let z=await y2(X,{scope:Q,releaseId:Z,imageRef:$.imageRef,imageSha256:$.imageSha256,artifactRoot:$.artifactRoot,invocationId:$.invocationId,startedAt:$.startedAt,externalParameters:{imageRef:$.imageRef,artifactRoot:$.artifactRoot}});W(`Image scanner: ${z.scan.run.status} (${z.scan.findings.length} findings)`),W(`Release SBOM: ${z.sbomSource==="image"?"full image inventory":z.sbomSource==="manifest"?"package-manifest fallback":"unavailable"}`);let F=M5(X,{scope:Q,operationId:$.invocationId,desiredConfigHash:Y.project.desiredConfigHash});return W(`Release policy: ${F.decision.policyId} v${F.decision.policyVersion} — ${F.decision.outcome.toUpperCase()}`),W(`Release decision: ${F.decision.explanation}`),{allowed:F.decision.outcome!=="block",explanation:F.decision.explanation}}finally{Y.store.close()}}function jX(G){try{let $=u(process.cwd(),G),J=new z1($.store).current();if(!J.attackMode&&!J.mitigationPause&&J.ipRules.allow.length===0&&J.ipRules.block.length===0)return G;let Y=b2(G,J);return W(`Applying protection controls: ${J.ipRules.allow.length} allow, ${J.ipRules.block.length} block${J.attackMode?", attack mode ON":""}${J.mitigationPause?", mitigation PAUSED":""}`),Y}catch{return G}}function K5(G){G.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($)=>{R("Pre-Deployment Security Scan");try{let J=$?.source||".",Y=$?.failOn||"critical",X=$?.skipPatterns?.split(",").map((z)=>z.trim())||[];if(!H$(J)){K(`Source directory not found: ${J}`);return}W(`Source: ${J}`),W(`Fail on: ${Y} or higher severity`);let{passed:Q,result:Z}=await i1({sourceDir:J,failOnSeverity:Y,skipPatterns:X});if(t1(Z),Q)B(`
366
- ✓ Security scan passed - no blocking issues found`);else K(`
367
- ✗ Security scan failed - blocking issues detected`),W(`
368
- Recommendations:`),W(" 1. Remove any hardcoded credentials from your code"),W(" 2. Use environment variables or AWS Secrets Manager"),W(" 3. Add sensitive files to .gitignore"),W(" 4. Use --skip-patterns to ignore false positives"),process.exit(1)}catch(J){K(`Security scan failed: ${J.message}`),process.exit(1)}}),G.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($)=>{R("Deploying Infrastructure");let J=$?.yes===!0||process.env.CI==="true",Y=$?.env||"staging",X=null;X=await BX(Y);try{let Q=await x(),Z=jX(Q),{errors:z,warnings:F}=J5(Z);for(let C of F)N(C);if(z.length>0){K(`
369
- ✗ Deployment configuration is invalid:`);for(let C of z)K(` • ${C}`);process.exitCode=1;return}if($?.dryRun){if($.site&&!Z.sites?.[$.site]){K(`Site '${$.site}' was not found. Configured sites: ${Object.keys(Z.sites||{}).join(", ")||"none"}`),process.exitCode=1;return}let C=j1(Z),c=$.stack||i$(Z,Y),z$=c0(Z),X$=Z.project.region||"us-east-1",h$=$.site?[$.site]:Object.keys(Z.sites||{});R("Deployment Plan"),W(`Cloud provider: ${z$}`),W(`Mode: ${C}`),W(`Stack: ${c}`),W(`Region: ${X$}`),W(`Environment: ${Y}`),W(`Sites: ${h$.length>0?h$.join(", "):"none"}`),B("Dry run complete. No infrastructure, DNS, certificates, or application files were changed.");return}if(c0(Z)==="aws"){let C=Z.project?.region||"us-east-1";await UX(C)}let U;if(!$?.skipSecurityScan){let C=_2(Z,$?.site);if(!C)W("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:c}=await i1({sourceDir:C?process.cwd():HX(),failOnSeverity:$?.securityFailOn||"critical",exclude:Z.infrastructure?.security?.scan?.exclude});t1(c),U=c}else N(`Security scan skipped (--skip-security-scan)
370
- `);let M=nJ(Z,Y,U);if(!M.allowed){K(`
371
- ✗ Deployment blocked by security policy: ${M.explanation}`),W("Remediate the findings or create an authorized, time-limited waiver in the Security posture center."),process.exitCode=1;return}if(B(`✓ Security policy allows this deployment
372
- `),j1(Z)==="serverless"){y("Serverless project detected: deploying the Lambda application");let{deployServerlessApp:C}=await import("./chunk-5mrtzemj.js");await C(Z,Y,{}),B("Serverless deployment complete");return}let H=$?.stack||i$(Z,Y),j=Z.project.region||"us-east-1",V=Z.infrastructure?.deployStack!==!1,T=Z.sites&&Object.keys(Z.sites).length>0,A=Z.infrastructure?.dns?.provider;if(T&&A){if(await RX(Z,$?.site,A,j,$?.skipDnsVerification,Y,J),!V){if(Z.infrastructure?.compute){let C=Object.values(Z.sites).map((c)=>c.domain).filter((c)=>!!c);if(C.length>0)y("Syncing CloudFront dynamic HTTP methods for app domains..."),await s1(C)}return}}if(!V){W("Infrastructure stack deployment disabled (infrastructure.deployStack: false)");return}let q=c0(Z);if(W(`Cloud provider: ${q}`),W(`Stack: ${H}`),W(`Region: ${j}`),W(`Environment: ${Y}`),(q==="hetzner"||Z.infrastructure?.compute?.mode==="server")&&iJ(Z)){y(`Provisioning ${q} compute infrastructure...`);let C=c$({config:Z,provider:q});if(!C.provisionComputeInfrastructure){K(`${q} driver does not support compute provisioning`),process.exitCode=1;return}let c=await C.provisionComputeInfrastructure({config:Z,environment:Y});if(B(`${q} compute infrastructure ready`),c.appPublicIp)W(`App server: ${c.appPublicIp}`);if(c.appInstanceId)W(`Server ID: ${c.appInstanceId}`);if(!await pJ(Z,Y,j,$?.site))K(`App deploy to ${q} compute reported a failure`),process.exitCode=1;else{if(B(`App deployed to ${q} compute`),$?.skipDnsVerification)W("DNS verification and record reconciliation skipped (--skip-dns-verification)");else await VX(Z,c.appPublicIp,A,c.appPublicIpv6),await AX(Z,A);let X$=await Z5({config:Z,environment:Y,driver:C,logger:{info:W,warn:N,error:K,step:y,success:B}});if(!X$)K("App deployed, but rpx TLS certificate reconciliation failed");if(!X$)process.exitCode=1;if(!$?.skipDnsVerification)await qX(Z,c.appPublicIp,c.appPublicIpv6)}return}y("Generating CloudFormation template...");let L=new C0({config:Z,environment:Y});L.generate();let D=L.toJSON(),P=JSON.parse(D);if(Object.keys(P.Resources||{}).length===0){W(`No infrastructure resources to deploy for environment '${Y}' — skipping stack ${H}.`);return}y("Validating template...");let _=s0(P),S=i0(D),I=t0(P),g=[..._.errors,...S.errors,...I.errors];if(g.length>0){K("Template validation failed:");for(let C of g)K(` - ${C.path}: ${C.message}`);return}let f=[..._.warnings,...S.warnings,...I.warnings];if(f.length>0)for(let C of f)N(` - ${C.path}: ${C.message}`);B("Template validated successfully");let d=Object.keys(P.Resources).length;W(`
373
- Resources to deploy: ${d}`);let r={};for(let C of Object.values(P.Resources)){let c=C.Type;r[c]=(r[c]||0)+1}for(let[C,c]of Object.entries(r).sort((z$,X$)=>X$[1]-z$[1]).slice(0,5))W(` - ${C}: ${c}`);if(!(J||await k(`
374
- Deploy now?`,!0))){W("Deployment cancelled");return}let l=new Y$(j);y("Checking stack status...");let s=!1;try{let C=await l.describeStacks({stackName:H});s=C.Stacks&&C.Stacks.length>0}catch(C){s=!1}if(s){W("Stack exists, updating...");let C=new E("Updating CloudFormation stack...");C.start();try{await l.updateStack({stackName:H,templateBody:D,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:Z.project.name},{Key:"Environment",Value:Y},{Key:"ManagedBy",Value:"ts-cloud"}]}),C.succeed("Update initiated"),y("Waiting for stack update to complete..."),await l.waitForStack(H,"stack-update-complete"),B("Stack updated successfully!")}catch(c){if(c.message.includes("No updates are to be performed"))C.succeed("No changes detected"),W("Stack is already up to date — continuing to app deploy");else throw c}}else{W("Creating new stack...");let C=new E("Creating CloudFormation stack...");C.start(),await l.createStack({stackName:H,templateBody:D,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:Z.project.name},{Key:"Environment",Value:Y},{Key:"ManagedBy",Value:"ts-cloud"}]}),C.succeed("Stack creation initiated"),y("Waiting for stack creation to complete..."),await l.waitForStack(H,"stack-create-complete"),B("Stack created successfully!")}let e=await l.getStackOutputs(H);if(n$(`Deployment Complete!
375
-
376
- Stack: ${H}
377
- Region: ${j}
378
- Environment: ${Y}
379
- Resources: ${d}
365
+ `}function Wi(r){r.command("env:list","List configured environments and their deployed stack status").action(async()=>{w("Environments");try{let e=await P(),t=Object.keys(e.environments??{});if(!t.length){a("No environments configured. Add environments.<env> to your cloud.config.");return}let n=[];for(let i of t){let o=e.environments[i],s=o?.region||e.project.region||"us-east-1",c=o?.app?`serverless (${o.app.kind})`:"server/static",l="not deployed";if(o?.app)try{let d=new ie(s),{Stacks:u}=await d.describeStacks({stackName:Lc(e,i)});l=u?.[0]?.StackStatus??"not deployed"}catch{l="not deployed"}n.push([i,o?.type??i,c,s,l])}x(["Environment","Type","Kind","Region","Stack status"],n)}catch(e){p(`Failed to list environments: ${e.message}`),process.exitCode=1}}),r.command("env:compare <env1> <env2>","Compare two environments' app configuration").action(async(e,t)=>{w(`Comparing ${e} ↔ ${t}`);try{let n=await P(),i=n.environments?.[e],o=n.environments?.[t];if(!i||!o){p(`Both environments must exist in config (have: ${Object.keys(n.environments??{}).join(", ")||"none"}).`),process.exitCode=1;return}let s=(m,f="")=>{let h={};for(let[S,y]of Object.entries(m??{})){let v=f?`${f}.${S}`:S;if(y&&typeof y==="object"&&!Array.isArray(y))Object.assign(h,s(y,v));else h[v]=Array.isArray(y)?JSON.stringify(y):String(y)}return h},c=s(i),l=s(o),u=[...new Set([...Object.keys(c),...Object.keys(l)])].sort().filter((m)=>c[m]!==l[m]).map((m)=>[m,c[m]??"—",l[m]??"—"]);if(!u.length){g("Identical — no configuration differences.");return}x(["Setting",e,t],u),a(`
366
+ ${u.length} difference(s)`)}catch(n){p(`Comparison failed: ${n.message}`),process.exitCode=1}}),r.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(e)=>{try{let t=await P(),n=e?.env||"production",{region:i,functions:o}=bt(t,n),s=e?.function??"http",d=(await new tt(i).getFunction(o[s])).Configuration?.Environment?.Variables??{},u=e?.file??`.env.${n}`;Nm(u,Lm(d)),g(`Wrote ${Object.keys(d).length} variable(s) from ${o[s]} → ${u}`)}catch(t){p(`env:pull failed: ${t.message}`),process.exitCode=1}}),r.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(e)=>{try{let t=await P(),n=e?.env||"production",{region:i,functions:o}=bt(t,n),s=e?.file??`.env.${n}`;if(!Dm(s)){p(`File not found: ${s}`),process.exitCode=1;return}let c=Om(Tm(s,"utf8")),l=(e?.function??"all").toLowerCase();if(l!=="all"&&!["http","queue","cli"].includes(l)){p("--function must be one of: http, queue, cli, all"),process.exitCode=1;return}let d=l==="all"?["http","queue","cli"]:[l],u=(t.environments?.[n]?.app?.provisionedConcurrency??0)>0,m=new tt(i);for(let f of d){let h=o[f],S=c;if(!e?.replace)S={...(await m.getFunction(h)).Configuration?.Environment?.Variables??{},...c};if(jc(h,S),await m.updateFunctionConfiguration({FunctionName:h,Environment:{Variables:S}}),await m.waitForFunctionActive(h,120),u){let y=await m.publishVersion({FunctionName:h});if(y.Version)await m.updateAlias({FunctionName:h,Name:"live",FunctionVersion:y.Version})}g(`Pushed ${Object.keys(c).length} var(s) → ${h}${e?.replace?" (replaced)":" (merged)"}${u?" (published + flipped live alias)":""}`)}a("Note: a subsequent `cloud deploy:serverless` re-injects infra/secret env from config.")}catch(t){p(`env:push failed: ${t.message}`),process.exitCode=1}}),r.command("env:create <name>","Create new environment").action(async(e)=>{Vi(`env:create ${e}`,"Add an `environments.<name>` block to your cloud.config and run `cloud deploy:serverless --env <name>`.")}),r.command("env:switch <name>","Switch active environment").action(async(e)=>{Vi(`env:switch ${e}`,'ts-cloud has no persistent "active" environment — pass --env <name> to each command.')});for(let e of["clone","promote","sync"])r.command(`env:${e} <source> <target>`,`(${e}) environment configuration`).action(async(t,n)=>{Vi(`env:${e} ${t} ${n}`,`Copy the \`environments.${t}\` block to \`environments.${n}\` in cloud.config, then \`cloud deploy:serverless --env ${n}\`.`)});r.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(e,t)=>{let n;try{let i=await P(),o=t.env||"production",s=t.site||Object.keys(i.sites??{})[0];if(!s)throw Error("No application site is configured; pass --site after adding one");n=q(process.cwd(),i);let c=n.environments.get(o),l=c?n.store.listResources(n.project.id,c.id).find((y)=>y.kind==="application"&&y.slug===s):void 0;if(!c||!l)throw Error(`Application ${s} was not found in base environment ${o}`);let d=new nr(n.store),u=d.previews.getDefinitionForResource(l.id);if(!u){let y=i.sites?.[s]?.domain,v=t.domain||(y?`https://{name}.preview.${y}`:void 0);if(!v)throw Error("Pass --domain with an HTTPS pattern containing {name} to configure previews");u=d.previews.createDefinition({projectId:n.project.id,resourceId:l.id,baseEnvironmentId:c.id,domainPattern:v,ttlHours:Number(t.ttl)||void 0})}let m=Number(t.pr)||void 0,f=m?d.previews.findForPullRequest(u.id,"local",m):d.previews.findForBranch(u.id,"local",e);if(t.getUrl){if(!f)throw Error("Preview was not found");a(f.url??"URL unavailable");return}if(t.extend){if(!f)throw Error("Preview was not found");let y=d.previews.extend(f.id,Number(t.extend));g(`Extended ${y.name} until ${y.expiresAt}`);return}if(t.destroy){if(!f)throw Error("Preview was not found");if(!t.yes&&!await O(`Destroy ${f.name} and only its tagged resources?`,!1))return;let y=d.enqueueDestroy(f,"manual");g(`Preview teardown queued: ${y.id}`);return}if(t.rebuild){if(!f)throw Error("Preview was not found");let y=d.enqueueDeploy(f,{reason:"manual_rebuild"});g(`Preview rebuild queued: ${y.id}`);return}if(!t.sha)throw Error("Pass --sha with the exact immutable commit to deploy");let h=d.previews.upsert({definitionId:u.id,repository:"local",branch:e,pullRequestNumber:m,commitSha:t.sha}),S=d.enqueueDeploy(h.preview,{created:h.created});g(`${h.created?"Preview creation":"Preview update"} queued: ${S.id}`),a(`${h.preview.url} · expires ${h.preview.expiresAt}`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n?.store.close()}}),r.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(e)=>{let t;try{let n=await P();t=q(process.cwd(),n);let o=new nr(t.store).cleanup({dryRun:e.dryRun,maxAgeHours:Number(e.maxAge)||void 0,keepCount:Number(e.keep)||void 0});x(["Preview","Status","Expires","Reason"],o.candidates.map((s)=>[s.preview.name,s.preview.status,s.preview.expiresAt,s.reasons.join(", ")])),g(e.dryRun?`${o.candidates.length} cleanup candidate(s)`:`${o.operations.length} teardown job(s) queued`)}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}finally{t?.store.close()}}),r.command("env:previews","List persistent preview environments").option("--site <name>","Filter application site").action(async(e)=>{let t;try{let n=await P();t=q(process.cwd(),n);let i=new nr(t.store),o=e.site?new Set(t.store.listResources(t.project.id).filter((c)=>c.slug===e.site).map((c)=>c.id)):void 0,s=i.previews.listInstances({projectId:t.project.id}).filter((c)=>!o||o.has(c.resourceId));x(["ID","Name","Status","Branch / PR","Commit","URL","Expires","Cost"],s.map((c)=>[c.id,c.name,c.status,c.pullRequestNumber?`PR #${c.pullRequestNumber}`:c.branch,c.commitSha.slice(0,12),c.url??"—",c.expiresAt,c.costEstimate==null?"—":`$${c.costEstimate.toFixed(2)}`]))}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}finally{t?.store.close()}})}import{readFileSync as rr}from"node:fs";async function Ee(){let r=await P(),e=q(process.cwd(),r);return{config:r,controlPlane:e,applications:new Gc(e.store)}}async function qi(r,e,t){let n=await Ee();try{let i=n.applications.get(r);if(!i)throw Error("Compose application was not found");let o=e??n.controlPlane.store.listEnvironments(i.projectId).find((c)=>c.id===i.environmentId)?.slug;if(!o)throw Error("Compose environment was not found");let s=await Kc(n.config,o,t(i));if(!s.ok)throw Error(s.stderr||s.error||"Remote Compose command failed");if(s.stdout)a(s.stdout)}finally{n.controlPlane.store.close()}}function Gi(r){r.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(e,t)=>{let n=await Ee();try{let i=n.controlPlane.environments.get(t.env??"production");if(!i)throw Error("Environment was not found");let o=n.applications.preview(rr(e,"utf8"),{name:t.name??"Compose application",projectId:n.controlPlane.project.id,environmentId:i.id});if(x(["Severity","Path","Code","Message"],o.diagnostics.map((s)=>[s.severity,s.path,s.code,s.message])),!o.valid)process.exitCode=1;else g(`${Object.keys(o.manifest.spec.services).length} services are valid`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.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(e,t)=>{let n=await Ee();try{let i=n.controlPlane.environments.get(t.env??"production");if(!i)throw Error("Environment was not found");let o=t.name??e.split("/").at(-2)??"Compose application",s=n.applications.import(rr(e,"utf8"),{name:o,projectId:n.controlPlane.project.id,environmentId:i.id});g(`Imported ${s.application.name} (${s.application.id})`),a(`${Object.keys(s.application.manifest.spec.services).length} services · ${s.parsed.diagnostics.length} diagnostic(s)`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.command("compose:templates","List pinned verified Compose templates").action(()=>x(["ID","Version","Category","Architecture","Verified"],_c().map((e)=>[e.id,e.version,e.category,e.architecture,e.lastVerifiedAt]))),r.command("compose:catalog <file>","Validate and inspect a local/custom template catalog").option("--env <environment>","Target environment",{default:"production"}).action(async(e,t)=>{let n=await Ee();try{let i=n.controlPlane.environments.get(t.env??"production");if(!i)throw Error("Environment was not found");let o=zc(rr(e,"utf8"),{projectId:n.controlPlane.project.id,environmentId:i.id});if(x(["ID","Version","Category","Services","Checksum"],o.templates.map((s)=>[s.template.id,s.template.version,s.template.category,String(Object.keys(s.parsed.manifest.spec.services).length),s.template.checksum.slice(0,12)])),o.diagnostics.length)x(["Severity","Path","Message"],o.diagnostics.map((s)=>[s.severity,s.path,s.message]));if(!o.valid)process.exitCode=1}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.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(e,t)=>{let n=await Ee();try{let i=n.controlPlane.environments.get(t.env??"production");if(!i)throw Error("Environment was not found");let o=n.applications.fromTemplate(e,{domain:t.domain??""},{name:t.name??e,projectId:n.controlPlane.project.id,environmentId:i.id,version:t.version});g(`Created ${o.application.name} from ${e}@${o.application.templateVersion}`),a(`Export with cloud compose:export ${o.application.id}`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.command("compose:list","List Compose applications").action(async()=>{let e=await Ee();try{x(["ID","Name","Status","Services","Source","Updated"],e.applications.list({projectId:e.controlPlane.project.id}).map((t)=>[t.id,t.name,t.status,String(Object.keys(t.manifest.spec.services).length),t.templateId?`${t.templateId}@${t.templateVersion}`:"compose",t.updatedAt]))}finally{e.controlPlane.store.close()}}),r.command("compose:export <application>","Print a normalized editable Compose file").action(async(e)=>{let t=await Ee();try{let n=t.applications.get(e);if(!n)throw Error("Compose application was not found");a(Mc(n.manifest))}finally{t.controlPlane.store.close()}}),r.command("compose:diff <application> <file>","Compare persisted and proposed Compose manifests").action(async(e,t)=>{let n=await Ee();try{let i=n.applications.get(e);if(!i)throw Error("Compose application was not found");let o=Fc(rr(t,"utf8"),i.manifest.metadata);x(["Path","Before","After"],Bc(i.manifest,o.manifest).map((s)=>[s.path,JSON.stringify(s.before),JSON.stringify(s.after)]))}finally{n.controlPlane.store.close()}});for(let e of["deploy","redeploy","start","stop"])r.command(`compose:${e} <application>`,`${e} a Compose application through the durable queue`).action(async(t)=>{let n=await Ee();try{let i=n.applications.get(t);if(!i)throw Error("Compose application was not found");let o=new ir(n.controlPlane.store).enqueue(i,e);g(`${e} queued: ${o.id}`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}});r.command("compose:scale <application> <service> <replicas>","Scale one capable Compose service").action(async(e,t,n)=>{let i=await Ee();try{let o=i.applications.get(e);if(!o)throw Error("Compose application was not found");let s=new ir(i.controlPlane.store).enqueue(o,"scale",{service:t,replicas:Number(n)});g(`Scale queued: ${s.id}`)}catch(o){p(o instanceof Error?o.message:String(o)),process.exitCode=1}finally{i.controlPlane.store.close()}}),r.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(e,t)=>{let n=await Ee();try{let i=n.applications.get(e);if(!i)throw Error("Compose application was not found");let o=new ir(n.controlPlane.store).enqueue(i,"delete",{removeVolumes:t.removeVolumes,confirmation:t.confirm});g(`Deletion queued: ${o.id}`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.command("compose:logs <application> <service>","Read ordered service logs").option("--lines <count>","Tail line count",{default:"200"}).option("--env <environment>","Target environment").action((e,t,n)=>qi(e,n.env,(i)=>Wc(i.manifest,t,Number(n.lines)||200))),r.command("compose:shell <application> <service> [command...]","Run a command in one service").option("--env <environment>","Target environment").action((e,t,n,i)=>qi(e,i.env,(o)=>qc(o.manifest,t,n?.length?n:["sh"]))),r.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((e,t)=>qi(e,t.env,(n)=>{if(t.action==="scale")return Vc(n.manifest,t.service??"",Number(t.replicas));return Uc(n.manifest,t.action??"deploy",{service:t.service,removeVolumes:t.removeVolumes})}))}import{readFileSync as jm}from"node:fs";async function De(){let r=await P(),e=q(process.cwd(),r);return{controlPlane:e,releases:new Jc(e.store)}}function Ki(r){if(!r)return{};return JSON.parse(jm(r,"utf8"))}function Hi(r){r.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(e,t)=>{let n=await De();try{let i=n.releases.releases.registerArtifact({organizationId:n.controlPlane.organization.id,digest:String(t.digest??""),kind:t.kind??"compute",uri:e,size:Number(t.size),mediaType:t.mediaType});g(`Artifact registered: ${i.id}`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.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(e,t,n)=>{let i=await De();try{let o=i.controlPlane.environments.get(n.env??"production"),s=o?i.controlPlane.store.listResources(i.controlPlane.project.id,o.id).find((l)=>l.slug===e):void 0;if(!o||!s)throw Error("Release resource was not found");let c=i.releases.releases.create({organizationId:i.controlPlane.organization.id,projectId:i.controlPlane.project.id,environmentId:o.id,resourceId:s.id,artifactId:t,kind:n.kind??"compute",sourceSha:n.sourceSha,config:Ki(n.config),manifest:Ki(n.manifest),strategy:n.strategy??"atomic",healthGate:n.healthPath?{protocol:"http",path:n.healthPath,timeoutSeconds:5,intervalSeconds:5,healthyThreshold:2,unhealthyThreshold:2}:void 0,approvalRequired:n.approval,trigger:n.trigger});g(`Release created: ${c.id} (${c.status})`)}catch(o){p(o instanceof Error?o.message:String(o)),process.exitCode=1}finally{i.controlPlane.store.close()}}),r.command("release:list","List unified release history").option("--env <environment>","Filter environment").action(async(e)=>{let t=await De();try{let n=e.env?t.controlPlane.environments.get(e.env):void 0;x(["ID","Resource","Status","Kind","Artifact","Strategy","Source","Created"],t.releases.releases.list({projectId:t.controlPlane.project.id,environmentId:n?.id}).map((i)=>[i.id,t.controlPlane.store.getResource(i.resourceId)?.slug??i.resourceId,i.status,i.kind,i.artifactId.slice(0,12),i.strategy,i.sourceSha?.slice(0,12)??"—",i.createdAt]))}finally{t.controlPlane.store.close()}}),r.command("release:show <release>","Show provenance, health, approvals, and transitions").action(async(e)=>{let t=await De();try{let n=t.releases.releases.get(e);if(!n)throw Error("Release was not found");a(JSON.stringify({release:n,artifact:t.releases.releases.getArtifact(n.artifactId),approvals:t.releases.releases.approvals(e),transitions:t.releases.releases.transitions(e)},null,2))}finally{t.controlPlane.store.close()}}),r.command("release:compare <left> <right>","Compare artifact, source, config, manifest, and data caveats").action(async(e,t)=>{let n=await De();try{let i=n.releases.releases.compare(e,t);a(JSON.stringify(i,null,2))}finally{n.controlPlane.store.close()}}),r.command("release:capabilities <kind>","Explain supported deployment strategies").option("--health","Readiness gate is configured").option("--replicas <count>","Replica count",{default:"1"}).action((e,t)=>x(["Strategy","Supported","Capacity","Cost","Explanation","Rollback"],Hc({kind:e,hasHealthGate:!!t.health,replicas:Number(t.replicas)}).map((n)=>[n.strategy,n.supported?"yes":"no",`${n.capacityMultiplier}x`,n.costImpact,n.explanation,n.rollback]))),r.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(e,t,n)=>{let i=await De();try{let o=i.controlPlane.environments.get(String(n.env??"")),s=o?i.controlPlane.store.listResources(i.controlPlane.project.id,o.id).find((l)=>l.slug===t):void 0;if(!o||!s)throw Error("Promotion target was not found");let c=i.releases.releases.promote(e,{targetEnvironmentId:o.id,targetResourceId:s.id,config:Ki(n.config),approvalRequired:n.approval});g(`Promoted without rebuild: ${c.id} (${c.status})`)}catch(o){p(o instanceof Error?o.message:String(o)),process.exitCode=1}finally{i.controlPlane.store.close()}}),r.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(e,t)=>{let n=await De();try{if(!t.actor)throw Error("--actor is required");let i=n.releases.releases.approve(e,{actorId:t.actor,decision:t.reject?"rejected":"approved",comment:t.comment});g(`Release is ${i.status}`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.command("release:activate <release>","Queue immutable artifact activation").action(async(e)=>{let t=await De();try{let n=t.releases.releases.get(e);if(!n)throw Error("Release was not found");let i=t.releases.enqueueActivation(n);g(`Activation queued: ${i.id}`)}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}finally{t.controlPlane.store.close()}}),r.command("release:rollback <release>","Restore the preserved prior immutable release").option("--to <release>","Explicit preserved release ID").action(async(e,t)=>{let n=await De();try{let i=n.releases.releases.get(e);if(!i)throw Error("Release was not found");let o=n.releases.enqueueRollback(i,{targetReleaseId:t.to});g(`Rollback queued: ${o.id}`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.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(e,t)=>{let n=await De();try{let i=n.releases.completeHealthGate(e,{healthy:!!t.healthy,operationId:t.operation,message:t.message});g(i.rollbackOperation?`Health failed; rollback queued: ${i.rollbackOperation.id}`:`Release is ${i.release.status}`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}finally{n.controlPlane.store.close()}}),r.command("release:pin <release>","Pin or unpin a release against retention").option("--remove","Remove pin").option("--reason <text>","Pin reason").action(async(e,t)=>{let n=await De();try{let i=n.releases.releases.pin(e,!t.remove,t.reason);g(i.pinned?"Release pinned.":"Release unpinned.")}finally{n.controlPlane.store.close()}})}import{existsSync as Fm}from"node:fs";function Ji(r){r.command("assets:build","Run the configured build hooks for an environment").option("--env <environment>","Environment (production, staging, development)",{default:"production"}).action(async(e)=>{w("Building Assets");try{let t=await P(),n=e?.env||"production",o=t.environments?.[n]?.app?.build??[];if(!o.length){p(`No build hooks defined for '${n}'. Set environments.${n}.app.build = ['bun run build', …] in cloud.config.`),process.exitCode=1;return}let{execSync:s}=await import("node:child_process");for(let c of o)M(`$ ${c}`),s(c,{stdio:"inherit"});g("Build hooks complete")}catch(t){p(`Build failed: ${t.message}`),process.exitCode=1}});for(let e of["assets:optimize:images","images:optimize"])r.command(e,"Optimize images (not implemented)").option("--dir <directory>","Directory to optimize").action(async()=>{p(`'${e}' is not implemented — ts-cloud has no built-in image optimizer.`),a("Optimize images in your own build step (e.g. sharp/squoosh), then `cloud assets:deploy`."),process.exitCode=1});r.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(e)=>{w("Deploying Assets to S3");try{let n=(await P()).project.region||"us-east-1",i=e?.source||"dist",o=e?.bucket,s=e?.prefix,c=e?.delete||!1,l=e?.cacheControl||"public, max-age=31536000";if(!o){p("--bucket is required");return}if(!Fm(i)){p(`Source directory not found: ${i}`);return}if(a(`Source: ${i}`),a(`Bucket: s3://${o}${s?`/${s}`:""}`),a(`Cache-Control: ${l}`),c)I("Delete mode enabled - files not in source will be removed");if(!await O(`
367
+ Deploy assets now?`,!0)){a("Deployment cancelled");return}let u=new de(n),m=new C("Uploading assets to S3...");m.start(),await u.sync({source:i,bucket:o,prefix:s,delete:c,cacheControl:l,acl:"public-read"}),m.succeed("Assets deployed successfully!");let h=(await u.getBucketSize(o,s)/1024/1024).toFixed(2);g(`
368
+ Deployment complete!`),a(`Total size: ${h} MB`),a(`
369
+ Assets URL: https://${o}.s3.${n}.amazonaws.com${s?`/${s}`:""}`)}catch(t){p(`Deployment failed: ${t.message}`)}}),r.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(e)=>{w("Invalidating CloudFront Cache");try{let t=e?.distribution;if(!t){p("--distribution is required");return}let i=(e?.paths||"/*").split(",").map((u)=>u.trim()),o=e?.wait||!1;if(a(`Distribution: ${t}`),a(`Paths: ${i.join(", ")}`),!await O(`
370
+ Invalidate cache now?`,!0)){a("Invalidation cancelled");return}let c=new ne,l=new C("Creating invalidation...");l.start();let d=await c.invalidatePaths(t,i);if(l.succeed("Invalidation created"),g(`
371
+ Invalidation ID: ${d.Id}`),a(`Status: ${d.Status}`),a(`Created: ${new Date(d.CreateTime).toLocaleString()}`),o){let u=new C("Waiting for invalidation to complete...");u.start(),await c.waitForInvalidation(t,d.Id),u.succeed("Invalidation completed!")}else a(`
372
+ Invalidation is in progress. Use --wait to wait for completion.`)}catch(t){p(`Invalidation failed: ${t.message}`)}})}function Yi(r){for(let[e,t]of[["team:add <email> <role>","Invite an organization member"],["team:list","List organization members"],["team:remove <email>","Revoke organization membership"]])r.command(e,t).action(async(...n)=>be(e.split(" ")[0],{target:String(n[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 qm,execSync as Io}from"node:child_process";import{copyFileSync as Cn,existsSync as ge,mkdirSync as gl,readFileSync as fo,writeFileSync as Gm}from"node:fs";import{tmpdir as hl}from"node:os";import{dirname as Km,join as yl,resolve as Hm}from"node:path";import{existsSync as Zi,readdirSync as Yc,statSync as Qi}from"node:fs";import{join as or}from"node:path";var Mm=new Set([".DS_Store","Thumbs.db",".gitkeep",".keep"]);function Zc(r){return Mm.has(r)}function Qc(r,e=0){if(e>8)return!0;let t;try{t=Yc(r)}catch{return!1}for(let n of t){if(Zc(n))continue;let i=or(r,n),o;try{o=Qi(i)}catch{continue}if(o.isFile())return!0;if(o.isDirectory()&&Qc(i,e+1))return!0}return!1}function Bm(r){let e;try{e=Yc(r)}catch{return[]}let t=[];for(let n of e){if(Zc(n))continue;let i=or(r,n);try{if(Qi(i).isDirectory()&&Zi(or(i,"index.html")))t.push(n)}catch{}}return t.sort((n,i)=>n.localeCompare(i))}function Xc(r){let{root:e,siteName:t,expectIndex:n}=r,i=[];if(!Zi(e))return i.push({level:"error",message:`Build output not found at ${e} for site '${t}'`}),i;if(!Qi(e).isDirectory())return i.push({level:"error",message:`Site '${t}' has root ${e}, which is a file, not a directory`}),i;if(!Qc(e))return i.push({level:"error",message:`Site '${t}' would ship an empty release: ${e} contains no files. Check that the build ran and wrote where 'root' points.`}),i;if(!n)return i;if(Zi(or(e,"index.html")))return i;let o=Bm(e);if(o.length===1){let s=`${e.replace(/\/+$/,"")}/${o[0]}`;return i.push({level:"error",message:`Site '${t}' has no index.html at the top of ${e}, but ${s} has one. The generator wrote one level down, so this release would serve 404s at the domain root. Set root: '${s}'.`}),i}if(o.length>1)return i.push({level:"warning",message:`Site '${t}' has no index.html at the top of ${e}. These subdirectories have one: ${o.join(", ")}. The domain root will 404.`}),i;return i.push({level:"warning",message:`Site '${t}' has no index.html anywhere under ${e}. The domain root will 404.`}),i}function _m(r){if(!r)return[];if(Array.isArray(r))return r.map(String);let e=r,t=e.Items??e.Item;if(!t)return[];if(Array.isArray(t))return t.map(String);if(typeof t==="object"&&t!==null){let n=t;if("Item"in n){let i=n.Item;if(!i)return[];return Array.isArray(i)?i.map(String):[String(i)]}if("CNAME"in n){let i=n.CNAME;if(!i)return[];return Array.isArray(i)?i.map(String):[String(i)]}return Object.values(n).flatMap((i)=>{if(Array.isArray(i))return i.map(String);return typeof i==="string"?[i]:[]})}return[String(t)]}async function sr(r){if(r.length===0)return;let e=new ne,t=await e.listDistributions(),n=[...new Set(r.map((i)=>i.toLowerCase()))];for(let i of n){let o=t.find((c)=>_m(c.Aliases).some((d)=>d.toLowerCase()===i));if(!o?.Id){I(`No CloudFront distribution found for ${i} — skipping dynamic HTTP method sync`);continue}if(await e.ensureDynamicHttpMethods(o.Id))g(`CloudFront ${o.Id} (${i}): enabled POST/PUT/PATCH/DELETE on dynamic paths`);else a(`CloudFront ${o.Id} (${i}): already allows dynamic HTTP methods`)}}import{execSync as zm}from"node:child_process";async function Um(r,e,t,n={},i=process.cwd()){if(!r)return;if(n.step?.(`Running ${t} hook`),typeof r==="string"){zm(r,{stdio:"inherit",cwd:i});return}await r(e)}async function Sn(r,e,t={},n){try{return await Um(r.hooks?.[e],r,e,t,n),!0}catch(i){return t.error?.(`${e} hook failed: ${i?.message||i}`),!1}}var el=["js","mjs","cjs","css","map","woff","woff2","ttf","otf","eot","png","jpg","jpeg","gif","webp","avif","svg","ico","mp4","webm","ogg","mp3","wav","pdf","wasm"];function Xi(r){return`"${r.replace(/\\/g,"\\\\").replace(/"/g,"\\\"")}"`}function tl(r){return`{${r.map(Xi).join(" ")}}`}function eo(r){if(r.length===1)return`http.host eq ${Xi(r[0])}`;return`http.host in ${tl(r)}`}function to(r,e={}){if(e.enabled===!1||r.length===0)return[];let t=eo(r),n=[],i=e.bypassPaths??[],o=i.length>0?`(${i.map((d)=>`starts_with(http.request.uri.path, ${Xi(d)})`).join(" or ")})`:void 0;if(o)n.push({action:"set_cache_settings",description:"bypass cache",expression:`(${t} and ${o})`,enabled:!0,action_parameters:{cache:!1}});let s=o?`${t} and not ${o}`:t,c=e.assetExtensions??el,l=c.length>0?`lower(http.request.uri.path.extension) in ${tl([...c])}`:void 0;if(l)n.push({action:"set_cache_settings",description:"cache fingerprinted assets",expression:`(${s} and ${l})`,enabled:!0,action_parameters:{cache:!0,edge_ttl:{mode:"override_origin",default:e.assetEdgeTtl??2592000},browser_ttl:{mode:"override_origin",default:e.assetBrowserTtl??31536000}}});return n.push({action:"set_cache_settings",description:"cache documents",expression:l?`(${s} and not ${l})`:`(${s})`,enabled:!0,action_parameters:{cache:!0,edge_ttl:{mode:"override_origin",default:e.documentEdgeTtl??3600},browser_ttl:{mode:"override_origin",default:e.documentBrowserTtl??0}}}),n}function no(r,e,t){if(r.length===0||!e||!t)return[];return[{action:"rewrite",description:"origin guard header",expression:`(${eo(r)})`,enabled:!0,action_parameters:{headers:{[e]:{operation:"set",value:t}}}}]}var ot=(r)=>r===void 0?void 0:r?"on":"off";function ro(r){let e={ssl:r.ssl,always_use_https:ot(r.alwaysUseHttps),automatic_https_rewrites:ot(r.automaticHttpsRewrites),min_tls_version:r.minTlsVersion,tls_1_3:ot(r.tls13),brotli:ot(r.brotli),http3:ot(r.http3),"0rtt":ot(r.zeroRtt),early_hints:ot(r.earlyHints),browser_cache_ttl:r.browserCacheTtl,always_online:ot(r.alwaysOnline),websockets:ot(r.websockets)};if(r.hsts)e.security_header={strict_transport_security:{enabled:r.hsts.enabled,max_age:r.hsts.maxAge??31536000,include_subdomains:r.hsts.includeSubdomains??!1,preload:r.hsts.preload??!1,nosniff:r.hsts.noSniff??!0}};for(let t of Object.keys(e))if(e[t]===void 0)delete e[t];return{...e,...r.raw??{}}}var $n={ssl:"strict",alwaysUseHttps:!0,automaticHttpsRewrites:!0,minTlsVersion:"1.2",tls13:!0,brotli:!0,http3:!0,earlyHints:!0,alwaysOnline:!0,browserCacheTtl:0};import{connect as Vm}from"node:tls";async function io(r){let{address:e,serverName:t,port:n=443,timeoutMs:i=5000}=r;return new Promise((o)=>{let s=!1,c=(d)=>{if(s)return;s=!0;try{l.destroy()}catch{}o(d)},l=Vm({host:e,port:n,servername:t,rejectUnauthorized:!1,timeout:i});l.once("secureConnect",()=>{if(l.authorized){c({reachable:!0,trusted:!0});return}c({reachable:!0,trusted:!1,reason:l.authorizationError?String(l.authorizationError):"origin certificate is not trusted for this hostname"})}),l.once("timeout",()=>{c({reachable:!1,trusted:!1,reason:`no TLS handshake within ${i}ms`})}),l.once("error",(d)=>{c({reachable:!1,trusted:!1,reason:d.message})})})}async function so(r){let{provider:e,zone:t,hosts:n,ipv4:i,ipv6:o,proxied:s=!0,settings:c=$n,cache:l,originGuard:d,purge:u=!0,ttl:m=300,skipOriginProbe:f=!1}=r,h={records:[],settingsChanged:[],cacheRules:0,originGuard:!1,purged:!1,deferredProxy:[],warnings:[]};if(n.length===0)return h;let S=[];for(let v of n){let R=s;if(s&&!f){let A=await io({address:i,serverName:v});if(!A.trusted){R=!1;let k=A.reason||"origin is not serving a trusted certificate";h.deferredProxy.push({host:v,reason:k}),h.warnings.push(`${v}: publishing DNS-only for now — the origin does not yet serve a certificate this proxy can verify (${k}). 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(R)S.push(v);let b=[{type:"A",content:i}];if(o&&oo(v))b.push({type:"AAAA",content:o});for(let{type:A,content:k}of b){let T=await e.upsertRecord(t,{name:v,type:A,content:k,ttl:m,proxied:R});if(T.success)h.records.push({host:v,type:A,content:k,proxied:R});else h.warnings.push(`${v} ${A} → ${k} failed: ${T.message||"unknown provider error"}`)}}if(S.length===0){if(s)h.warnings.push("No host is proxied yet, so zone settings, cache rules and purge were skipped — they only take effect through the proxy.");return h}try{let v=ro(c),R=await e.applyZoneSettings(t,v);h.settingsChanged=R.changed;for(let b of R.failed)h.warnings.push(`zone setting '${b.id}' could not be applied: ${b.error}`)}catch(v){h.warnings.push(`zone settings could not be read: ${v instanceof Error?v.message:String(v)}`)}let y=to(S,l);if(y.length>0){let v=await e.putManagedPhaseRules(t,"http_request_cache_settings",y);if(v.success)h.cacheRules=y.length;else h.warnings.push(`cache rules could not be written: ${v.message}`)}if(d){let v=no(S,d.header,d.value);if(v.length>0){let R=await e.putManagedPhaseRules(t,"http_request_late_transform",v);if(R.success)h.originGuard=!0;else h.warnings.push(`origin guard header could not be written: ${R.message}. `+"The gateway will reject requests that lack it — disable the guard or grant the token Transform Rules: Edit.")}}if(u){if(h.purged=await e.purgeCache(t,{hosts:S}),!h.purged)h.warnings.push("edge cache purge failed — new content may take until TTL expiry to appear.")}return h}var Wm=(r)=>process.env[r];function nl(r,e){let t=r.replace(/\.$/,"").toLowerCase();if(e){let n=e.replace(/\.$/,"").toLowerCase();if(t===n||t.endsWith(`.${n}`))return n}return t.split(".").slice(-2).join(".")}function co(r,e={}){let t=e.env??Wm,n=r.infrastructure?.compute?.proxy?.cdn,i=[];if(!n||n.provider!=="cloudflare")return{plan:null,errors:i};if(n.originDomain)i.push(`compute.proxy.cdn.originDomain (${n.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 o=t("CLOUDFLARE_API_TOKEN");if(!o)return i.push("CLOUDFLARE_API_TOKEN is not set — the Cloudflare CDN cannot be reconciled."),{plan:null,errors:i};let s=r.sites??{},c=n.frontedHosts?.length?[...new Set(n.frontedHosts.map((f)=>f.toLowerCase()))]:ao(s,{autoWww:r.infrastructure?.compute?.proxy?.autoWww!==!1});if(c.length===0)return i.push("No hostnames to front — set compute.proxy.cdn.frontedHosts, or give a site a domain."),{plan:null,errors:i};let l=r.infrastructure?.dns?.domain,d=new Set(c.map((f)=>nl(f,l)));if(d.size>1)return i.push(`Fronted hosts span more than one zone (${[...d].join(", ")}). A Cloudflare zone is configured as a unit, so run one CDN config per zone.`),{plan:null,errors:i};let u=n.cloudflare??{},m=n.secret?{header:n.secretHeader??"X-Origin-Verify",value:n.secret}:void 0;return{plan:{zone:[...d][0],hosts:c,apiToken:o,zoneId:u.zoneId??t("CLOUDFLARE_ZONE_ID"),accountId:u.accountId??t("CLOUDFLARE_ACCOUNT_ID"),proxied:u.proxied!==!1,settings:u.settings??$n,cache:u.cache,originGuard:m,purge:u.purgeOnDeploy!==!1,skipOriginProbe:u.skipOriginProbe===!0},errors:i}}async function Jm(r){let e=rl();if(e.emptyEnvKey||e.emptyEnvSecret)I("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.");a(`AWS credentials: ${e.description}`);try{let n=await new Dt(r).getCallerIdentity();if(n.Arn)a(`AWS identity: ${n.Arn}`)}catch(t){I(`Could not verify AWS identity: ${t.message}`)}}function Ym(){try{return Io("git rev-parse --short HEAD",{stdio:["ignore","pipe","ignore"]}).toString().trim()}catch{return Date.now().toString(36)}}async function lo(r,e,t,n){let{ensureManagementDashboard:i}=await import("./chunk-wewcjam5.js");i(r,{logger:{info:a,warn:I}});let o=r.sites||{},s=Object.entries(o);if(s.length===0)return I("infrastructure.compute is set but no sites are configured — nothing to deploy. Add sites to `cloud.config.ts`."),!0;if(n&&!o[n])return p(`Site '${n}' was not found. Configured sites: ${s.map(([R])=>R).join(", ")}`),!1;let c=s.filter(([R,b])=>{if(!b)return!1;if(n&&R!==n)return!1;if(kn(b)==="bucket")return I(`Site '${R}' targets a bucket — skipping compute deploy (handled by the static-site path).`),!1;return bo(b)});if(n)a(`Deploying only '${n}'. Other sites keep their current release; proxy routes are still regenerated in full.`);let l=r.project.slug,u=(r.infrastructure?.compute||{}).runtime||"bun",m=Ke({config:r}),f=Ym(),h=new Map,S=new Map,y={step:(R)=>M(R),error:(R)=>p(R)};if(!await Sn(r,"beforeDeploy",y))return!1;if(!await Sn(r,"beforeBuild",y))return!1;for(let[R,b]of c){let A=kn(b);if(w(`Preparing site: ${R}`),a(`Domain: ${b.domain||"(none)"}`),a(`Source: ${b.root}`),a(`Kind: ${A==="server-static"?"static (shipped to /var/www)":"app (systemd service)"}`),A==="server-app")a(`Start: ${b.start}`),a(`Port: ${b.port??"(unset)"}`);if(b.build){M(`Running build: ${b.build}`);try{Io(b.build,{stdio:"inherit",cwd:process.cwd()})}catch(N){return p(`Build failed for site '${R}': ${N.message}`),!1}}let k=Xc({root:b.root,siteName:R,expectIndex:A==="server-static"});for(let N of k)if(N.level==="error")p(N.message);else I(N.message);if(k.some((N)=>N.level==="error"))return!1;let T=b.build?void 0:JSON.stringify([Hm(b.root),[...b.exclude||[]].sort()]),D=T?S.get(T):void 0;if(D){M(`Reusing package for ${b.root} → ${D}`),h.set(R,D);continue}let E=yl(hl(),`${l}-${R}-${f}.tar.gz`);M(`Packaging ${b.root} → ${E}`);try{let N=(b.exclude||[]).map((_)=>`--exclude=${_}`);qm("tar",["czf",E,"-C",b.root,...N,"."],{stdio:"inherit",env:{...process.env,COPYFILE_DISABLE:"1"}})}catch(N){return p(`Failed to package '${R}': ${N.message}`),!1}if(h.set(R,E),T)S.set(T,E)}if(!await Sn(r,"afterBuild",y))return!1;let v=await wo({config:n?{...r,sites:Object.fromEntries(c)}:r,rpxConfig:n?r:void 0,environment:e,driver:m,sha:f,runtime:u,tarballForSite:(R)=>{let b=h.get(R);if(!b)throw Error(`Missing tarball for site '${R}'`);return b},logger:{info:(R)=>a(R),warn:(R)=>I(R),error:(R)=>p(R),step:(R)=>M(R),success:(R)=>g(R)}});if(v&&!await Sn(r,"afterDeploy",y))return!1;return v}async function ar(r){let e=new ul;if(M("Running pre-deployment security scan..."),r.exclude?.length)a(`Excluding from the scan: ${r.exclude.join(", ")}`);let t=await e.scan({directory:r.sourceDir,failOnSeverity:r.failOnSeverity||"critical",skipPatterns:r.skipPatterns,exclude:r.exclude});return{passed:t.passed,result:t}}function Zm(){let r=yl(hl(),`ts-cloud-empty-scan-${process.pid}`);return gl(r,{recursive:!0}),r}function cr(r){let{summary:e,findings:t,scannedFiles:n,duration:i}=r;if(a(`
373
+ Scanned ${n} files in ${i}ms`),e.critical>0)p(` Critical: ${e.critical}`);else a(` Critical: ${e.critical}`);if(e.high>0)I(` High: ${e.high}`);else a(` High: ${e.high}`);if(a(` Medium: ${e.medium}`),a(` Low: ${e.low}`),t.length>0){a(`
374
+ Findings:`);let o=t.filter((u)=>u.pattern.severity==="critical"),s=t.filter((u)=>u.pattern.severity==="high"),c=t.filter((u)=>u.pattern.severity==="medium"),l=t.filter((u)=>u.pattern.severity==="low"),d=(u,m,f)=>{if(u.length>0){console.log(`
375
+ ${il(`[${m}]`,f)}`);for(let h of u.slice(0,10))a(` ${h.pattern.name}`),a(` File: ${h.file}:${h.line}`),a(` Match: ${h.match}`);if(u.length>10)a(` ... and ${u.length-10} more ${m.toLowerCase()} findings`)}};d(o,"CRITICAL","red"),d(s,"HIGH","yellow"),d(c,"MEDIUM","blue"),d(l,"LOW","gray")}}function vl(r,e){let t=Object.keys(r.environments??{});if(e&&t.includes(e))return e;if(t.includes("production"))return"production";if(t[0])return t[0];throw Error("A configured environment is required for security policy evaluation")}function uo(r,e,t){let n=vl(r,e),i=q(process.cwd(),r);try{let o=new Co(i.store);ko(i);let s=Ao(i,n);if(t)ml(o,s,t);else pl(o,s);let c=Ro(o,{scope:s,desiredConfigHash:i.project.desiredConfigHash});return a(`Security policy: ${c.decision.policyId} v${c.decision.policyVersion} — ${c.decision.outcome.toUpperCase()}`),a(`Security decision: ${c.decision.explanation}`),{allowed:c.decision.outcome!=="block",explanation:c.decision.explanation}}finally{i.store.close()}}async function Qm(r,e){let t=vl(r),n=q(process.cwd(),r);try{let i=new Co(n.store);ko(n);let o=Ao(n,t),s=e.imageSha256?`${e.imageRef}@sha256:${e.imageSha256.replace(/^sha256:/,"")}`:e.imageRef;M("Scanning the built image and producing release security artifacts...");let c=await fl(i,{scope:o,releaseId:s,imageRef:e.imageRef,imageSha256:e.imageSha256,artifactRoot:e.artifactRoot,invocationId:e.invocationId,startedAt:e.startedAt,externalParameters:{imageRef:e.imageRef,artifactRoot:e.artifactRoot}});a(`Image scanner: ${c.scan.run.status} (${c.scan.findings.length} findings)`),a(`Release SBOM: ${c.sbomSource==="image"?"full image inventory":c.sbomSource==="manifest"?"package-manifest fallback":"unavailable"}`);let l=Ro(i,{scope:o,operationId:e.invocationId,desiredConfigHash:n.project.desiredConfigHash});return a(`Release policy: ${l.decision.policyId} v${l.decision.policyVersion} — ${l.decision.outcome.toUpperCase()}`),a(`Release decision: ${l.decision.explanation}`),{allowed:l.decision.outcome!=="block",explanation:l.decision.explanation}}finally{n.store.close()}}function Xm(r){try{let e=q(process.cwd(),r),t=new vn(e.store).current();if(!t.attackMode&&!t.mitigationPause&&t.ipRules.allow.length===0&&t.ipRules.block.length===0)return r;let n=dl(r,t);return a(`Applying protection controls: ${t.ipRules.allow.length} allow, ${t.ipRules.block.length} block${t.attackMode?", attack mode ON":""}${t.mitigationPause?", mitigation PAUSED":""}`),n}catch{return r}}function Po(r){r.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(e)=>{w("Pre-Deployment Security Scan");try{let t=e?.source||".",n=e?.failOn||"critical",i=e?.skipPatterns?.split(",").map((c)=>c.trim())||[];if(!ge(t)){p(`Source directory not found: ${t}`);return}a(`Source: ${t}`),a(`Fail on: ${n} or higher severity`);let{passed:o,result:s}=await ar({sourceDir:t,failOnSeverity:n,skipPatterns:i});if(cr(s),o)g(`
376
+ ✓ Security scan passed - no blocking issues found`);else p(`
377
+ ✗ Security scan failed - blocking issues detected`),a(`
378
+ Recommendations:`),a(" 1. Remove any hardcoded credentials from your code"),a(" 2. Use environment variables or AWS Secrets Manager"),a(" 3. Add sensitive files to .gitignore"),a(" 4. Use --skip-patterns to ignore false positives"),process.exit(1)}catch(t){p(`Security scan failed: ${t.message}`),process.exit(1)}}),r.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(e)=>{w("Deploying Infrastructure");let t=e?.yes===!0||process.env.CI==="true",n=e?.env||"staging",i=null;i=await ep(n);try{let o=await P(),s=Xm(o),{errors:c,warnings:l}=vo(s);for(let j of l)I(j);if(c.length>0){p(`
379
+ ✗ Deployment configuration is invalid:`);for(let j of c)p(` • ${j}`);process.exitCode=1;return}if(e?.dryRun){if(e.site&&!s.sites?.[e.site]){p(`Site '${e.site}' was not found. Configured sites: ${Object.keys(s.sites||{}).join(", ")||"none"}`),process.exitCode=1;return}let j=An(s),W=e.stack||Ve(s,n),ue=Yt(s),ae=s.project.region||"us-east-1",Fe=e.site?[e.site]:Object.keys(s.sites||{});w("Deployment Plan"),a(`Cloud provider: ${ue}`),a(`Mode: ${j}`),a(`Stack: ${W}`),a(`Region: ${ae}`),a(`Environment: ${n}`),a(`Sites: ${Fe.length>0?Fe.join(", "):"none"}`),g("Dry run complete. No infrastructure, DNS, certificates, or application files were changed.");return}if(Yt(s)==="aws"){let j=s.project?.region||"us-east-1";await Jm(j)}let d;if(!e?.skipSecurityScan){let j=ll(s,e?.site);if(!j)a("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:W}=await ar({sourceDir:j?process.cwd():Zm(),failOnSeverity:e?.securityFailOn||"critical",exclude:s.infrastructure?.security?.scan?.exclude});cr(W),d=W}else I(`Security scan skipped (--skip-security-scan)
380
+ `);let u=uo(s,n,d);if(!u.allowed){p(`
381
+ ✗ Deployment blocked by security policy: ${u.explanation}`),a("Remediate the findings or create an authorized, time-limited waiver in the Security posture center."),process.exitCode=1;return}if(g(`✓ Security policy allows this deployment
382
+ `),An(s)==="serverless"){M("Serverless project detected: deploying the Lambda application");let{deployServerlessApp:j}=await import("./chunk-jnx4jgw8.js");await j(s,n,{}),g("Serverless deployment complete");return}let m=e?.stack||Ve(s,n),f=s.project.region||"us-east-1",h=s.infrastructure?.deployStack!==!1,S=s.sites&&Object.keys(s.sites).length>0,y=s.infrastructure?.dns?.provider;if(S&&y){if(await ip(s,e?.site,y,f,e?.skipDnsVerification,n,t),!h){if(s.infrastructure?.compute){let j=Object.values(s.sites).map((W)=>W.domain).filter((W)=>!!W);if(j.length>0)M("Syncing CloudFront dynamic HTTP methods for app domains..."),await sr(j)}return}}if(!h){a("Infrastructure stack deployment disabled (infrastructure.deployStack: false)");return}let v=Yt(s);if(a(`Cloud provider: ${v}`),a(`Stack: ${m}`),a(`Region: ${f}`),a(`Environment: ${n}`),(v==="hetzner"||s.infrastructure?.compute?.mode==="server")&&po(s)){M(`Provisioning ${v} compute infrastructure...`);let j=Ke({config:s,provider:v});if(!j.provisionComputeInfrastructure){p(`${v} driver does not support compute provisioning`),process.exitCode=1;return}let W=await j.provisionComputeInfrastructure({config:s,environment:n});if(g(`${v} compute infrastructure ready`),W.appPublicIp)a(`App server: ${W.appPublicIp}`);if(W.appInstanceId)a(`Server ID: ${W.appInstanceId}`);if(!await lo(s,n,f,e?.site))p(`App deploy to ${v} compute reported a failure`),process.exitCode=1;else{if(g(`App deployed to ${v} compute`),e?.skipDnsVerification)a("DNS verification and record reconciliation skipped (--skip-dns-verification)");else await tp(s,W.appPublicIp,y,W.appPublicIpv6),await np(s,y);let ae=await So({config:s,environment:n,driver:j,logger:{info:a,warn:I,error:p,step:M,success:g}});if(!ae)p("App deployed, but rpx TLS certificate reconciliation failed");if(!ae)process.exitCode=1;if(!e?.skipDnsVerification)await rp(s,W.appPublicIp,W.appPublicIpv6)}return}M("Generating CloudFormation template...");let b=new Ut({config:s,environment:n});b.generate();let A=b.toJSON(),k=JSON.parse(A);if(Object.keys(k.Resources||{}).length===0){a(`No infrastructure resources to deploy for environment '${n}' — skipping stack ${m}.`);return}M("Validating template...");let D=on(k),E=sn(A),N=an(k),_=[...D.errors,...E.errors,...N.errors];if(_.length>0){p("Template validation failed:");for(let j of _)p(` - ${j.path}: ${j.message}`);return}let B=[...D.warnings,...E.warnings,...N.warnings];if(B.length>0)for(let j of B)I(` - ${j.path}: ${j.message}`);g("Template validated successfully");let V=Object.keys(k.Resources).length;a(`
383
+ Resources to deploy: ${V}`);let K={};for(let j of Object.values(k.Resources)){let W=j.Type;K[W]=(K[W]||0)+1}for(let[j,W]of Object.entries(K).sort((ue,ae)=>ae[1]-ue[1]).slice(0,5))a(` - ${j}: ${W}`);if(!(t||await O(`
384
+ Deploy now?`,!0))){a("Deployment cancelled");return}let G=new ie(f);M("Checking stack status...");let L=!1;try{let j=await G.describeStacks({stackName:m});L=j.Stacks&&j.Stacks.length>0}catch(j){L=!1}if(L){a("Stack exists, updating...");let j=new C("Updating CloudFormation stack...");j.start();try{await G.updateStack({stackName:m,templateBody:A,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:s.project.name},{Key:"Environment",Value:n},{Key:"ManagedBy",Value:"ts-cloud"}]}),j.succeed("Update initiated"),M("Waiting for stack update to complete..."),await G.waitForStack(m,"stack-update-complete"),g("Stack updated successfully!")}catch(W){if(W.message.includes("No updates are to be performed"))j.succeed("No changes detected"),a("Stack is already up to date — continuing to app deploy");else throw W}}else{a("Creating new stack...");let j=new C("Creating CloudFormation stack...");j.start(),await G.createStack({stackName:m,templateBody:A,capabilities:["CAPABILITY_IAM","CAPABILITY_NAMED_IAM"],tags:[{Key:"Project",Value:s.project.name},{Key:"Environment",Value:n},{Key:"ManagedBy",Value:"ts-cloud"}]}),j.succeed("Stack creation initiated"),M("Waiting for stack creation to complete..."),await G.waitForStack(m,"stack-create-complete"),g("Stack created successfully!")}let H=await G.getStackOutputs(m);if(Qe(`Deployment Complete!
385
+
386
+ Stack: ${m}
387
+ Region: ${f}
388
+ Environment: ${n}
389
+ Resources: ${V}
380
390
 
381
391
  View in console:
382
- https://console.aws.amazon.com/cloudformation/home?region=${j}#/stacks/stackinfo?stackId=${encodeURIComponent(H)}`,"green"),Object.keys(e).length>0){W(`
383
- Stack Outputs:`);for(let[C,c]of Object.entries(e))W(` - ${C}: ${c}`)}if(Z.infrastructure?.storage)for(let[C,c]of Object.entries(Z.infrastructure.storage)){if(!c.website||!c.root)continue;let z$=e[`${C}BucketName`]||e.FrontendBucketName,X$=e[`${C}CloudFrontDistributionId`];if(!z$){N(`Could not find bucket name for storage '${C}' — skipping file upload`);continue}if(!H$(c.root)){N(`Source directory not found: ${c.root} — skipping file upload for '${C}'`);continue}y(`Uploading files from ${c.root} to s3://${z$}...`);let{uploadStaticFiles:h$,invalidateCache:k0}=await import("./chunk-ngcyftm2.js"),j$=await h$({sourceDir:c.root,bucket:z$,region:j,onProgress:(a,i,O$)=>{if(a%10===0||a===i)W(` ${a}/${i}: ${O$}`)}});if(j$.errors.length>0)N(`Upload completed with errors: ${j$.errors.join(", ")}`);else{let a=j$.skipped>0?`Uploaded ${j$.uploaded} files (${j$.skipped} unchanged)`:`Uploaded ${j$.uploaded} files`;B(a)}if(X$&&j$.uploaded>0){y("Invalidating CloudFront cache...");let{invalidationId:a}=await k0(X$);B(`Cache invalidation created: ${a}`)}}if(Z.infrastructure?.compute)await pJ(Z,Y,j,$?.site);if(Z.infrastructure?.compute&&Z.sites){let C=Object.values(Z.sites).map((c)=>c.domain).filter((c)=>!!c);if(C.length>0)y("Syncing CloudFront dynamic HTTP methods for app domains..."),await s1(C)}}catch(Q){if(K(`Deployment failed: ${Q.message}`),process.exitCode=1,Q.stack)W(`
384
- Stack trace:`),console.error(Q.stack)}finally{if(X)await X()}}),G.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($)=>{R("Deploying Server Infrastructure");try{let J=await x(),Y=$?.env||"production",X=J.project.region||"us-east-1",Q=sJ(J);if(Q){K(Q),process.exitCode=1;return}if(!iJ(J)){K("No `infrastructure.compute` configured — nothing to deploy as a server."),W("Add an EC2 compute block to cloud.config, or use `cloud deploy:serverless` for Lambda apps."),process.exitCode=1;return}if(W(`Region: ${X}`),W(`Environment: ${Y}`),!await pJ(J,Y,X,$?.site)){process.exitCode=1;return}if(J.sites){let z=Object.values(J.sites).map((F)=>F.domain).filter((F)=>!!F);if(z.length>0)y("Syncing CloudFront dynamic HTTP methods for app domains..."),await s1(z)}B(`
385
- Server deployment complete!`)}catch(J){K(`Deployment failed: ${J.message}`),process.exitCode=1}}),G.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($)=>{try{let J=await x(),Y=$?.env||"production",X=sJ(J);if(X){K(X),process.exitCode=1;return}let{deployServerlessApp:Q,redeployServerlessApp:Z}=await import("./chunk-5mrtzemj.js");if($?.redeploy){await Z(J,Y);return}await Q(J,Y,{skipBuild:$?.skipBuild,skipDeployHooks:$?.skipHooks})}catch(J){K(`Deployment failed: ${J.message}`),process.exitCode=1}}),G.command("serverless:rollback","Roll back a serverless app to the previous build").option("--env <environment>","Environment (production, staging, development)").action(async($)=>{try{let J=await x(),Y=$?.env||"production",{rollbackServerlessApp:X}=await import("./chunk-5mrtzemj.js");await X(J,Y)}catch(J){K(`Rollback failed: ${J.message}`),process.exitCode=1}}),G.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($,J)=>{R("Rolling Back Release");try{let Y=await x(),X=J?.env||"production",Q=$||Object.keys(Y.sites||{})[0];if(!Q){K("No site configured to roll back."),process.exitCode=1;return}let{rollbackComputeSite:Z}=await import("./chunk-m7dgskt1.js"),z=await Z({driver:c$({config:Y}),slug:Y.project.slug,environment:X,logger:v},{siteName:Q,to:J?.to});if(!z.success){K(`Rollback failed: ${z.error||"unknown error"}`),process.exitCode=1;return}for(let F of z.perInstance||[])W(` ${F.instanceId}: ${F.output?.trim()||F.status}`)}catch(Y){K(`Rollback failed: ${Y.message}`),process.exitCode=1}}),G.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($,J)=>{R("Deployment History");try{let Y=await x(),X=J?.env||"production",Q=$||Object.keys(Y.sites||{})[0];if(!Q){K("No site configured."),process.exitCode=1;return}let{getComputeDeployHistory:Z}=await import("./chunk-m7dgskt1.js"),z=await Z({driver:c$({config:Y}),slug:Y.project.slug,environment:X,logger:v},{siteName:Q,limit:Number.parseInt(J?.limit||"20",10)||20});if(!z.success){K(`Could not read history: ${z.error||"unknown error"}`),process.exitCode=1;return}for(let F of z.perInstance||[])W(`
386
- ${F.instanceId}:`),W(F.output?.trimEnd()||"(no output)")}catch(Y){K(`History lookup failed: ${Y.message}`),process.exitCode=1}}),G.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($,J,Y)=>{R(`Running Recipe: ${$}`);try{if(!H$(J)){K(`Recipe script not found: ${J}`),process.exitCode=1;return}let X=await x(),Q=Y?.env||"production",Z=tJ(J,"utf8").split(`
387
- `),{runComputeRecipe:z}=await import("./chunk-m7dgskt1.js"),F=await z({driver:c$({config:X}),slug:X.project.slug,environment:Q,logger:v},{name:$,script:Z,user:Y?.user});for(let U of F.perInstance||[])W(`
388
- ${U.instanceId}:`),W(U.output?.trimEnd()||"(no output)");if(!F.success)process.exitCode=1}catch(X){K(`Recipe failed: ${X.message}`),process.exitCode=1}}),G.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($,J)=>{R("Database Restore");try{let Y=await x(),X=J?.env||"production",{restoreDatabaseBackup:Q}=await import("./chunk-m7dgskt1.js"),Z=await Q({driver:c$({config:Y}),slug:Y.project.slug,environment:X,logger:v},{database:D2(Y),from:$});for(let z of Z.perInstance||[])W(` ${z.instanceId}: ${z.output?.trim()||z.status}`);if(!Z.success)K(`Restore failed: ${Z.error||"unknown error"}`),process.exitCode=1}catch(Y){K(`Restore failed: ${Y.message}`),process.exitCode=1}}),G.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($)=>{R("Quick Deploy (push-to-deploy)");try{let J=await x(),{buildQuickDeployCi:Y,inferQuickDeployProvider:X}=await import("./chunk-3vqfexc4.js"),Q=$?.provider?.toLowerCase();if(Q&&!["github","gitlab","bitbucket"].includes(Q))throw Error(`Unsupported CI provider '${$?.provider}'. Use github, gitlab, or bitbucket.`);let Z=Q;if(!Z)try{let F=H5("git remote get-url origin",{stdio:["ignore","pipe","ignore"]}).toString().trim();Z=X(F)}catch{}let z=Y(J,$?.env||"production",{provider:Z,site:$?.site,skipDnsVerification:$?.skipDnsVerification,setup:H$("pantry.lock")?"pantry":"bun",sshPrivateKeySecret:$?.sshKeySecret});if(!z){N("Could not resolve a GitHub, GitLab, or Bitbucket provider from --provider, the origin remote, or a configured site repository."),process.exitCode=1;return}if(H$(z.path)&&!$?.force){N(`${z.path} already exists — re-run with --force to overwrite.`);return}C2(zX(z.path),{recursive:!0}),WX(z.path,z.content),B(`Wrote ${z.path} (${z.provider}, deploys on push to '${z.branch}').`),W("Next: commit it and add your provider credentials as CI secrets/variables.")}catch(J){K(`Quick deploy setup failed: ${J.message}`),process.exitCode=1}}),G.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($)=>{try{let J=await x(),Y=$?.env||"production",{setMaintenance:X}=await import("./chunk-5mrtzemj.js");await X(J,Y,!0,$?.secret)}catch(J){K(`Failed to enable maintenance mode: ${J.message}`),process.exitCode=1}}),G.command("up","Bring the serverless app out of maintenance mode").option("--env <environment>","Environment (production, staging, development)").action(async($)=>{try{let J=await x(),Y=$?.env||"production",{setMaintenance:X}=await import("./chunk-5mrtzemj.js");await X(J,Y,!1)}catch(J){K(`Failed to disable maintenance mode: ${J.message}`),process.exitCode=1}}),G.command("command <cmd>","Run a command on the serverless CLI function (e.g. migrations)").option("--env <environment>","Environment (production, staging, development)").action(async($,J)=>{try{let Y=await x(),X=J?.env||"production",{runAndRecordCommand:Q}=await import("./chunk-5mrtzemj.js"),{id:Z,output:z}=await Q(Y,X,$);if(W(z),Z>0)W(`
389
- (recorded as #${Z} — re-run with \`cloud command:again ${Z}\`)`)}catch(Y){K(`Command failed: ${Y.message}`),process.exitCode=1}}),G.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($)=>{try{let J=await x(),Y=$?.env||"production",{listCommandHistory:X}=await import("./chunk-5mrtzemj.js"),Q=await X(J,Y);if(!Q.length){W("No command history yet.");return}let Z=Number($?.limit??20);R(`Command history — ${J.project.slug} (${Y})`),w(["#","When","Status","Command"],Q.slice(-Z).reverse().map((z)=>[String(z.id),z.timestamp,z.status,z.command]))}catch(J){K(`Failed to read history: ${J.message}`),process.exitCode=1}}),G.command("command:again [id]","Re-run a recorded command (defaults to the most recent)").option("--env <environment>","Environment (production, staging, development)").action(async($,J)=>{try{let Y=await x(),X=J?.env||"production",{getCommandRecord:Q,runAndRecordCommand:Z}=await import("./chunk-5mrtzemj.js"),z=await Q(Y,X,$?Number($):void 0);if(!z){K($?`No command #${$} in history.`:"No command history to re-run."),process.exitCode=1;return}W(`Re-running #${z.id}: ${z.command}`);let F=await Z(Y,X,z.command);if(W(F.output),F.id>0)W(`
390
- (recorded as #${F.id})`)}catch(Y){K(`Command failed: ${Y.message}`),process.exitCode=1}}),G.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($,J)=>{try{let Y=await x(),X=J?.env||"production",{runDbQuery:Q}=await import("./chunk-5mrtzemj.js"),Z=await Q(Y,X,$);W(Z)}catch(Y){K(`Query failed: ${Y.message}`),process.exitCode=1}}),G.command("serverless:db-scale <min> <max>","Rescale the serverless Aurora cluster (min/max ACUs)").option("--env <environment>","Environment (production, staging, development)").action(async($,J,Y)=>{R("Scaling serverless database");try{let X=await x(),Q=Y?.env||"production",Z=Number($),z=Number(J);if(!(Z>0)||!(z>=Z)){K("Provide valid capacities: min > 0 and max >= min (e.g. `serverless:db-scale 0.5 8`)."),process.exitCode=1;return}let{scaleServerlessDatabase:F}=await import("./chunk-5mrtzemj.js");await F(X,Q,Z,z)}catch(X){K(`Scale failed: ${X.message}`),process.exitCode=1}}),G.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($)=>{R("Restoring serverless database");try{let J=await x(),Y=$?.env||"production",X;if($?.to){if(X=new Date($.to),Number.isNaN(X.getTime())){K(`Invalid --to timestamp: ${$.to} (use ISO-8601, e.g. 2026-06-18T10:00:00Z).`),process.exitCode=1;return}}let{restoreServerlessDatabase:Q}=await import("./chunk-5mrtzemj.js");await Q(J,Y,{toTime:X,latest:$?.latest,target:$?.target})}catch(J){K(`Restore failed: ${J.message}`),process.exitCode=1}}),G.command("serverless:info","Show an operational summary of a deployed serverless app").option("--env <environment>","Environment (production, staging, development)").action(async($)=>{try{let J=await x(),Y=$?.env||"production",{serverlessInfo:X}=await import("./chunk-5mrtzemj.js"),Q=await X(J,Y);if(R(`${Q.slug} (${Q.environment})`),W(`Stack: ${Q.stackStatus}`),W(`Region: ${Q.region}`),Q.endpoint)W(`URL: ${Q.endpoint}`);if(Q.assetUrl)W(`Assets: ${Q.assetUrl}`);if(W(`Scheduler:${Q.scheduler==="off"?" off":` ${Q.scheduler}`}`),W(`Queues: ${Q.queues.length?Q.queues.join(", "):"none"}`),W(`Warming: ${Q.provisionedConcurrency>0?`provisioned concurrency ×${Q.provisionedConcurrency}`:"none / ping-warm"}`),Q.lastRelease)W(`Release: ${Q.lastRelease.sha.slice(0,12)} @ ${Q.lastRelease.timestamp}`);w(["Function","Version","Provisioned"],Q.functions.map((Z)=>[Z.name,Z.version,Z.provisioned?`${Z.provisioned.status} ${Z.provisioned.allocated}/${Z.provisioned.requested}`:"—"]))}catch(J){K(`Failed to read info: ${J.message}`),process.exitCode=1}}),G.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($)=>{R("Building management dashboard (live data)");try{let{existsSync:J}=await import("node:fs"),Y=$?.ui||"ui";if(!J(`${Y}/package.json`)){K(`No UI project at ${Y}/ (expected ${Y}/package.json). Pass --ui <dir>.`),process.exitCode=1;return}let X=await x(),Q=$?.env||"production";y("Gathering live data from AWS");let Z=null;if(X.environments?.[Q]?.app){let{resolveDashboardData:M}=await import("./chunk-5ectjq0b.js"),H=await M(X,Q).catch((j)=>{return N(`Serverless data unavailable (${j.message}) — that view uses sample data.`),null});if(H)Z={...Z??{},...H}}if(X.infrastructure?.compute){let{resolveServerDashboardData:M}=await import("./chunk-k9t9wvms.js"),H=await M(X,Q).catch(()=>null);if(H){if(H._serverReachable===!1)N("Server box not reachable over SSM — server view uses config + sample data.");Z={...Z??{},...H}}}y("Building dashboard");let{encodeDashboardPayload:z}=await import("./chunk-fz21b57w.js"),{execSync:F}=await import("node:child_process"),{rmSync:U}=await import("node:fs");for(let M of[".stx/ssg-cache",".stx/cache"])U(`${Y}/${M}`,{recursive:!0,force:!0});F("bun run build",{cwd:Y,stdio:"inherit",env:{...process.env,...Z?{TSCLOUD_DASHBOARD_DATA:z(Z)}:{}}}),B(`Dashboard built → ${Y}/dist${Z?" (live data)":" (sample data)"}`)}catch(J){K(`Dashboard build failed: ${J.message}`),process.exitCode=1}}),G.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($)=>{R("Building PHP runtime layer");try{let J=await x().catch(()=>null),Y=$?.region||J?.project.region||"us-east-1",X=$?.arch||"x86_64",Q=$?.php||"8.3",Z=`${$?.name||"tscloud-php"}-${Q.replace(".","")}-${X}`,z=$?.bucket||`${J?.project.slug||"tscloud"}-layers`,{buildPhpRuntimeLayerZip:F}=await import("./chunk-5n9fmabb.js"),{S3Client:U}=await import("./chunk-598yf4bs.js"),{LambdaClient:M}=await import("./chunk-9pvsff8g.js"),H=F({architecture:X,phpVersion:Q,onStep:(q)=>y(q)});W(`Layer: ${H.fileCount} files, ${(H.zip.length/1024/1024).toFixed(1)} MB`);let j=new U(Y);if(!await j.bucketExists(z))y(`Creating layer bucket ${z}`),await j.createBucket(z);let V=`layers/${Z}.zip`;y("Uploading layer zip"),await j.putObject({bucket:z,key:V,body:H.zip,contentType:"application/zip"}),y("Publishing layer version");let A=await new M(Y).publishLayerVersion({LayerName:Z,Description:`ts-cloud PHP ${Q} runtime (${X})`,Content:{S3Bucket:z,S3Key:V},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[X]});n$(["PHP runtime layer published","",`ARN: ${A.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'php', layers: ['${A.LayerVersionArn}'] }`,"or set TSCLOUD_PHP_LAYER_ARN before deploying."].join(`
391
- `),"green")}catch(J){K(`Layer build failed: ${J.message}`),process.exitCode=1}}),G.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($)=>{R("Building Node runtime layer");try{let J=await x().catch(()=>null),Y=$?.region||J?.project.region||"us-east-1",X=$?.arch||"x86_64",Q=$?.node||"24",{buildNodeRuntimeLayerZip:Z}=await import("./chunk-5n9fmabb.js"),{S3Client:z}=await import("./chunk-598yf4bs.js"),{LambdaClient:F}=await import("./chunk-9pvsff8g.js"),U=Z({architecture:X,version:Q,onStep:(q)=>y(q)}),M=`${$?.name||"tscloud-node"}-${U.version.split(".")[0]}-${X}`,H=$?.bucket||`${J?.project.slug||"tscloud"}-layers`;W(`Layer: Node ${U.version}, ${(U.zip.length/1024/1024).toFixed(1)} MB`);let j=new z(Y);if(!await j.bucketExists(H))y(`Creating layer bucket ${H}`),await j.createBucket(H);let V=`layers/${M}.zip`;y("Uploading layer zip"),await j.putObject({bucket:H,key:V,body:U.zip,contentType:"application/zip"}),y("Publishing layer version");let A=await new F(Y).publishLayerVersion({LayerName:M,Description:`ts-cloud Node ${U.version} runtime (${X})`,Content:{S3Bucket:H,S3Key:V},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[X]});n$(["Node runtime layer published","",`ARN: ${A.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'node', runtimeVersion: '${U.version.split(".")[0]}', layers: ['${A.LayerVersionArn}'] }`,"or set TSCLOUD_NODE_LAYER_ARN before deploying."].join(`
392
- `),"green")}catch(J){K(`Layer build failed: ${J.message}`),process.exitCode=1}}),G.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($)=>{R("Building Bun runtime layer");try{let J=await x().catch(()=>null),Y=$?.region||J?.project.region||"us-east-1",X=$?.arch||"x86_64",Q=$?.bun||"latest",{buildBunRuntimeLayerZip:Z}=await import("./chunk-5n9fmabb.js"),{S3Client:z}=await import("./chunk-598yf4bs.js"),{LambdaClient:F}=await import("./chunk-9pvsff8g.js"),U=Z({architecture:X,version:Q,onStep:(q)=>y(q)}),M=`${$?.name||"tscloud-bun"}-${U.version.replace(/\./g,"")}-${X}`,H=$?.bucket||`${J?.project.slug||"tscloud"}-layers`;W(`Layer: Bun ${U.version}, ${(U.zip.length/1024/1024).toFixed(1)} MB`);let j=new z(Y);if(!await j.bucketExists(H))y(`Creating layer bucket ${H}`),await j.createBucket(H);let V=`layers/${M}.zip`;y("Uploading layer zip"),await j.putObject({bucket:H,key:V,body:U.zip,contentType:"application/zip"}),y("Publishing layer version");let A=await new F(Y).publishLayerVersion({LayerName:M,Description:`ts-cloud Bun ${U.version} runtime (${X})`,Content:{S3Bucket:H,S3Key:V},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[X]});n$(["Bun runtime layer published","",`ARN: ${A.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'bun', layers: ['${A.LayerVersionArn}'] }`,"or set TSCLOUD_BUN_LAYER_ARN before deploying."].join(`
393
- `),"green")}catch(J){K(`Layer build failed: ${J.message}`),process.exitCode=1}}),G.command("deploy:status","Check deployment status").option("--stack <name>","Stack name").option("--env <environment>","Environment").action(async($)=>{R("Deployment Status");try{let J=await x(),Y=$?.env||"production",X=$?.stack||i$(J,Y),Q=J.project.region||"us-east-1";W(`Stack: ${X}`),W(`Region: ${Q}`);let Z=new E("Checking deployment status...");Z.start();let F=await new Y$(Q).describeStacks({stackName:X});if(F.Stacks.length===0){Z.fail("Stack not found"),X1("No deployment found for this environment");return}let U=F.Stacks[0];if(Z.succeed("Status retrieved"),W(`
394
- Status: ${U.StackStatus}`),W(`Created: ${U.CreationTime}`),U.LastUpdatedTime)W(`Last Updated: ${U.LastUpdatedTime}`);if(U.Outputs&&U.Outputs.length>0){W(`
395
- Outputs:`);for(let M of U.Outputs)W(` ${M.OutputKey}: ${M.OutputValue}`)}}catch(J){K(`Failed to get status: ${J.message}`)}}),G.command("deploy:rollback","Rollback to previous version").option("--stack <name>","Stack name").option("--env <environment>","Environment").action(async($)=>{R("Rolling Back Deployment");try{let J=await x(),Y=$?.env||"production",X=$?.stack||i$(J,Y),Q=J.project.region||"us-east-1";if(W(`Stack: ${X}`),W(`Region: ${Q}`),!await k(`
396
- Are you sure you want to rollback?`,!1)){W("Rollback cancelled");return}let z=new E("Rolling back stack...");z.start();let F=new Y$(Q);await F.deleteStack(X),z.succeed("Stack deletion initiated"),y("Waiting for stack deletion..."),await F.waitForStack(X,"stack-delete-complete"),B("Stack rolled back successfully!")}catch(J){K(`Rollback failed: ${J.message}`)}}),G.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($)=>{R("Deploying Static Site");try{let J=await x(),Y=J.project.region||"us-east-1",X=$?.source||"dist",Q;if(!$?.skipSecurityScan){y("Scanning source directory for leaked secrets...");let{result:D}=await i1({sourceDir:X,failOnSeverity:$?.securityFailOn||"critical"});t1(D),Q=D}else N(`Security scan skipped (--skip-security-scan)
397
- `);let Z=nJ(J,void 0,Q);if(!Z.allowed){K(`
398
- ✗ Static deployment blocked by security policy: ${Z.explanation}`),W("Remediate the findings or create an authorized, time-limited waiver in the Security posture center."),process.exitCode=1;return}B(`✓ Security policy allows this deployment
399
- `);let z=$?.bucket,F=$?.distribution,U=$?.prefix,M=$?.delete||!1,H=$?.cacheControl||"public, max-age=31536000",j=$?.invalidate!==!1,V=$?.wait||!1;if(!z){K("--bucket is required");return}if(!H$(X)){K(`Source directory not found: ${X}`);return}if(W(`Source: ${X}`),W(`Bucket: s3://${z}${U?`/${U}`:""}`),W(`Cache-Control: ${H}`),F)W(`CloudFront Distribution: ${F}`);if(M)N("Delete mode enabled - files not in source will be removed");if(!await k(`
400
- Deploy static site now?`,!0)){W("Deployment cancelled");return}let A=new Z$(Y),q=new E("Uploading files to S3...");q.start(),await A.sync({source:X,bucket:z,prefix:U,delete:M,cacheControl:H,acl:"public-read"}),q.succeed("Files uploaded successfully!");let L=(await A.getBucketSize(z,U)/1024/1024).toFixed(2);if(W(`Total size: ${L} MB`),j&&F){let D=new $$,P=new E("Invalidating CloudFront cache...");P.start();let b=await D.invalidateAll(F);if(P.succeed("Invalidation created"),W(`Invalidation ID: ${b.Id}`),V){let _=new E("Waiting for invalidation to complete...");_.start(),await D.waitForInvalidation(F,b.Id),_.succeed("Invalidation completed!")}}n$(`Static Site Deployed!
401
-
402
- Source: ${X}
403
- Bucket: s3://${z}${U?`/${U}`:""}
404
- Size: ${L} MB
405
- ${F?`Distribution: ${F}`:""}
392
+ https://console.aws.amazon.com/cloudformation/home?region=${f}#/stacks/stackinfo?stackId=${encodeURIComponent(m)}`,"green"),Object.keys(H).length>0){a(`
393
+ Stack Outputs:`);for(let[j,W]of Object.entries(H))a(` - ${j}: ${W}`)}if(s.infrastructure?.storage)for(let[j,W]of Object.entries(s.infrastructure.storage)){if(!W.website||!W.root)continue;let ue=H[`${j}BucketName`]||H.FrontendBucketName,ae=H[`${j}CloudFrontDistributionId`];if(!ue){I(`Could not find bucket name for storage '${j}' — skipping file upload`);continue}if(!ge(W.root)){I(`Source directory not found: ${W.root} — skipping file upload for '${j}'`);continue}M(`Uploading files from ${W.root} to s3://${ue}...`);let{uploadStaticFiles:Fe,invalidateCache:jt}=await import("./chunk-ddmjjstg.js"),ye=await Fe({sourceDir:W.root,bucket:ue,region:f,onProgress:(Y,X,ke)=>{if(Y%10===0||Y===X)a(` ${Y}/${X}: ${ke}`)}});if(ye.errors.length>0)I(`Upload completed with errors: ${ye.errors.join(", ")}`);else{let Y=ye.skipped>0?`Uploaded ${ye.uploaded} files (${ye.skipped} unchanged)`:`Uploaded ${ye.uploaded} files`;g(Y)}if(ae&&ye.uploaded>0){M("Invalidating CloudFront cache...");let{invalidationId:Y}=await jt(ae);g(`Cache invalidation created: ${Y}`)}}if(s.infrastructure?.compute)await lo(s,n,f,e?.site);if(s.infrastructure?.compute&&s.sites){let j=Object.values(s.sites).map((W)=>W.domain).filter((W)=>!!W);if(j.length>0)M("Syncing CloudFront dynamic HTTP methods for app domains..."),await sr(j)}}catch(o){if(p(`Deployment failed: ${o.message}`),process.exitCode=1,o.stack)a(`
394
+ Stack trace:`),console.error(o.stack)}finally{if(i)await i()}}),r.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(e)=>{w("Deploying Server Infrastructure");try{let t=await P(),n=e?.env||"production",i=t.project.region||"us-east-1",o=mo(t);if(o){p(o),process.exitCode=1;return}if(!po(t)){p("No `infrastructure.compute` configured — nothing to deploy as a server."),a("Add an EC2 compute block to cloud.config, or use `cloud deploy:serverless` for Lambda apps."),process.exitCode=1;return}if(a(`Region: ${i}`),a(`Environment: ${n}`),!await lo(t,n,i,e?.site)){process.exitCode=1;return}if(t.sites){let c=Object.values(t.sites).map((l)=>l.domain).filter((l)=>!!l);if(c.length>0)M("Syncing CloudFront dynamic HTTP methods for app domains..."),await sr(c)}g(`
395
+ Server deployment complete!`)}catch(t){p(`Deployment failed: ${t.message}`),process.exitCode=1}}),r.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(e)=>{try{let t=await P(),n=e?.env||"production",i=mo(t);if(i){p(i),process.exitCode=1;return}let{deployServerlessApp:o,redeployServerlessApp:s}=await import("./chunk-jnx4jgw8.js");if(e?.redeploy){await s(t,n);return}await o(t,n,{skipBuild:e?.skipBuild,skipDeployHooks:e?.skipHooks})}catch(t){p(`Deployment failed: ${t.message}`),process.exitCode=1}}),r.command("serverless:rollback","Roll back a serverless app to the previous build").option("--env <environment>","Environment (production, staging, development)").action(async(e)=>{try{let t=await P(),n=e?.env||"production",{rollbackServerlessApp:i}=await import("./chunk-jnx4jgw8.js");await i(t,n)}catch(t){p(`Rollback failed: ${t.message}`),process.exitCode=1}}),r.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(e,t)=>{w("Rolling Back Release");try{let n=await P(),i=t?.env||"production",o=e||Object.keys(n.sites||{})[0];if(!o){p("No site configured to roll back."),process.exitCode=1;return}let{rollbackComputeSite:s}=await import("./chunk-g6a4n3rh.js"),c=await s({driver:Ke({config:n}),slug:n.project.slug,environment:i,logger:z},{siteName:o,to:t?.to});if(!c.success){p(`Rollback failed: ${c.error||"unknown error"}`),process.exitCode=1;return}for(let l of c.perInstance||[])a(` ${l.instanceId}: ${l.output?.trim()||l.status}`)}catch(n){p(`Rollback failed: ${n.message}`),process.exitCode=1}}),r.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(e,t)=>{w("Deployment History");try{let n=await P(),i=t?.env||"production",o=e||Object.keys(n.sites||{})[0];if(!o){p("No site configured."),process.exitCode=1;return}let{getComputeDeployHistory:s}=await import("./chunk-g6a4n3rh.js"),c=await s({driver:Ke({config:n}),slug:n.project.slug,environment:i,logger:z},{siteName:o,limit:Number.parseInt(t?.limit||"20",10)||20});if(!c.success){p(`Could not read history: ${c.error||"unknown error"}`),process.exitCode=1;return}for(let l of c.perInstance||[])a(`
396
+ ${l.instanceId}:`),a(l.output?.trimEnd()||"(no output)")}catch(n){p(`History lookup failed: ${n.message}`),process.exitCode=1}}),r.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(e,t,n)=>{w(`Running Recipe: ${e}`);try{if(!ge(t)){p(`Recipe script not found: ${t}`),process.exitCode=1;return}let i=await P(),o=n?.env||"production",s=fo(t,"utf8").split(`
397
+ `),{runComputeRecipe:c}=await import("./chunk-g6a4n3rh.js"),l=await c({driver:Ke({config:i}),slug:i.project.slug,environment:o,logger:z},{name:e,script:s,user:n?.user});for(let d of l.perInstance||[])a(`
398
+ ${d.instanceId}:`),a(d.output?.trimEnd()||"(no output)");if(!l.success)process.exitCode=1}catch(i){p(`Recipe failed: ${i.message}`),process.exitCode=1}}),r.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(e,t)=>{w("Database Restore");try{let n=await P(),i=t?.env||"production",{restoreDatabaseBackup:o}=await import("./chunk-g6a4n3rh.js"),s=await o({driver:Ke({config:n}),slug:n.project.slug,environment:i,logger:z},{database:Fn(n),from:e});for(let c of s.perInstance||[])a(` ${c.instanceId}: ${c.output?.trim()||c.status}`);if(!s.success)p(`Restore failed: ${s.error||"unknown error"}`),process.exitCode=1}catch(n){p(`Restore failed: ${n.message}`),process.exitCode=1}}),r.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(e)=>{w("Quick Deploy (push-to-deploy)");try{let t=await P(),{buildQuickDeployCi:n,inferQuickDeployProvider:i}=await import("./chunk-35ttwn26.js"),o=e?.provider?.toLowerCase();if(o&&!["github","gitlab","bitbucket"].includes(o))throw Error(`Unsupported CI provider '${e?.provider}'. Use github, gitlab, or bitbucket.`);let s=o;if(!s)try{let l=Io("git remote get-url origin",{stdio:["ignore","pipe","ignore"]}).toString().trim();s=i(l)}catch{}let c=n(t,e?.env||"production",{provider:s,site:e?.site,skipDnsVerification:e?.skipDnsVerification,setup:ge("pantry.lock")?"pantry":"bun",sshPrivateKeySecret:e?.sshKeySecret});if(!c){I("Could not resolve a GitHub, GitLab, or Bitbucket provider from --provider, the origin remote, or a configured site repository."),process.exitCode=1;return}if(ge(c.path)&&!e?.force){I(`${c.path} already exists — re-run with --force to overwrite.`);return}gl(Km(c.path),{recursive:!0}),Gm(c.path,c.content),g(`Wrote ${c.path} (${c.provider}, deploys on push to '${c.branch}').`),a("Next: commit it and add your provider credentials as CI secrets/variables.")}catch(t){p(`Quick deploy setup failed: ${t.message}`),process.exitCode=1}}),r.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(e)=>{try{let t=await P(),n=e?.env||"production",{setMaintenance:i}=await import("./chunk-jnx4jgw8.js");await i(t,n,!0,e?.secret)}catch(t){p(`Failed to enable maintenance mode: ${t.message}`),process.exitCode=1}}),r.command("up","Bring the serverless app out of maintenance mode").option("--env <environment>","Environment (production, staging, development)").action(async(e)=>{try{let t=await P(),n=e?.env||"production",{setMaintenance:i}=await import("./chunk-jnx4jgw8.js");await i(t,n,!1)}catch(t){p(`Failed to disable maintenance mode: ${t.message}`),process.exitCode=1}}),r.command("command <cmd>","Run a command on the serverless CLI function (e.g. migrations)").option("--env <environment>","Environment (production, staging, development)").action(async(e,t)=>{try{let n=await P(),i=t?.env||"production",{runAndRecordCommand:o}=await import("./chunk-jnx4jgw8.js"),{id:s,output:c}=await o(n,i,e);if(a(c),s>0)a(`
399
+ (recorded as #${s} — re-run with \`cloud command:again ${s}\`)`)}catch(n){p(`Command failed: ${n.message}`),process.exitCode=1}}),r.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(e)=>{try{let t=await P(),n=e?.env||"production",{listCommandHistory:i}=await import("./chunk-jnx4jgw8.js"),o=await i(t,n);if(!o.length){a("No command history yet.");return}let s=Number(e?.limit??20);w(`Command history — ${t.project.slug} (${n})`),x(["#","When","Status","Command"],o.slice(-s).reverse().map((c)=>[String(c.id),c.timestamp,c.status,c.command]))}catch(t){p(`Failed to read history: ${t.message}`),process.exitCode=1}}),r.command("command:again [id]","Re-run a recorded command (defaults to the most recent)").option("--env <environment>","Environment (production, staging, development)").action(async(e,t)=>{try{let n=await P(),i=t?.env||"production",{getCommandRecord:o,runAndRecordCommand:s}=await import("./chunk-jnx4jgw8.js"),c=await o(n,i,e?Number(e):void 0);if(!c){p(e?`No command #${e} in history.`:"No command history to re-run."),process.exitCode=1;return}a(`Re-running #${c.id}: ${c.command}`);let l=await s(n,i,c.command);if(a(l.output),l.id>0)a(`
400
+ (recorded as #${l.id})`)}catch(n){p(`Command failed: ${n.message}`),process.exitCode=1}}),r.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(e,t)=>{try{let n=await P(),i=t?.env||"production",{runDbQuery:o}=await import("./chunk-jnx4jgw8.js"),s=await o(n,i,e);a(s)}catch(n){p(`Query failed: ${n.message}`),process.exitCode=1}}),r.command("serverless:db-scale <min> <max>","Rescale the serverless Aurora cluster (min/max ACUs)").option("--env <environment>","Environment (production, staging, development)").action(async(e,t,n)=>{w("Scaling serverless database");try{let i=await P(),o=n?.env||"production",s=Number(e),c=Number(t);if(!(s>0)||!(c>=s)){p("Provide valid capacities: min > 0 and max >= min (e.g. `serverless:db-scale 0.5 8`)."),process.exitCode=1;return}let{scaleServerlessDatabase:l}=await import("./chunk-jnx4jgw8.js");await l(i,o,s,c)}catch(i){p(`Scale failed: ${i.message}`),process.exitCode=1}}),r.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(e)=>{w("Restoring serverless database");try{let t=await P(),n=e?.env||"production",i;if(e?.to){if(i=new Date(e.to),Number.isNaN(i.getTime())){p(`Invalid --to timestamp: ${e.to} (use ISO-8601, e.g. 2026-06-18T10:00:00Z).`),process.exitCode=1;return}}let{restoreServerlessDatabase:o}=await import("./chunk-jnx4jgw8.js");await o(t,n,{toTime:i,latest:e?.latest,target:e?.target})}catch(t){p(`Restore failed: ${t.message}`),process.exitCode=1}}),r.command("serverless:info","Show an operational summary of a deployed serverless app").option("--env <environment>","Environment (production, staging, development)").action(async(e)=>{try{let t=await P(),n=e?.env||"production",{serverlessInfo:i}=await import("./chunk-jnx4jgw8.js"),o=await i(t,n);if(w(`${o.slug} (${o.environment})`),a(`Stack: ${o.stackStatus}`),a(`Region: ${o.region}`),o.endpoint)a(`URL: ${o.endpoint}`);if(o.assetUrl)a(`Assets: ${o.assetUrl}`);if(a(`Scheduler:${o.scheduler==="off"?" off":` ${o.scheduler}`}`),a(`Queues: ${o.queues.length?o.queues.join(", "):"none"}`),a(`Warming: ${o.provisionedConcurrency>0?`provisioned concurrency ×${o.provisionedConcurrency}`:"none / ping-warm"}`),o.lastRelease)a(`Release: ${o.lastRelease.sha.slice(0,12)} @ ${o.lastRelease.timestamp}`);x(["Function","Version","Provisioned"],o.functions.map((s)=>[s.name,s.version,s.provisioned?`${s.provisioned.status} ${s.provisioned.allocated}/${s.provisioned.requested}`:"—"]))}catch(t){p(`Failed to read info: ${t.message}`),process.exitCode=1}}),r.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(e)=>{w("Building management dashboard (live data)");try{let{existsSync:t}=await import("node:fs"),n=e?.ui||"ui";if(!t(`${n}/package.json`)){p(`No UI project at ${n}/ (expected ${n}/package.json). Pass --ui <dir>.`),process.exitCode=1;return}let i=await P(),o=e?.env||"production";M("Gathering live data from AWS");let s=null;if(i.environments?.[o]?.app){let{resolveDashboardData:u}=await import("./chunk-08c0pzkb.js"),m=await u(i,o).catch((f)=>(I(`Serverless data unavailable (${f.message}) — that view uses sample data.`),null));if(m)s={...s??{},...m}}if(i.infrastructure?.compute){let{resolveServerDashboardData:u}=await import("./chunk-6gktf2m2.js"),m=await u(i,o).catch(()=>null);if(m){if(m._serverReachable===!1)I("Server box not reachable over SSM — server view uses config + sample data.");s={...s??{},...m}}}M("Building dashboard");let{encodeDashboardPayload:c}=await import("./chunk-gs40j75w.js"),{execSync:l}=await import("node:child_process"),{rmSync:d}=await import("node:fs");for(let u of[".stx/ssg-cache",".stx/cache"])d(`${n}/${u}`,{recursive:!0,force:!0});l("bun run build",{cwd:n,stdio:"inherit",env:{...process.env,...s?{TSCLOUD_DASHBOARD_DATA:c(s)}:{}}}),g(`Dashboard built → ${n}/dist${s?" (live data)":" (sample data)"}`)}catch(t){p(`Dashboard build failed: ${t.message}`),process.exitCode=1}}),r.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(e)=>{w("Building PHP runtime layer");try{let t=await P().catch(()=>null),n=e?.region||t?.project.region||"us-east-1",i=e?.arch||"x86_64",o=e?.php||"8.3",s=`${e?.name||"tscloud-php"}-${o.replace(".","")}-${i}`,c=e?.bucket||`${t?.project.slug||"tscloud"}-layers`,{buildPhpRuntimeLayerZip:l}=await import("./chunk-ezftw2q4.js"),{S3Client:d}=await import("./chunk-w5d6tr2x.js"),{LambdaClient:u}=await import("./chunk-ds1z880f.js"),m=l({architecture:i,phpVersion:o,onStep:(v)=>M(v)});a(`Layer: ${m.fileCount} files, ${(m.zip.length/1024/1024).toFixed(1)} MB`);let f=new d(n);if(!await f.bucketExists(c))M(`Creating layer bucket ${c}`),await f.createBucket(c);let h=`layers/${s}.zip`;M("Uploading layer zip"),await f.putObject({bucket:c,key:h,body:m.zip,contentType:"application/zip"}),M("Publishing layer version");let y=await new u(n).publishLayerVersion({LayerName:s,Description:`ts-cloud PHP ${o} runtime (${i})`,Content:{S3Bucket:c,S3Key:h},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[i]});Qe(["PHP runtime layer published","",`ARN: ${y.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'php', layers: ['${y.LayerVersionArn}'] }`,"or set TSCLOUD_PHP_LAYER_ARN before deploying."].join(`
401
+ `),"green")}catch(t){p(`Layer build failed: ${t.message}`),process.exitCode=1}}),r.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(e)=>{w("Building Node runtime layer");try{let t=await P().catch(()=>null),n=e?.region||t?.project.region||"us-east-1",i=e?.arch||"x86_64",o=e?.node||"24",{buildNodeRuntimeLayerZip:s}=await import("./chunk-ezftw2q4.js"),{S3Client:c}=await import("./chunk-w5d6tr2x.js"),{LambdaClient:l}=await import("./chunk-ds1z880f.js"),d=s({architecture:i,version:o,onStep:(v)=>M(v)}),u=`${e?.name||"tscloud-node"}-${d.version.split(".")[0]}-${i}`,m=e?.bucket||`${t?.project.slug||"tscloud"}-layers`;a(`Layer: Node ${d.version}, ${(d.zip.length/1024/1024).toFixed(1)} MB`);let f=new c(n);if(!await f.bucketExists(m))M(`Creating layer bucket ${m}`),await f.createBucket(m);let h=`layers/${u}.zip`;M("Uploading layer zip"),await f.putObject({bucket:m,key:h,body:d.zip,contentType:"application/zip"}),M("Publishing layer version");let y=await new l(n).publishLayerVersion({LayerName:u,Description:`ts-cloud Node ${d.version} runtime (${i})`,Content:{S3Bucket:m,S3Key:h},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[i]});Qe(["Node runtime layer published","",`ARN: ${y.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'node', runtimeVersion: '${d.version.split(".")[0]}', layers: ['${y.LayerVersionArn}'] }`,"or set TSCLOUD_NODE_LAYER_ARN before deploying."].join(`
402
+ `),"green")}catch(t){p(`Layer build failed: ${t.message}`),process.exitCode=1}}),r.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(e)=>{w("Building Bun runtime layer");try{let t=await P().catch(()=>null),n=e?.region||t?.project.region||"us-east-1",i=e?.arch||"x86_64",o=e?.bun||"latest",{buildBunRuntimeLayerZip:s}=await import("./chunk-ezftw2q4.js"),{S3Client:c}=await import("./chunk-w5d6tr2x.js"),{LambdaClient:l}=await import("./chunk-ds1z880f.js"),d=s({architecture:i,version:o,onStep:(v)=>M(v)}),u=`${e?.name||"tscloud-bun"}-${d.version.replace(/\./g,"")}-${i}`,m=e?.bucket||`${t?.project.slug||"tscloud"}-layers`;a(`Layer: Bun ${d.version}, ${(d.zip.length/1024/1024).toFixed(1)} MB`);let f=new c(n);if(!await f.bucketExists(m))M(`Creating layer bucket ${m}`),await f.createBucket(m);let h=`layers/${u}.zip`;M("Uploading layer zip"),await f.putObject({bucket:m,key:h,body:d.zip,contentType:"application/zip"}),M("Publishing layer version");let y=await new l(n).publishLayerVersion({LayerName:u,Description:`ts-cloud Bun ${d.version} runtime (${i})`,Content:{S3Bucket:m,S3Key:h},CompatibleRuntimes:["provided.al2023"],CompatibleArchitectures:[i]});Qe(["Bun runtime layer published","",`ARN: ${y.LayerVersionArn}`,"","Reference it in your config:",` app: { kind: 'bun', layers: ['${y.LayerVersionArn}'] }`,"or set TSCLOUD_BUN_LAYER_ARN before deploying."].join(`
403
+ `),"green")}catch(t){p(`Layer build failed: ${t.message}`),process.exitCode=1}}),r.command("deploy:status","Check deployment status").option("--stack <name>","Stack name").option("--env <environment>","Environment").action(async(e)=>{w("Deployment Status");try{let t=await P(),n=e?.env||"production",i=e?.stack||Ve(t,n),o=t.project.region||"us-east-1";a(`Stack: ${i}`),a(`Region: ${o}`);let s=new C("Checking deployment status...");s.start();let l=await new ie(o).describeStacks({stackName:i});if(l.Stacks.length===0){s.fail("Stack not found"),fn("No deployment found for this environment");return}let d=l.Stacks[0];if(s.succeed("Status retrieved"),a(`
404
+ Status: ${d.StackStatus}`),a(`Created: ${d.CreationTime}`),d.LastUpdatedTime)a(`Last Updated: ${d.LastUpdatedTime}`);if(d.Outputs&&d.Outputs.length>0){a(`
405
+ Outputs:`);for(let u of d.Outputs)a(` ${u.OutputKey}: ${u.OutputValue}`)}}catch(t){p(`Failed to get status: ${t.message}`)}}),r.command("deploy:rollback","Rollback to previous version").option("--stack <name>","Stack name").option("--env <environment>","Environment").action(async(e)=>{w("Rolling Back Deployment");try{let t=await P(),n=e?.env||"production",i=e?.stack||Ve(t,n),o=t.project.region||"us-east-1";if(a(`Stack: ${i}`),a(`Region: ${o}`),!await O(`
406
+ Are you sure you want to rollback?`,!1)){a("Rollback cancelled");return}let c=new C("Rolling back stack...");c.start();let l=new ie(o);await l.deleteStack(i),c.succeed("Stack deletion initiated"),M("Waiting for stack deletion..."),await l.waitForStack(i,"stack-delete-complete"),g("Stack rolled back successfully!")}catch(t){p(`Rollback failed: ${t.message}`)}}),r.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(e)=>{w("Deploying Static Site");try{let t=await P(),n=t.project.region||"us-east-1",i=e?.source||"dist",o;if(!e?.skipSecurityScan){M("Scanning source directory for leaked secrets...");let{result:A}=await ar({sourceDir:i,failOnSeverity:e?.securityFailOn||"critical"});cr(A),o=A}else I(`Security scan skipped (--skip-security-scan)
407
+ `);let s=uo(t,void 0,o);if(!s.allowed){p(`
408
+ ✗ Static deployment blocked by security policy: ${s.explanation}`),a("Remediate the findings or create an authorized, time-limited waiver in the Security posture center."),process.exitCode=1;return}g(`✓ Security policy allows this deployment
409
+ `);let c=e?.bucket,l=e?.distribution,d=e?.prefix,u=e?.delete||!1,m=e?.cacheControl||"public, max-age=31536000",f=e?.invalidate!==!1,h=e?.wait||!1;if(!c){p("--bucket is required");return}if(!ge(i)){p(`Source directory not found: ${i}`);return}if(a(`Source: ${i}`),a(`Bucket: s3://${c}${d?`/${d}`:""}`),a(`Cache-Control: ${m}`),l)a(`CloudFront Distribution: ${l}`);if(u)I("Delete mode enabled - files not in source will be removed");if(!await O(`
410
+ Deploy static site now?`,!0)){a("Deployment cancelled");return}let y=new de(n),v=new C("Uploading files to S3...");v.start(),await y.sync({source:i,bucket:c,prefix:d,delete:u,cacheControl:m,acl:"public-read"}),v.succeed("Files uploaded successfully!");let b=(await y.getBucketSize(c,d)/1024/1024).toFixed(2);if(a(`Total size: ${b} MB`),f&&l){let A=new ne,k=new C("Invalidating CloudFront cache...");k.start();let T=await A.invalidateAll(l);if(k.succeed("Invalidation created"),a(`Invalidation ID: ${T.Id}`),h){let D=new C("Waiting for invalidation to complete...");D.start(),await A.waitForInvalidation(l,T.Id),D.succeed("Invalidation completed!")}}Qe(`Static Site Deployed!
411
+
412
+ Source: ${i}
413
+ Bucket: s3://${c}${d?`/${d}`:""}
414
+ Size: ${b} MB
415
+ ${l?`Distribution: ${l}`:""}
406
416
 
407
417
  View your site:
408
- https://${z}.s3.${Y}.amazonaws.com${U?`/${U}`:""}/index.html`,"green")}catch(J){K(`Deployment failed: ${J.message}`),process.exitCode=1}}),G.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($)=>{R("Deploying Container");try{let J=await x(),Y=J.project.region||"us-east-1",X=$?.cluster,Q=$?.service,Z=$?.repository,z=$?.image||"latest",F=$?.dockerfile||"Dockerfile",U=$?.context||".",M=$?.force||!1,H=$?.wait||!1,j=new Date().toISOString(),V=crypto.randomUUID();if(!X||!Q){K("--cluster and --service are required");return}if(!Z){K("--repository is required");return}if(!H$(F)){K(`Dockerfile not found: ${F}`);return}let T;if(!$?.skipSecurityScan){y("Scanning build context for leaked secrets...");let{result:i}=await i1({sourceDir:U,failOnSeverity:$?.securityFailOn||"critical"});t1(i),T=i}else N(`Security scan skipped (--skip-security-scan)
409
- `);let A=nJ(J,void 0,T);if(!A.allowed){K(`
410
- ✗ Container deployment blocked by security policy: ${A.explanation}`),W("Remediate the findings or create an authorized, time-limited waiver in the Security posture center."),process.exitCode=1;return}if(B(`✓ Security policy allows this deployment
411
- `),W(`Cluster: ${X}`),W(`Service: ${Q}`),W(`Repository: ${Z}`),W(`Image Tag: ${z}`),W(`Dockerfile: ${F}`),!await k(`
412
- Deploy container now?`,!0)){W("Deployment cancelled");return}let O=new V1(Y),L=new W5(Y),D=new E("Getting ECR credentials...");D.start();let P=await O.getAuthorizationToken();if(!P.authorizationData?.[0]){D.fail("Failed to get ECR credentials");return}let b=P.authorizationData[0],S=(b.proxyEndpoint||"").replace("https://","");D.succeed("ECR credentials obtained");let I=new E("Logging into ECR...");I.start();let g=b.authorizationToken||"",d=Buffer.from(g,"base64").toString("utf8").split(":")[1],{spawn:r}=await import("child_process"),W$=r("docker",["login","--username","AWS","--password-stdin",S],{stdio:["pipe","pipe","pipe"]});W$.stdin.write(d),W$.stdin.end(),await new Promise((i,O$)=>{W$.on("close",(y$)=>{if(y$===0)i();else O$(Error(`Docker login failed with code ${y$}`))})}),I.succeed("Logged into ECR");let l=new E("Building Docker image...");l.start();let s=`${S}/${Z}:${z}`,e=r("docker",["build","-t",s,"-f",F,U],{stdio:["pipe","pipe","pipe"]});await new Promise((i,O$)=>{let y$="";e.stderr.on("data",(L0)=>{y$+=L0.toString()}),e.on("close",(L0)=>{if(L0===0)i();else O$(Error(`Docker build failed: ${y$}`))})}),l.succeed("Docker image built");let C=new E("Pushing image to ECR...");C.start();let c=r("docker",["push",s],{stdio:["pipe","pipe","pipe"]});await new Promise((i,O$)=>{let y$="";c.stderr.on("data",(L0)=>{y$+=L0.toString()}),c.on("close",(L0)=>{if(L0===0)i();else O$(Error(`Docker push failed: ${y$}`))})}),C.succeed("Image pushed to ECR");let z$,X$=r("docker",["image","inspect","--format={{.Id}}",s],{stdio:["ignore","pipe","pipe"]}),[h$,k0]=await Promise.all([new Promise((i)=>X$.on("close",i)),new Response(X$.stdout).text()]);if(h$===0){let i=k0.trim();if(/^sha256:[a-f0-9]{64}$/i.test(i))z$=i}let j$=await KX(J,{imageRef:s,imageSha256:z$,artifactRoot:U,invocationId:V,startedAt:j});if(!j$.allowed){K(`
413
- ✗ ECS service update blocked by release security policy: ${j$.explanation}`),W("The image remains in ECR for investigation. Remediate and rebuild it, or create an authorized, time-limited waiver."),process.exitCode=1;return}B(`✓ Release security policy allows the ECS service update
414
- `);let a=new E("Updating ECS service...");if(a.start(),await L.updateService({cluster:X,service:Q,forceNewDeployment:M}),a.succeed("ECS service updated"),H){let i=new E("Waiting for deployment to stabilize...");i.start(),await L.waitForServiceStable(X,Q),i.succeed("Deployment stabilized")}B(`
415
- Container deployment complete!`),W(`
416
- Image: ${s}`),W(`Cluster: ${X}`),W(`Service: ${Q}`)}catch(J){K(`Deployment failed: ${J.message}`),process.exitCode=1}})}async function BX(G){let $=process.cwd(),J=`${$}/.env`,Y=`${$}/.env.local`,Z=({stage:["stage","staging"],staging:["staging","stage"]}[G]||[G]).map((H)=>`${$}/.env.${H}`).find((H)=>H$(H));if(!Z){if(H$(J))N(`No .env.${G} found, falling back to .env`);else W(`No .env.${G} or .env file found; using the existing process environment`);return null}let z=Z.split("/").pop();y(`Loading environment file: ${z}`);let F=null,U=null;if(H$(Y)){F=`${Y}.bak`,K1(Y,F);let H=tJ(Y,"utf-8");for(let V of H.split(`
417
- `)){let T=V.trim();if(!T||T.startsWith("#"))continue;let A=T.indexOf("=");if(A===-1)continue;let q=T.slice(0,A).trim();delete process.env[q]}let{unlinkSync:j}=await import("node:fs");j(Y),W("Temporarily moved .env.local out of the way and purged its values from process.env")}if(H$(J))U=`${J}.bak`,K1(J,U);K1(Z,J);let M=tJ(Z,"utf-8");for(let H of M.split(`
418
- `)){let j=H.trim();if(!j||j.startsWith("#"))continue;let V=j.indexOf("=");if(V===-1)continue;let T=j.slice(0,V).trim(),A=j.slice(V+1).trim();process.env[T]=A}return B(`Loaded ${z}`),async()=>{let{unlinkSync:H}=await import("node:fs");if(U)K1(U,`${$}/.env`),H(U),W("Restored .env");if(F)K1(F,`${$}/.env.local`),H(F),W("Restored .env.local")}}async function VX(G,$,J,Y){if(!J)return;let X=G5(G.sites||{});if(X.size===0)return;if(!$){N("DNS: no app server IP resolved — skipping A-record reconciliation.");return}let Q;try{Q=U$(J)}catch(M){N(`DNS: ${M instanceof Error?M.message:String(M)}`),W(`Point each site domain at ${$} manually.`);return}if(!Q){N(`DNS provider '${J}' is not configured — skipping A-record reconciliation.`),W(`Point each site domain at ${$} manually.`);return}if(Q.provider==="route53"){let M=G.infrastructure?.dns?.hostedZoneId;if(M&&!Q.hostedZoneId)Q.hostedZoneId=M}let Z=U0(Q),z=G.infrastructure?.dns?.domain,F=e1(Y),U=F?"A/AAAA":"A";y(`Reconciling DNS: ${X.size} ${U} record(s) → ${$} via ${J}...`);for(let M of X){let H=z&&M.endsWith(z)?z:M.split(".").slice(-2).join(".");try{let j=await X5({provider:Z,zone:H,fqdn:M,ipv4:$,ipv6:F});for(let V of j.published)B(` ✓ ${V.fqdn} ${V.type} → ${V.content}`);for(let V of j.warnings)N(` ⚠ ${M}: ${V}`)}catch(j){N(` ⚠ ${M}: ${j instanceof Error?j.message:String(j)} — set the address record manually.`)}}}async function AX(G,$){let J=G.infrastructure?.dns?.records;if(!$||!J?.length)return;let Y=G.infrastructure?.dns?.domain;if(!Y){N("DNS records: infrastructure.dns.domain is not set — skipping declared records.");return}let X;try{X=U$($)}catch(z){N(`DNS records: ${z instanceof Error?z.message:String(z)}`);return}if(!X){N(`DNS records: provider '${$}' is not configured — skipping.`);return}let Q=U0(X),Z=oJ(J,Y);y(`Reconciling ${Z.length} declared DNS record(s) on ${Y}...`);try{let z=await $5({provider:Q,zone:Y,records:Z});for(let F of z.outcomes){let{record:U,action:M}=F,H=`${U.type} ${U.name} -> ${U.content.slice(0,60)}`;if(M==="failed")N(` ⚠ ${H}: ${F.message||"failed"}`);else if(M==="unchanged")W(` = ${H}`);else B(` ✓ ${H} (${M})`)}for(let F of z.warnings)N(` ⚠ ${F}`)}catch(z){N(`DNS records: ${z instanceof Error?z.message:String(z)}`)}}async function qX(G,$,J){let{plan:Y,errors:X}=aJ(G);for(let Z of X)N(`Cloudflare CDN: ${Z}`);if(!Y)return;if(!$){N("Cloudflare CDN: no app server IP resolved — skipping.");return}y(`Cloudflare CDN: reconciling ${Y.hosts.length} host(s) on ${Y.zone}...`);let Q=new eJ(Y.apiToken,{zoneId:Y.zoneId,accountId:Y.accountId});try{let Z=await lJ({provider:Q,zone:Y.zone,hosts:Y.hosts,ipv4:$,ipv6:e1(J),proxied:Y.proxied,settings:Y.settings,cache:Y.cache,originGuard:Y.originGuard,purge:Y.purge,skipOriginProbe:Y.skipOriginProbe});for(let z of Z.records)B(` ✓ ${z.host} ${z.type} → ${z.content}${z.proxied?" (proxied)":" (DNS-only)"}`);for(let z of Z.settingsChanged)W(` ✓ ${z.id}: ${JSON.stringify(z.from)} → ${JSON.stringify(z.to)}`);if(Z.cacheRules>0)B(` ✓ ${Z.cacheRules} cache rule(s) applied`);if(Z.originGuard)B(" ✓ origin guard header applied");if(Z.purged)B(" ✓ edge cache purged");for(let z of Z.warnings)N(` ⚠ ${z}`)}catch(Z){N(`Cloudflare CDN: ${Z instanceof Error?Z.message:String(Z)}`)}}async function RX(G,$,J,Y,X,Q,Z=!1){let z=G.sites||{},F=$?[$]:Object.keys(z);if(F.length===0){N("No sites configured in cloud.config.ts");return}let U=null,M=!1,H=()=>{if(U||M)return U;let j=null;try{j=U$(J)}catch(V){K(V.message)}if(!j)return K(`DNS provider '${J}' is not configured. Please set the required environment variables.`),W(`
419
- For Cloudflare: CLOUDFLARE_API_TOKEN`),W("For Porkbun: PORKBUN_API_KEY, PORKBUN_SECRET_KEY"),W("For GoDaddy: GODADDY_API_KEY, GODADDY_API_SECRET"),M=!0,null;if(j.provider==="route53"){let V=G.infrastructure?.dns?.hostedZoneId;if(V&&!j.hostedZoneId)j.hostedZoneId=V}return U={config:j,provider:U0(j)},U};for(let j of F){let V=z[j];if(!V){K(`Site '${j}' not found in configuration`);continue}if(!S2(V)){let S=B1(V);W(`Site '${j}' is ${S} — handled outside the static bucket pipeline.`);continue}let T=H();if(!T)return;let A=V.domain;if(!A){K(`Site '${j}' has no domain configured`);continue}if(R(`Deploying Site: ${j}`),W(`Domain: ${A}`),W(`Source: ${V.root}`),W(`DNS Provider: ${J}`),V.build){y(`Running build command: ${V.build}`);try{let{execSync:S}=await import("node:child_process");S(V.build,{stdio:"inherit",cwd:process.cwd()}),B("Build completed successfully")}catch(S){K(`Build failed: ${S.message}`);continue}}if(!H$(V.root)){if(K(`Source directory not found: ${V.root}`),!V.build)W('Run your build command first (e.g., bun run generate) or add a "build" option to your site config');continue}y("Checking existing DNS records...");let q=await T.provider.listRecords(A);if(q.success&&q.records.length>0){let S=A.split("."),I=S.length>2?S[0]:"@",g=S.slice(-2).join("."),f=q.records.find((d)=>d.type==="CNAME"&&(d.name===A||d.name===I||d.name===`${I}.${g}`));if(f){let d=f.content.includes("netlify"),r=f.content.includes("vercel");if(f.content.includes("cloudfront.net"))W(`Domain already points to CloudFront: ${f.content}`),W("Proceeding with file upload...");else{let l=d?"Netlify":r?"Vercel":"another provider";if(N(`
420
- Existing CNAME record detected:`),W(` ${f.name} -> ${f.content}`),d||r)W(`
421
- This domain is currently pointing to ${l}.`),W("Deploying will update this record to point to AWS CloudFront.");if(!(Z||await k(`
422
- Update DNS record to point to AWS CloudFront?`,!0))){W("Deployment cancelled");continue}y(`Removing old ${l} CNAME record...`);let e=await T.provider.deleteRecord(A,{name:f.name,type:"CNAME",content:f.content});if(e.success)B(`Removed CNAME: ${f.name} -> ${f.content}`);else N(`Could not remove old record: ${e.message}`),W("The deployment will attempt to update it instead.")}}}let O=V.root,L,D=!!V.installScript;if(D){let{mkdtempSync:S,copyFileSync:I,readdirSync:g,statSync:f}=await import("node:fs"),{tmpdir:d}=await import("node:os"),{join:r,resolve:W$}=await import("node:path");L=S(r(d(),"cloud-install-"));let l=W$(V.installScript);I(l,r(L,"index.html"));let s=l.split("/").pop();if(s!=="index.html")I(l,r(L,s));if(H$(V.root)){let e=g(V.root);for(let C of e){let c=r(V.root,C);if(f(c).isFile()&&C!=="index.html")I(c,r(L,C))}}O=L,W(`Install script: ${V.installScript}`),W(`Serving at: curl -fsSL https://${A} | bash`)}y("Deploying to AWS (S3 + CloudFront)...");let P=x2(G,j,V,Q),b=N2(G,j);W(`Stack: ${P}`);let _=await o1({siteName:b,stackName:P,domain:A,region:Y,bucket:P2(G.project.slug,Q,j,V.bucket),sourceDir:O,certificateArn:V.certificateArn,dnsProvider:T.config,skipDnsVerification:X,passthroughUrls:D,dynamicApp:!!G.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginDomain:G.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginPort:G.infrastructure?.compute?.cloudFrontOriginPort??G.infrastructure?.api?.port??3008,computeOriginId:G.infrastructure?.compute?.cloudFrontOriginId??`${G.project.slug}-site-ec2`,onProgress:(S,I)=>{if(S==="infrastructure")y(I||"Setting up infrastructure...");else if(S==="upload"){if(I?.includes("/")&&!I.includes("1/")){let g=I.match(/(\d+)\/(\d+)/);if(g){let[,f,d]=g;if(Number(f)%10===0||f===d)W(` Uploaded ${f}/${d} files`)}}}else if(S==="invalidate")y("Invalidating CDN cache...")}});if(L){let{rmSync:S}=await import("node:fs");S(L,{recursive:!0,force:!0})}if(_.success){B(`
423
- Deployment successful!`);let S=_.filesSkipped>0?`${_.filesUploaded} uploaded, ${_.filesSkipped} unchanged`:`${_.filesUploaded}`,I=D?`
424
- Install: curl -fsSL https://${_.domain} | bash`:"";n$(`Site Deployed!
425
-
426
- Domain: https://${_.domain}
427
- CloudFront: ${_.distributionDomain}
428
- Bucket: ${_.bucket}
429
- Files: ${S}${I}
430
-
431
- Your site is now live at https://${_.domain}`,"green")}else throw Error(_.message||`Site '${j}' deployment failed`)}}import{execFileSync as LX}from"node:child_process";import{createHash as EX}from"node:crypto";import{existsSync as d2,mkdirSync as TX,mkdtempSync as OX,readdirSync as wX,readFileSync as DX,rmSync as xX,statSync as c2,writeFileSync as NX}from"node:fs";import{tmpdir as PX}from"node:os";import{basename as SX,join as u0,relative as _X,resolve as j5}from"node:path";var bX=new Set([".git","node_modules","dist","coverage"]);function u2(G,$=G){let J=[];for(let Y of wX(G).sort()){if(bX.has(Y))continue;let X=u0(G,Y);if(m2(X,$))continue;let Q=c2(X);if(Q.isDirectory())J.push(...u2(X,$));else if(Q.isFile())J.push(_X($,X))}return J}function IX(G){let $=j5(G);if(!d2($)||!c2($).isDirectory())throw Error(`Container context does not exist: ${$}`);let J=EX("sha256");for(let Y of u2($))J.update(Y),J.update("\x00"),J.update(DX(u0($,Y))),J.update("\x00");return J.digest("hex")}function kX(G){return{ecr:new V1(G.region||"us-east-1",G.profile),run:($,J,Y)=>{LX($,J,{cwd:Y?.cwd,env:Y?.env,stdio:Y?.stdio||"inherit"})}}}async function l2(G,$){let J=j5(G.context),Y=j5(G.dockerfile||u0(J,"Dockerfile"));if(!d2(Y))throw Error(`Dockerfile does not exist: ${Y}`);if(!G.repository.split("/").every((T)=>/^[a-z0-9][a-z0-9._-]*$/.test(T)&&!/[._-]$/.test(T)&&!/[._-]{2}/.test(T)))throw Error("ECR repository name is invalid");let Z=$||kX(G),z=IX(J),F=`sha-${z.slice(0,20)}`,U="";try{U=(await Z.ecr.describeRepositories({repositoryNames:[G.repository]})).repositories?.[0]?.repositoryUri||""}catch{}if(!U)U=(await Z.ecr.createRepository({repositoryName:G.repository,imageTagMutability:"IMMUTABLE",imageScanningConfiguration:{scanOnPush:!0},tags:[{Key:"ManagedBy",Value:"ts-cloud"}]})).repository?.repositoryUri||"";if(!U)throw Error(`Could not resolve ECR repository URI for ${G.repository}`);await Z.ecr.putLifecyclePolicy({repositoryName:G.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 M=`${U}:${F}`,H=OX(u0(PX(),"ts-cloud-container-"));try{Z.run("docker",["version"],{stdio:"ignore"}),Z.run("docker",["build","--platform",G.platform||"linux/amd64","--provenance=false","--sbom=false","--file",Y,"--tag",M,J],{cwd:J});let T=(await Z.ecr.getAuthorizationToken()).authorizationData?.[0];if(!T?.authorizationToken||!T.proxyEndpoint)throw Error("ECR did not return Docker authorization data");let A=T.proxyEndpoint.replace(/^https?:\/\//,""),q=u0(H,"docker");TX(q,{recursive:!0}),NX(u0(q,"config.json"),JSON.stringify({auths:{[A]:{auth:T.authorizationToken}}}),{mode:384}),Z.run("docker",["push",M],{env:{...process.env,DOCKER_CONFIG:q}})}finally{xX(H,{recursive:!0,force:!0})}let V=(await Z.ecr.describeImages({repositoryName:G.repository,imageIds:[{imageTag:F}]})).imageDetails?.[0]?.imageDigest;if(!V||!/^sha256:[a-f0-9]{64}$/i.test(V))throw Error(`ECR did not return an immutable digest for ${SX(M)}`);return{repository:G.repository,tag:F,imageUri:M,digest:V,digestUri:`${U}@${V}`,contextSha256:z}}import{createHash as hX}from"node:crypto";function yX(G){let $=G;return String($?.code||$?.name||$?.statusCode||"")}function CX(G){return yX(G)==="ValidationError"&&String(G.message).includes("does not exist")}function fX(G){return Object.fromEntries((G||[]).map(($)=>[$.OutputKey,$.OutputValue]))}function gX(G){let $=G?.Aliases?.Items??G?.Aliases;if(Array.isArray($))return $.map(String);if(typeof $==="string")return[$];return[]}function p2(G){if(!/^[a-z][a-z0-9-]{1,62}$/.test(G.slug))throw Error("Slug must be a lowercase DNS-safe identifier");if(!/^[A-Z0-9]{8,32}$/.test(G.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(G.expectedAlias))throw Error("Expected distribution alias is invalid");let $=G.imageUri.match(/@((?:sha256:)[a-f0-9]{64})$/i)?.[1];if(!$)throw Error("Container image must use an immutable ECR digest URI ending in @sha256:<64 hex>");let J=G.stackName||`${G.slug}-backend`;if(!/^[A-Za-z][A-Za-z0-9-]{0,127}$/.test(J))throw Error("CloudFormation stack name is invalid");let Y=G.pathPattern||"/api/*";if(!/^\/[A-Za-z0-9_.*?~!$&'()+,;=:@%/-]+\*$/.test(Y)||Y==="/*")throw Error("Backend path must be a non-default wildcard pattern");let X=G.originId||`${G.slug}-backend`;if(!/^[A-Za-z0-9_.-]{1,128}$/.test(X))throw Error("CloudFront origin ID is invalid");if(G.originVerifySecret&&G.originVerifySecret.length<24)throw Error("Origin verification secret must contain at least 24 characters");if(!!G.originDomain!==!!G.certificateArn)throw Error("A custom origin domain and matching ACM certificate ARN must be provided together");return{stackName:J,region:G.region||"us-east-1",pathPattern:Y,originId:X,digest:$}}function B5(G){let $=G.region||"us-east-1";return{cloudformation:new Y$($,G.profile),cloudfront:new $$(G.profile),sts:new P0($,G.profile),fetch,sleep:(J)=>new Promise((Y)=>setTimeout(Y,J))}}function vX(G){let $=p2(G),J=a2({name:G.name,slug:G.slug,domain:G.originDomain||G.expectedAlias,imageUri:G.imageUri,certificateArn:G.certificateArn,originVerifySecret:G.originVerifySecret,desiredCount:G.desiredCount,database:G.database,cache:G.cache,queue:G.queue}),Y=r2(J);return Y.Description=`Managed backend for existing static frontend ${G.expectedAlias}`,Y.Metadata={ManagedBy:"ts-cloud",ExistingDistributionId:G.distributionId,ExistingDistributionAlias:G.expectedAlias,BackendPathPattern:$.pathPattern,ContainerDigest:$.digest},Y}async function mX(G,$,J){let Y;for(let X=0;X<30;X++){let Q=Date.now();try{let Z=await G.fetch($,{redirect:"manual",headers:J?{"x-origin-verify":J}:void 0}),z=Date.now()-Q,F=await Z.text(),U=F;try{U=JSON.parse(F)}catch{}if(Z.ok)return{url:$,status:Z.status,latencyMs:z,body:U};Y=Error(`origin returned ${Z.status}`)}catch(Z){Y=Z}if(X<29)await G.sleep(1e4)}throw Error(`Backend origin did not become healthy: ${Y instanceof Error?Y.message:String(Y)}`)}async function n2(G,$){let J=p2(G),Y=$||B5({region:J.region,profile:G.profile}),[X,Q]=await Promise.all([Y.sts.getCallerIdentity(),Y.cloudfront.getDistribution(G.distributionId)]);if(!X.Account)throw Error("AWS identity did not return an account ID");if(!Q.Enabled)throw Error(`CloudFront distribution ${G.distributionId} is disabled`);if(!gX(Q).includes(G.expectedAlias))throw Error(`Distribution ${G.distributionId} does not contain expected alias ${G.expectedAlias}`);let Z;try{Z=(await Y.cloudformation.describeStacks({stackName:J.stackName})).Stacks[0]}catch(P){if(!CX(P))throw P}let z=vX(G),F=JSON.stringify(z);if(Buffer.byteLength(F)>51200)throw Error("Generated template exceeds the direct CloudFormation template body limit");let U=Object.values(z.Resources||{}).reduce((P,b)=>{return P[b.Type]=(P[b.Type]||0)+1,P},{}),M=[`cloud cdn:origin:remove ${G.distributionId} ${G.originDomain||"ALB_HOST"} --id ${J.originId} --path '${J.pathPattern}' --profile ${G.profile||"default"} --apply --confirm 'remove:${G.distributionId}:${J.pathPattern}'`,`After CloudFront is deployed, restore external DNS if changed and delete stack ${J.stackName} only with an approved data-retention decision.`],H={mode:G.apply?"apply":"plan",accountId:X.Account,stack:{name:J.stackName,existed:!!Z,status:Z?.StackStatus,templateSha256:hX("sha256").update(F).digest("hex"),resourceCount:Object.keys(z.Resources||{}).length,resourceTypes:U},distribution:{id:Q.Id,alias:G.expectedAlias,domainName:Q.DomainName,status:Q.Status,pathPattern:J.pathPattern,originId:J.originId},artifact:{imageUri:G.imageUri,digest:J.digest},services:{database:G.database!==!1,cache:G.cache!==!1,queue:G.queue!==!1,mail:"ses",desiredCount:G.desiredCount||1},cloudFrontApplied:!1,dnsApplied:!1,applied:!1,rollback:M};if(!G.apply)return H;let j=`${G.distributionId}:${J.pathPattern}:${J.stackName}`;if(G.confirm!==j)throw Error(`Pass exact confirmation token ${j} to provision the backend and change live routing`);if(Z)try{await Y.cloudformation.updateStack({stackName:J.stackName,templateBody:F,capabilities:["CAPABILITY_NAMED_IAM"],tags:[{Key:"ManagedBy",Value:"ts-cloud"},{Key:"Application",Value:G.slug}]}),await Y.cloudformation.waitForStack(J.stackName,"stack-update-complete")}catch(P){if(!String(P.message).includes("No updates are to be performed"))throw P}else await Y.cloudformation.createStack({stackName:J.stackName,templateBody:F,capabilities:["CAPABILITY_NAMED_IAM"],onFailure:"ROLLBACK",tags:[{Key:"ManagedBy",Value:"ts-cloud"},{Key:"Application",Value:G.slug}]}),await Y.cloudformation.waitForStack(J.stackName,"stack-create-complete");let V=(await Y.cloudformation.describeStacks({stackName:J.stackName})).Stacks[0],T=fX(V?.Outputs),A=T.AppLoadBalancerDnsName;if(!A)throw Error("Backend stack did not return AppLoadBalancerDnsName");let q=!1;if(G.originDomain&&G.originDomain!==A){if(!Y.dns)throw Error(`External DNS provider is required to map ${G.originDomain} to ${A}`);let P=await Y.dns.upsertRecord(G.originDomain,{name:G.originDomain,type:"CNAME",content:A,ttl:300});if(!P.success)throw Error(P.message||"External DNS update failed");q=!0}let O=G.originDomain||A,L=G.certificateArn?"https-only":"http-only",D=G.skipHealthCheck?void 0:await mX(Y,`${L==="https-only"?"https":"http"}://${O}/api/health`,G.originVerifySecret);return await Y.cloudfront.upsertExistingDistributionOrigin(G.distributionId,{id:J.originId,domainName:O,pathPattern:J.pathPattern,protocolPolicy:L,customHeaders:G.originVerifySecret?{"X-Origin-Verify":G.originVerifySecret}:void 0}),{...H,outputs:T,health:D,cloudFrontApplied:!0,dnsApplied:q,applied:!0}}function s2(G={}){let J={fargate:730*(G.desiredCount||1)*0.012342500000000001,applicationLoadBalancer:16.425,natGateway:32.85,postgres:730*(G.multiAzDatabase?0.032:0.016),redis:23.36};return{monthlyUsd:Number(Object.values(J).reduce((Y,X)=>Y+X,0).toFixed(2)),components:Object.fromEntries(Object.entries(J).map(([Y,X])=>[Y,Number(X.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 V5(G){G.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($,J)=>{try{let Y=await l2({repository:$,context:J.context||".",dockerfile:J.dockerfile,platform:J.platform,region:J.region,profile:J.profile});B("Immutable container artifact published."),W(JSON.stringify(Y,null,2))}catch(Y){K(Y instanceof Error?Y.message:String(Y)),process.exitCode=1}}),G.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($,J,Y)=>{try{if(!Y.slug)throw Error("--slug is required");if(!Y.image)throw Error("--image must be an immutable digest URI from container:artifact");let X=Y.originSecretEnv||"TS_CLOUD_ORIGIN_SECRET",Q=process.env[X];if(Y.apply&&!Q)throw Error(`${X} must contain a stable random CloudFront-to-ALB secret before apply`);let Z=B5({region:Y.region,profile:Y.profile}),z=Y.originDomain&&Y.apply?{...Z,dns:C$(Y.dnsProvider)}:Z,F=await n2({name:Y.name||Y.slug,slug:Y.slug,imageUri:Y.image,distributionId:$,expectedAlias:J,stackName:Y.stack,pathPattern:Y.path,originId:Y.originId,originDomain:Y.originDomain,certificateArn:Y.certificateArn,originVerifySecret:Q,desiredCount:Number(Y.desiredCount)||1,database:Y.database!==!1,cache:Y.cache!==!1,queue:Y.queue!==!1,region:Y.region,profile:Y.profile,skipHealthCheck:!!Y.skipHealthCheck,apply:!!Y.apply,confirm:Y.confirm},z);if(W(JSON.stringify(F,null,2)),F.applied)B("Full-stack backend is healthy and the CloudFront path patch was submitted.");else W(`Plan only. Apply with --confirm '${$}:${F.distribution.pathPattern}:${F.stack.name}'.`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}}),G.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(($)=>W(JSON.stringify(s2({desiredCount:Number($.desiredCount)||1,multiAzDatabase:!!$.multiAzDatabase}),null,2)))}function A5(G){G.command("destroy","Destroy the single-server compute (instance + firewall)").option("--env <env>","Environment",{default:"production"}).option("--force","Skip the confirmation prompt").action(async($)=>{R("Destroy Compute");let J=await x(),Y=$?.env||"production",X=c0(J),Q=c$({config:J,provider:X});if(!Q.destroyCompute){K(`The ${X} driver does not support compute teardown`);return}if(N(`This terminates the ${X} server for ${J.project.slug}/${Y} and deletes its firewall.`),!$?.force){if(!await k("This is irreversible. Continue?",!1)){W("Cancelled");return}}let Z=new E("Destroying compute...");Z.start();try{let{destroyed:z}=await Q.destroyCompute({config:J,environment:Y});if(Z.succeed("Compute destroyed"),z.length>0)z.forEach((F)=>W(` removed ${F}`));else W("Nothing to destroy (no matching resources found)")}catch(z){Z.fail("Teardown failed"),K(z instanceof Error?z.message:String(z))}})}import{statSync as dX,writeFileSync as cX}from"node:fs";function q5(G){G.command("stack:list","List all CloudFormation stacks").action(async()=>{R("CloudFormation Stacks");try{let J=(await x()).project.region||"us-east-1",Y=new Y$(J),X=new E("Loading stacks...");X.start();let Q=await Y.listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS"]);if(X.succeed(`Found ${Q.StackSummaries.length} stacks`),Q.StackSummaries.length===0){W("No stacks found");return}let Z=["Stack Name","Status","Created","Updated"],z=Q.StackSummaries.map((F)=>[F.StackName,F.StackStatus,new Date(F.CreationTime).toLocaleString(),F.LastUpdatedTime?new Date(F.LastUpdatedTime).toLocaleString():"Never"]);w(Z,z)}catch($){K(`Failed to list stacks: ${$.message}`)}}),G.command("stack:describe STACK_NAME","Describe a CloudFormation stack").action(async($)=>{R(`Stack: ${$}`);try{let Y=(await x()).project.region||"us-east-1",X=new Y$(Y),Q=new E("Loading stack details...");Q.start();let Z=await X.describeStacks({stackName:$});if(!Z.Stacks||Z.Stacks.length===0){Q.fail("Stack not found");return}let z=Z.Stacks[0];if(Q.succeed("Stack details loaded"),W(`
432
- Stack Information:`),W(` - Name: ${z.StackName}`),W(` - Status: ${z.StackStatus}`),W(` - Created: ${new Date(z.CreationTime).toLocaleString()}`),z.LastUpdatedTime)W(` - Updated: ${new Date(z.LastUpdatedTime).toLocaleString()}`);if(z.Parameters&&z.Parameters.length>0){W(`
433
- Parameters:`);for(let U of z.Parameters)W(` - ${U.ParameterKey}: ${U.ParameterValue}`)}if(z.Outputs&&z.Outputs.length>0){W(`
434
- Outputs:`);for(let U of z.Outputs)if(W(` - ${U.OutputKey}: ${U.OutputValue}`),U.Description)W(` ${U.Description}`)}if(z.Tags&&z.Tags.length>0){W(`
435
- Tags:`);for(let U of z.Tags)W(` - ${U.Key}: ${U.Value}`)}y(`
436
- Loading stack resources...`);let F=await X.listStackResources($);if(F.StackResourceSummaries.length>0){W(`
437
- Resources (${F.StackResourceSummaries.length}):`);let U=["Logical ID","Type","Status"],M=F.StackResourceSummaries.slice(0,10).map((H)=>[H.LogicalResourceId,H.ResourceType,H.ResourceStatus]);if(w(U,M),F.StackResourceSummaries.length>10)W(`
438
- ... and ${F.StackResourceSummaries.length-10} more resources`)}}catch(J){K(`Failed to describe stack: ${J.message}`)}}),G.command("stack:delete STACK_NAME","Delete a CloudFormation stack").option("--yes","Skip the confirmation prompt").action(async($,J)=>{R(`Delete Stack: ${$}`);try{let X=(await x()).project.region||"us-east-1";if(N("This will permanently delete the stack and all its resources!"),!(J?.yes||await k(`
439
- Are you sure you want to delete this stack?`,!1))){W("Deletion cancelled");return}let Z=new Y$(X),z=new E("Deleting stack...");z.start(),await Z.deleteStack($),z.succeed("Stack deletion initiated"),y("Waiting for stack deletion..."),await Z.waitForStack($,"stack-delete-complete"),B("Stack deleted successfully!")}catch(Y){K(`Failed to delete stack: ${Y.message}`)}}),G.command("stack:events STACK_NAME","Show stack events").option("--limit <number>","Limit number of events",{default:"20"}).action(async($,J)=>{R(`Stack Events: ${$}`);try{let X=(await x()).project.region||"us-east-1",Q=new Y$(X),Z=new E("Loading events...");Z.start();let z=await Q.describeStackEvents($);Z.succeed(`Found ${z.StackEvents.length} events`);let F=J?.limit?Number.parseInt(J.limit):20,U=z.StackEvents.slice(0,F);if(U.length===0){W("No events found");return}let M=["Time","Resource","Status","Reason"],H=U.map((j)=>[new Date(j.Timestamp).toLocaleString(),j.LogicalResourceId,j.ResourceStatus,j.ResourceStatusReason||""]);w(M,H)}catch(Y){K(`Failed to load events: ${Y.message}`)}}),G.command("stack:outputs STACK_NAME","Show stack outputs").action(async($)=>{R(`Stack Outputs: ${$}`);try{let Y=(await x()).project.region||"us-east-1",X=new Y$(Y),Q=new E("Loading stack outputs...");Q.start();let Z=await X.describeStacks({stackName:$});if(!Z.Stacks||Z.Stacks.length===0){Q.fail("Stack not found");return}let z=Z.Stacks[0];if(Q.succeed("Stack outputs loaded"),!z.Outputs||z.Outputs.length===0){W("No outputs found for this stack");return}let F=["Key","Value","Description","Export Name"],U=z.Outputs.map((M)=>[M.OutputKey||"",M.OutputValue||"",M.Description||"",M.ExportName||""]);w(F,U),W(`
440
- Copy-friendly format:`);for(let M of z.Outputs)W(`${M.OutputKey}=${M.OutputValue}`)}catch(J){K(`Failed to load outputs: ${J.message}`)}}),G.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($,J)=>{R(`Export Stack: ${$}`);try{let X=(await x()).project.region||"us-east-1",Q=new Y$(X),Z=new E("Fetching stack template...");Z.start();let z=await Q.getTemplate($);if(!z.TemplateBody){Z.fail("Template not found");return}Z.succeed("Template fetched");let F=J?.format||"json",U=z.TemplateBody;if(F==="json"){let M=JSON.parse(U);U=JSON.stringify(M,null,2)}if(J?.output){let M=J.output;cX(M,U,"utf-8"),B(`Template exported to: ${M}`);let j=(dX(M).size/1024).toFixed(2);W(`File size: ${j} KB`)}else W(`
441
- Template:`),console.log(U)}catch(Y){K(`Failed to export template: ${Y.message}`)}})}import{existsSync as uX}from"node:fs";function R5(G,$){G.command("upgrade","Upgrade CLI to latest version").action(async()=>{V$("upgrade",{message:`Self-update is disabled for this ${$} build because no signed update-manifest source is configured.`,nextAction:"Install a verified release with your package manager or configure platform maintenance updates."})}),G.command("doctor","Check system requirements and AWS credentials").action(async()=>{if(R("System Diagnostics"),y("Checking Bun..."),B(`Bun ${process.versions.bun}`),y("Checking AWS CLI..."),await i2())B("AWS CLI is installed");else K("AWS CLI is not installed"),W("Install: https://aws.amazon.com/cli/");if(y("Checking AWS credentials..."),await t2()){B("AWS credentials are configured");let Q=await e2();if(Q)W(`Account ID: ${Q}`)}else K("AWS credentials are not configured"),W("Run: aws configure");y("Checking CloudFront list access...");let X=!1;try{let{CloudFrontClient:Q}=await import("./chunk-e3xrwh9z.js");await new Q().listDistributions(),B("CloudFront list access is enabled"),X=!0}catch(Q){N(`CloudFront list check failed: ${Q.message}`)}if(X){y("Checking CloudFront create access...");try{let Q=new Y$("us-east-1"),Z=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 Q.validateTemplate(Z),B("CloudFront create access appears enabled")}catch(Q){if(Q.message?.includes("403")||Q.message?.includes("AccessDenied")||Q.message?.includes("must be verified"))K("CloudFront create access denied - account verification required"),W(""),W("Your AWS account needs to be verified for CloudFront."),W("This is required before you can create CloudFront distributions."),W(""),W("To verify your account:"),W(" 1. Go to: https://console.aws.amazon.com/support/home#/"),W(" 2. Create a support case"),W(" 3. Select: Service limit increase > CloudFront"),W(' 4. Request: "Please verify my account for CloudFront access"'),W(""),W("Verification usually takes 1-2 business days.");else B("CloudFront create access appears enabled (validation passed)")}}y("Checking S3 access...");try{let{S3Client:Q}=await import("./chunk-598yf4bs.js");await new Q("us-east-1").listBuckets(),B("S3 access is enabled")}catch(Q){N(`S3 check failed: ${Q.message}`)}y("Checking ACM (SSL certificates) access...");try{await new B0("us-east-1").listCertificates(),B("ACM access is enabled")}catch(Q){N(`ACM check failed: ${Q.message}`)}if(y("Checking configuration..."),uX("cloud.config.ts"))B("cloud.config.ts found");else N("cloud.config.ts not found"),W("Run: cloud init")}),G.command("regions","List available AWS regions").action(async()=>{R("AWS Regions");let J=new E("Fetching regions...");J.start();let Y=await o2();J.stop(),Y.forEach((X)=>{console.log(` ${X}`)})}),G.command("version","Show the version of the CLI").alias("v").action(()=>{console.log(`ts-cloud v${$}`)})}function L5(G){G.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($)=>{R("Analytics Sites");try{let J=new S$($.region),Y=new E("Fetching sites...");Y.start();let X=await J.scan({TableName:$.table,FilterExpression:"begins_with(pk, :pk)",ExpressionAttributeValues:{":pk":{S:"SITE#"}}});if(Y.succeed(`Found ${X.Items?.length||0} site(s)`),!X.Items||X.Items.length===0){W("No analytics sites found"),W("Use `cloud analytics:sites:create` to create a new site");return}let Q=X.Items.map((Z)=>S$.unmarshal(Z));w(["ID","Name","Domains","Active","Created"],Q.map((Z)=>[Z.siteId||"N/A",Z.name||"Unnamed",(Z.domains||[]).join(", ")||"-",Z.isActive?"Yes":"No",Z.createdAt?new Date(Z.createdAt).toLocaleDateString():"N/A"]))}catch(J){K(`Failed to list sites: ${J.message}`),process.exit(1)}}),G.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($)=>{R("Create Analytics Site");try{let J=new S$($.region),Y=$.name||await p("Site name","My Site"),X=[];if($.domain)X=Array.isArray($.domain)?$.domain:[$.domain];else X=(await p("Site domains (comma-separated)","example.com")).split(",").map((M)=>M.trim()).filter(Boolean);if(W(`
442
- Creating site: ${Y}`),W(`Domains: ${X.join(", ")}`),!await k(`
443
- Create this site?`,!0)){W("Operation cancelled");return}let Z=new E("Creating site...");Z.start();let z=`site_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,F=new Date().toISOString();await J.putItem({TableName:$.table,Item:{pk:{S:`SITE#${z}`},sk:{S:`SITE#${z}`},siteId:{S:z},name:{S:Y},domains:{L:X.map((U)=>({S:U}))},isActive:{BOOL:!0},createdAt:{S:F},updatedAt:{S:F}}}),Z.succeed("Site created successfully"),B(`
444
- Site ID: ${z}`),W(`
445
- Add the tracking script to your website:`),W(` <script src="https://analytics.stacksjs.com/track.js" data-site-id="${z}"></script>`)}catch(J){K(`Failed to create site: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("Update Analytics Site");try{let Y=new S$(J.region),X=await Y.getItem({TableName:J.table,Key:{pk:{S:`SITE#${$}`},sk:{S:`SITE#${$}`}}});if(!X.Item)K(`Site not found: ${$}`),process.exit(1);let Q=S$.unmarshal(X.Item);W(`Updating site: ${Q.name||"Unnamed"} (${$})`),W("");let Z=[],z={},F={};if(J.name)Z.push("#n = :name"),z["#n"]="name",F[":name"]={S:J.name},W(` Name: ${Q.name} -> ${J.name}`);if(J.domain!==void 0){let U=Array.isArray(J.domain)?J.domain:[J.domain];Z.push("domains = :domains"),F[":domains"]={L:U.map((M)=>({S:M}))},W(` Domains: ${JSON.stringify(Q.domains||[])} -> ${JSON.stringify(U)}`)}if(J.active!==void 0){let U=J.active==="true"||J.active==="1";Z.push("isActive = :active"),F[":active"]={BOOL:U},W(` Active: ${Q.isActive} -> ${U}`)}if(Z.length===0){N("No updates specified. Use --name, --domain, or --active options.");return}Z.push("updatedAt = :updatedAt"),F[":updatedAt"]={S:new Date().toISOString()},await Y.updateItem({TableName:J.table,Key:{pk:{S:`SITE#${$}`},sk:{S:`SITE#${$}`}},UpdateExpression:`SET ${Z.join(", ")}`,ExpressionAttributeNames:Object.keys(z).length>0?z:void 0,ExpressionAttributeValues:F}),W(""),B("Site updated successfully")}catch(Y){K(`Failed to update site: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R(`Analytics Data: ${$}`);try{let Y=new S$(J.region),X=new E("Querying data...");X.start();let Q=await Y.query({TableName:J.table,KeyConditionExpression:"pk = :pk",ExpressionAttributeValues:{":pk":{S:`SITE#${$}`}},ScanIndexForward:!1,Limit:parseInt(J.limit,10)});if(X.succeed(`Found ${Q.Items?.length||0} items`),!Q.Items||Q.Items.length===0){W("No data found for this site");return}let Z=Q.Items.map((z)=>S$.unmarshal(z));for(let z of Z){if(W(""),W(`SK: ${z.sk}`),z.path)W(` Path: ${z.path}`);if(z.timestamp)W(` Time: ${z.timestamp}`);if(z.visitorId)W(` Visitor: ${z.visitorId}`);if(z.browser)W(` Browser: ${z.browser}`);if(z.country)W(` Country: ${z.country}`)}}catch(Y){K(`Failed to query data: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R(`Realtime: ${$}`);try{let Y=new S$(J.region),X=new E("Checking realtime...");X.start();let Q=parseInt(J.minutes,10),Z=new Date(Date.now()-Q*60*1000),z=await Y.query({TableName:J.table,KeyConditionExpression:"pk = :pk AND sk BETWEEN :start AND :end",ExpressionAttributeValues:{":pk":{S:`SITE#${$}`},":start":{S:`PAGEVIEW#${Z.toISOString()}`},":end":{S:"PAGEVIEW#Z"}},ScanIndexForward:!1});if(X.succeed(`Found ${z.Items?.length||0} pageviews in last ${Q} minutes`),!z.Items||z.Items.length===0){N("No recent pageviews found"),W(""),W("Possible issues:"),W(" 1. Tracking script not installed correctly"),W(" 2. Site ID mismatch"),W(" 3. CORS or network issues");return}let F=z.Items.map((H)=>S$.unmarshal(H)),U=new Set(F.map((H)=>H.visitorId)).size;W(""),B(`${U} unique visitor(s) online`),W("");let M={};for(let H of F){let j=H.path||"/";M[j]=(M[j]||0)+1}W("Active pages:");for(let[H,j]of Object.entries(M).sort((V,T)=>T[1]-V[1]).slice(0,5))W(` ${H}: ${j} view(s)`);W(""),W("Recent pageviews:");for(let H of F.slice(0,5))W(` ${H.timestamp} - ${H.path} (${H.browser||"Unknown"})`)}catch(Y){K(`Failed to check realtime: ${Y.message}`),process.exit(1)}})}import{chmodSync as lX,mkdirSync as rX,readFileSync as aX,writeFileSync as pX}from"node:fs";import{dirname as nX,resolve as X6}from"node:path";function n(G){return new YY(G?{path:X6(G)}:{})}function sX(G){let $=G.health();R("Control-plane diagnostics"),W(`Database: ${$.path}`),W(`Schema: ${$.schemaVersion}/${$.supportedSchemaVersion}`),W(`Integrity: ${$.integrity}`),W(`Journal: ${$.journalMode}`),W(`Size: ${$.databaseBytes.toLocaleString()} bytes`),W(`Last backup: ${$.lastBackupAt??"never"}`),W(`Operations: ${Object.entries($.operations).map(([J,Y])=>`${J}=${Y}`).join(", ")}`),W(`Pending/retryable: ${$.pendingRetryableOperations}`)}function G0(G,$){let J=G.getOrganization($)??G.getOrganizationBySlug($);if(!J)throw Error(`Organization '${$}' was not found.`);return J}function $6(G,$){let J=G??"organization";if(J==="organization")return{type:"organization"};if(!["project","environment","resource"].includes(J)||!$)throw Error("Scoped access requires --scope project|environment|resource and --scope-id <id>.");return{type:J,id:$}}function J6(G,$){let J=$.includes("@")?G.getIdentityByEmail($):G.getIdentityByUsername($);if(!J)throw Error(`Authentication identity '${$}' was not found.`);return J}function Y6(G,$){return new S0(G,{encryptionKey:Q$(X6($??process.cwd()))})}function $Y(G,$="TS_CLOUD_API_TOKEN"){let J=process.env[$]?.trim();if(!J)throw Error(`Set ${$} to a one-time-issued API token.`);return new GY({baseUrl:G??process.env.TS_CLOUD_API_URL??"http://127.0.0.1:7676",token:J})}function E5(G){G.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($)=>{let J=await Q6({host:$?.host,port:Number($?.port??7676),environment:$?.env,box:$?.box,verbose:$?.verbose});R("ts-cloud Local Dashboard"),B(`Serving ${J.url}`),W("Use Ctrl+C to stop."),await new Promise((Y)=>{let X=()=>{J.server.stop(!0),Y()};process.once("SIGINT",X),process.once("SIGTERM",X)})}),G.command("control-plane:status","Inspect local control-plane storage health").option("--path <path>","Use a non-default control-plane database").action(($)=>{let J=n($?.path);try{sX(J)}finally{J.close()}}),G.command("control-plane:backup","Create a consistent local control-plane backup").option("--path <path>","Use a non-default control-plane database").action(($)=>{let J=n($?.path);try{let Y=J.createBackup("cli");B(`Backup written to ${Y}`)}finally{J.close()}}),G.command("control-plane:export <file>","Export portable control-plane metadata and history").option("--path <path>","Use a non-default control-plane database").action(($,J)=>{let Y=X6($),X=n(J?.path);try{rX(nX(Y),{recursive:!0}),pX(Y,`${JSON.stringify(X.exportSnapshot(),null,2)}
446
- `,{mode:384}),lX(Y,384),B(`Control-plane snapshot exported to ${Y}`)}finally{X.close()}}),G.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(($,J)=>{let Y=X6($),X=JSON.parse(aX(Y,"utf8")),Q=n(J?.path);try{let Z=J?.replace?Q.createBackup("pre-import"):void 0;if(Q.importSnapshot(X,{replace:J?.replace}),B(`Imported control-plane snapshot from ${Y}`),Z)W(`Previous state backed up to ${Z}`)}finally{Q.close()}}),G.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(($)=>{let J=n($?.path);try{let Y=J.compact({eventRetentionDays:Number($?.eventDays??90),operationRetentionDays:Number($?.operationDays??365),vacuum:$?.vacuum!==!1});if(B(`Removed ${Y.deletedEvents} event(s) and ${Y.deletedOperations} terminal operation(s).`),Y.vacuumed)W("Database file compacted.")}finally{J.close()}}),G.command("organization:list","List organizations in the local control plane").option("--path <path>","Use a non-default control-plane database").action(($)=>{let J=n($?.path);try{R("Organizations");for(let Y of J.listOrganizations())W(`${Y.slug} ${Y.name} ${Y.id}`)}finally{J.close()}}),G.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(($,J)=>{let Y=n(J?.path);try{let X=G0(Y,$);R(`${X.name} members`);for(let Q of Y.listMemberships(X.id,{includeRevoked:J?.all})){let Z=Y.getActor(Q.actorId);W(`${Z?.displayName??Q.actorId} ${Q.roleTemplate} ${Q.scope.type}:${Q.scope.id??X.slug} ${Q.status} ${Q.id}`);for(let z of Y.listGrants(Q.id))W(` ${z.effect} ${z.capability} @ ${z.scope.type}:${z.scope.id??X.slug}`)}}finally{Y.close()}}),G.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(($,J,Y)=>{let X=n(Y?.path);try{let Q=G0(X,$);if(!["owner","admin","deployer","operator","viewer","auditor"].includes(Y?.role??"viewer"))throw Error("Unknown role template.");let Z=X.createInvitation({organizationId:Q.id,email:J,roleTemplate:Y?.role??"viewer",scope:$6(Y?.scope,Y?.scopeId),expiresInMs:Number(Y?.days??7)*86400000});B(`Invitation created for ${Z.invitation.email}; expires ${Z.invitation.expiresAt}`),W(`${String(Y?.baseUrl??"http://127.0.0.1:7676").replace(/\/$/,"")}/accept-invitation?token=${encodeURIComponent(Z.token)}`),W("This token is shown once. Resending revokes it and creates a new token.")}finally{X.close()}}),G.command("organization:invitations <organization>","List invitation states without exposing tokens").option("--path <path>","Use a non-default control-plane database").action(($,J)=>{let Y=n(J?.path);try{let X=G0(Y,$);R(`${X.name} invitations`);for(let Q of Y.listInvitations(X.id))W(`${Q.email} ${Q.roleTemplate} ${Q.scope.type}:${Q.scope.id??X.slug} ${Q.state} ${Q.id}`)}finally{Y.close()}}),G.command("organization:revoke-invitation <id>","Revoke a pending organization invitation").option("--path <path>","Use a non-default control-plane database").action(($,J)=>{let Y=n(J?.path);try{let X=Y.revokeInvitation($);B(`Invitation ${X.id} is ${X.state}.`)}finally{Y.close()}}),G.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(($,J,Y,X)=>{let Q=n(X?.path);try{let Z=G0(Q,$);if(!G6.includes(Y))throw Error(`Unknown capability '${Y}'.`);if(X?.effect!==void 0&&!["allow","deny"].includes(X.effect))throw Error("Effect must be allow or deny.");let z=Q.upsertGrant({organizationId:Z.id,membershipId:J,effect:X?.effect??"allow",capability:Y,scope:$6(X?.scope,X?.scopeId)});B(`${z.effect} ${z.capability} added at ${z.scope.type}:${z.scope.id??Z.slug}.`)}finally{Q.close()}}),G.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(($,J)=>{if(J?.confirm!==$)throw Error(`Pass --confirm ${$} to revoke this membership.`);let Y=n(J?.path);try{let X=Y.revokeMembership($);B(`Membership ${X.id} revoked; session version is now ${X.sessionVersion}.`)}finally{Y.close()}}),G.command("auth:identities","List local authentication identities without credential material").option("--path <path>","Use a non-default control-plane database").action(($)=>{let J=n($?.path);try{let Y=new S0(J);R("Authentication identities");for(let X of Y.listIdentities())W(`${X.username} ${X.email??"no-email"} ${X.disabledAt?"disabled":"active"} ${X.id}`)}finally{J.close()}}),G.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(($,J)=>{let Y=n(J?.path);try{let X=new S0(Y),Q=J6(X,$);X.revokeActionTokens(Q.id,"password_reset");let Z=X.createActionToken(Q.id,"password_reset");B(`Created a one-time reset link for ${Q.username}; it expires ${Z.actionToken.expiresAt}.`),W(`${String(J?.baseUrl??"http://127.0.0.1:7676").replace(/\/$/,"")}/reset-password?token=${encodeURIComponent(Z.token)}`)}finally{Y.close()}}),G.command("auth:sessions <identity>","List active and expired sessions for an identity").option("--path <path>","Use a non-default control-plane database").action(($,J)=>{let Y=n(J?.path);try{let X=new S0(Y),Q=J6(X,$);R(`${Q.username} sessions`);for(let Z of X.listSessions(Q.id,{includeInactive:!0}))W(`${Z.state} ${Z.authMethod} ${Z.userAgent??"unknown-device"} ${Z.lastUsedAt} ${Z.id}`)}finally{Y.close()}}),G.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(($,J)=>{if(J?.confirm!==$)throw Error(`Pass --confirm ${$} to revoke these sessions.`);let Y=n(J?.path);try{let X=new S0(Y),Q=J6(X,$),Z=X.listSessions(Q.id);for(let z of Z)X.revokeSession(Q.id,z.id);B(`Revoked ${Z.length} session(s) for ${Q.username}.`)}finally{Y.close()}}),G.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(($,J)=>{if(J?.confirm!==$)throw Error(`Pass --confirm ${$} to disable MFA.`);let Y=n(J?.path);try{let X=new S0(Y),Q=J6(X,$);X.disableMfa(Q.id);for(let Z of X.listSessions(Q.id))X.revokeSession(Q.id,Z.id);B(`Disabled MFA and revoked active sessions for ${Q.username}.`)}finally{Y.close()}}),G.command("auth:oidc:list <organization>","List OIDC providers without credential material").option("--path <path>","Use a non-default control-plane database").action(($,J)=>{let Y=n(J?.path);try{let X=G0(Y,$),Q=Y6(Y);R(`${X.name} OIDC providers`);let Z=Q.listOidcProviders(X.id,{includeDisabled:!0});if(Z.length===0)W("No providers configured.");for(let z of Z)W(`${z.enabled?"enabled":"disabled"} ${z.slug} ${z.issuer} role=${z.defaultRole} enforce=${z.enforceSso}`)}finally{Y.close()}}),G.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(($,J,Y)=>{let X=n(Y?.path);try{let Q=G0(X,$),Z=Y6(X,Y?.authRoot),z=Z.getOidcProviderBySlug(J),F=Y?.secretEnv??"TS_CLOUD_OIDC_CLIENT_SECRET",U=process.env[F]?.trim();if(!z&&!U)throw Error(`Set ${F} to the client secret before creating this provider.`);let M=(Y?.domains??z?.allowedDomains.join(",")??"").split(",").map((V)=>V.trim()).filter(Boolean),H=Y?.defaultRole??z?.defaultRole??"viewer";if(!["admin","deployer","operator","viewer","auditor"].includes(H))throw Error("Default role must be admin, deployer, operator, viewer, or auditor.");let j=Z.upsertOidcProvider({id:z?.id,organizationId:Q.id,slug:J,name:Y?.name??z?.name??J,issuer:Y?.issuer??z?.issuer??"",clientId:Y?.clientId??z?.clientId??"",clientSecret:U,allowedDomains:M,scopes:(Y?.scopes??z?.scopes.join(",")??"email,profile").split(",").map((V)=>V.trim()).filter(Boolean),defaultRole:H,enforceSso:Y?.enforceSso??z?.enforceSso??!1,enabled:Y?.disabled?!1:z?.enabled??!0});if(B(`${j.name} saved as ${j.enabled?"enabled":"disabled"} with encrypted credentials.`),W(`Callback path: /auth/oidc/${j.slug}/callback`),j.enforceSso)W(`Offline recovery: cloud auth:oidc:disable ${j.slug} --confirm ${j.slug}`)}finally{X.close()}}),G.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($,J)=>{let Y=n(J?.path);try{let Q=Y6(Y,J?.authRoot).getOidcProviderBySlug($);if(!Q)throw Error(`OIDC provider '${$}' was not found.`);let Z=await JY(Q);B(`${Q.name} discovery is valid and issuer-bound.`),W(`Authorization: ${Z.authorization_endpoint}`),W(`Token: ${Z.token_endpoint}`),W(`JWKS: ${Z.jwks_uri}`)}finally{Y.close()}}),G.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(($,J)=>{if(J?.confirm!==$)throw Error(`Pass --confirm ${$} to disable this provider.`);let Y=n(J?.path);try{let X=Y6(Y,J?.authRoot),Q=X.getOidcProviderBySlug($);if(!Q)throw Error(`OIDC provider '${$}' was not found.`);X.setOidcProviderEnabled(Q.id,!1),B(`Disabled ${Q.name}, cancelled pending sign-ins, and removed SSO enforcement.`)}finally{Y.close()}}),G.command("api:service-accounts <organization>","List service accounts and safe token metadata").option("--path <path>","Use a non-default control-plane database").action(($,J)=>{let Y=n(J?.path);try{let X=G0(Y,$),Q=new l0(Y);R(`${X.name} service accounts`);for(let Z of Q.listServiceAccounts(X.id,{includeDisabled:!0})){W(`${Z.state} ${Z.slug} ${Z.name} ${Z.id}`);for(let z of Q.listTokens(Z.id,{includeInactive:!0}))W(` ${z.state} ${z.prefix} ${z.name} expires=${z.expiresAt} last-used=${z.lastUsedAt??"never"}`)}}finally{Y.close()}}),G.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(($,J,Y)=>{let X=n(Y?.path);try{let Q=G0(X,$),Z=Y?.role??"deployer";if(!["admin","deployer","operator","viewer","auditor"].includes(Z))throw Error("Service-account role cannot be owner and must be a supported role.");let F=new l0(X).createServiceAccount({organizationId:Q.id,slug:J,name:Y?.name??J,roleTemplate:Z,scope:$6(Y?.scope,Y?.scopeId)});B(`Created ${F.serviceAccount.name} (${F.serviceAccount.id}) with ${F.membership.roleTemplate} access.`)}finally{X.close()}}),G.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(($,J,Y)=>{let X=n(Y?.path);try{let Q=G0(X,$),Z=new l0(X),z=Z.getServiceAccount(J)??Z.getServiceAccountBySlug(Q.id,J);if(!z||z.organizationId!==Q.id)throw Error(`Service account '${J}' was not found.`);let F=(Y?.capabilities??"").split(",").map((H)=>H.trim()).filter((H)=>G6.includes(H));if(!F.length)throw Error(`Pass --capabilities with one or more of: ${G6.join(", ")}`);let U=Math.min(365,Math.max(1,Number(Y?.expiresDays??90))),M=Z.createToken({serviceAccountId:z.id,name:Y?.name??"CLI token",capabilities:F,scope:$6(Y?.scope,Y?.scopeId),expiresAt:new Date(Date.now()+U*24*60*60*1000).toISOString()});B(`Created ${M.token.prefix}; copy this secret now. It will not be shown again.`),W(M.secret)}finally{X.close()}}),G.command("api:token:rotate <token>","Create an overlapping replacement for an API token").option("--path <path>","Use a non-default control-plane database").action(($,J)=>{let Y=n(J?.path);try{let X=new l0(Y).rotateToken($);B(`Created overlapping replacement ${X.token.prefix}; revoke the old token after rollout.`),W(X.secret)}finally{Y.close()}}),G.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(($,J)=>{if(J?.confirm!==$)throw Error(`Pass --confirm ${$} to revoke this token.`);let Y=n(J?.path);try{let X=new l0(Y).revokeToken($);B(`Revoked ${X.prefix}; bearer requests stop immediately.`)}finally{Y.close()}}),G.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($)=>{let J=await $Y($?.baseUrl,$?.tokenEnv).listProjects();for(let Y of J.data)W(`${Y.slug} ${Y.name} ${Y.id}`)}),G.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($,J,Y)=>{let X=Y?.idempotencyKey??`cli-${Date.now()}-${crypto.randomUUID().slice(0,8)}`,Q=await $Y(Y?.baseUrl,Y?.tokenEnv).createDeployment({projectId:$,environmentId:J,serviceId:Y?.service,revision:Y?.revision},X);B(`${Q.idempotentReplay?"Replayed":"Queued"} operation ${Q.operation.id} (${Q.operation.state}).`)})}import{createHash as iX}from"node:crypto";var tX=`let coldStart = true
418
+ https://${c}.s3.${n}.amazonaws.com${d?`/${d}`:""}/index.html`,"green")}catch(t){p(`Deployment failed: ${t.message}`),process.exitCode=1}}),r.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(e)=>{w("Deploying Container");try{let t=await P(),n=t.project.region||"us-east-1",i=e?.cluster,o=e?.service,s=e?.repository,c=e?.image||"latest",l=e?.dockerfile||"Dockerfile",d=e?.context||".",u=e?.force||!1,m=e?.wait||!1,f=new Date().toISOString(),h=crypto.randomUUID();if(!i||!o){p("--cluster and --service are required");return}if(!s){p("--repository is required");return}if(!ge(l)){p(`Dockerfile not found: ${l}`);return}let S;if(!e?.skipSecurityScan){M("Scanning build context for leaked secrets...");let{result:X}=await ar({sourceDir:d,failOnSeverity:e?.securityFailOn||"critical"});cr(X),S=X}else I(`Security scan skipped (--skip-security-scan)
419
+ `);let y=uo(t,void 0,S);if(!y.allowed){p(`
420
+ ✗ Container deployment blocked by security policy: ${y.explanation}`),a("Remediate the findings or create an authorized, time-limited waiver in the Security posture center."),process.exitCode=1;return}if(g(`✓ Security policy allows this deployment
421
+ `),a(`Cluster: ${i}`),a(`Service: ${o}`),a(`Repository: ${s}`),a(`Image Tag: ${c}`),a(`Dockerfile: ${l}`),!await O(`
422
+ Deploy container now?`,!0)){a("Deployment cancelled");return}let R=new Rn(n),b=new $o(n),A=new C("Getting ECR credentials...");A.start();let k=await R.getAuthorizationToken();if(!k.authorizationData?.[0]){A.fail("Failed to get ECR credentials");return}let T=k.authorizationData[0],E=(T.proxyEndpoint||"").replace("https://","");A.succeed("ECR credentials obtained");let N=new C("Logging into ECR...");N.start();let _=T.authorizationToken||"",V=Buffer.from(_,"base64").toString("utf8").split(":")[1],{spawn:K}=await import("child_process"),oe=K("docker",["login","--username","AWS","--password-stdin",E],{stdio:["pipe","pipe","pipe"]});oe.stdin.write(V),oe.stdin.end(),await new Promise((X,ke)=>{oe.on("close",(Me)=>{if(Me===0)X();else ke(Error(`Docker login failed with code ${Me}`))})}),N.succeed("Logged into ECR");let G=new C("Building Docker image...");G.start();let L=`${E}/${s}:${c}`,H=K("docker",["build","-t",L,"-f",l,d],{stdio:["pipe","pipe","pipe"]});await new Promise((X,ke)=>{let Me="";H.stderr.on("data",(Ct)=>{Me+=Ct.toString()}),H.on("close",(Ct)=>{if(Ct===0)X();else ke(Error(`Docker build failed: ${Me}`))})}),G.succeed("Docker image built");let j=new C("Pushing image to ECR...");j.start();let W=K("docker",["push",L],{stdio:["pipe","pipe","pipe"]});await new Promise((X,ke)=>{let Me="";W.stderr.on("data",(Ct)=>{Me+=Ct.toString()}),W.on("close",(Ct)=>{if(Ct===0)X();else ke(Error(`Docker push failed: ${Me}`))})}),j.succeed("Image pushed to ECR");let ue,ae=K("docker",["image","inspect","--format={{.Id}}",L],{stdio:["ignore","pipe","pipe"]}),[Fe,jt]=await Promise.all([new Promise((X)=>ae.on("close",X)),new Response(ae.stdout).text()]);if(Fe===0){let X=jt.trim();if(/^sha256:[a-f0-9]{64}$/i.test(X))ue=X}let ye=await Qm(t,{imageRef:L,imageSha256:ue,artifactRoot:d,invocationId:h,startedAt:f});if(!ye.allowed){p(`
423
+ ✗ ECS service update blocked by release security policy: ${ye.explanation}`),a("The image remains in ECR for investigation. Remediate and rebuild it, or create an authorized, time-limited waiver."),process.exitCode=1;return}g(`✓ Release security policy allows the ECS service update
424
+ `);let Y=new C("Updating ECS service...");if(Y.start(),await b.updateService({cluster:i,service:o,forceNewDeployment:u}),Y.succeed("ECS service updated"),m){let X=new C("Waiting for deployment to stabilize...");X.start(),await b.waitForServiceStable(i,o),X.succeed("Deployment stabilized")}g(`
425
+ Container deployment complete!`),a(`
426
+ Image: ${L}`),a(`Cluster: ${i}`),a(`Service: ${o}`)}catch(t){p(`Deployment failed: ${t.message}`),process.exitCode=1}})}async function ep(r){let e=process.cwd(),t=`${e}/.env`,n=`${e}/.env.local`,s=({stage:["stage","staging"],staging:["staging","stage"]}[r]||[r]).map((m)=>`${e}/.env.${m}`).find((m)=>ge(m));if(!s){if(ge(t))I(`No .env.${r} found, falling back to .env`);else a(`No .env.${r} or .env file found; using the existing process environment`);return null}let c=s.split("/").pop();M(`Loading environment file: ${c}`);let l=null,d=null;if(ge(n)){l=`${n}.bak`,Cn(n,l);let m=fo(n,"utf-8");for(let h of m.split(`
427
+ `)){let S=h.trim();if(!S||S.startsWith("#"))continue;let y=S.indexOf("=");if(y===-1)continue;let v=S.slice(0,y).trim();delete process.env[v]}let{unlinkSync:f}=await import("node:fs");f(n),a("Temporarily moved .env.local out of the way and purged its values from process.env")}if(ge(t))d=`${t}.bak`,Cn(t,d);Cn(s,t);let u=fo(s,"utf-8");for(let m of u.split(`
428
+ `)){let f=m.trim();if(!f||f.startsWith("#"))continue;let h=f.indexOf("=");if(h===-1)continue;let S=f.slice(0,h).trim(),y=f.slice(h+1).trim();process.env[S]=y}return g(`Loaded ${c}`),async()=>{let{unlinkSync:m}=await import("node:fs");if(d)Cn(d,`${e}/.env`),m(d),a("Restored .env");if(l)Cn(l,`${e}/.env.local`),m(l),a("Restored .env.local")}}async function tp(r,e,t,n){if(!t)return;let i=yo(r.sites||{});if(i.size===0)return;if(!e){I("DNS: no app server IP resolved — skipping A-record reconciliation.");return}let o;try{o=ce(t)}catch(u){I(`DNS: ${u instanceof Error?u.message:String(u)}`),a(`Point each site domain at ${e} manually.`);return}if(!o){I(`DNS provider '${t}' is not configured — skipping A-record reconciliation.`),a(`Point each site domain at ${e} manually.`);return}if(o.provider==="route53"){let u=r.infrastructure?.dns?.hostedZoneId;if(u&&!o.hostedZoneId)o.hostedZoneId=u}let s=Re(o),c=r.infrastructure?.dns?.domain,l=_t(n),d=l?"A/AAAA":"A";M(`Reconciling DNS: ${i.size} ${d} record(s) → ${e} via ${t}...`);for(let u of i){let m=c&&u.endsWith(c)?c:u.split(".").slice(-2).join(".");try{let f=await rn({provider:s,zone:m,fqdn:u,ipv4:e,ipv6:l});for(let h of f.published)g(` ✓ ${h.fqdn} ${h.type} → ${h.content}`);for(let h of f.warnings)I(` ⚠ ${u}: ${h}`)}catch(f){I(` ⚠ ${u}: ${f instanceof Error?f.message:String(f)} — set the address record manually.`)}}}async function np(r,e){let t=r.infrastructure?.dns?.records;if(!e||!t?.length)return;let n=r.infrastructure?.dns?.domain;if(!n){I("DNS records: infrastructure.dns.domain is not set — skipping declared records.");return}let i;try{i=ce(e)}catch(c){I(`DNS records: ${c instanceof Error?c.message:String(c)}`);return}if(!i){I(`DNS records: provider '${e}' is not configured — skipping.`);return}let o=Re(i),s=go(t,n);M(`Reconciling ${s.length} declared DNS record(s) on ${n}...`);try{let c=await ho({provider:o,zone:n,records:s});for(let l of c.outcomes){let{record:d,action:u}=l,m=`${d.type} ${d.name} -> ${d.content.slice(0,60)}`;if(u==="failed")I(` ⚠ ${m}: ${l.message||"failed"}`);else if(u==="unchanged")a(` = ${m}`);else g(` ✓ ${m} (${u})`)}for(let l of c.warnings)I(` ⚠ ${l}`)}catch(c){I(`DNS records: ${c instanceof Error?c.message:String(c)}`)}}async function rp(r,e,t){let{plan:n,errors:i}=co(r);for(let s of i)I(`Cloudflare CDN: ${s}`);if(!n)return;if(!e){I("Cloudflare CDN: no app server IP resolved — skipping.");return}M(`Cloudflare CDN: reconciling ${n.hosts.length} host(s) on ${n.zone}...`);let o=new pn(n.apiToken,{zoneId:n.zoneId,accountId:n.accountId});try{let s=await so({provider:o,zone:n.zone,hosts:n.hosts,ipv4:e,ipv6:_t(t),proxied:n.proxied,settings:n.settings,cache:n.cache,originGuard:n.originGuard,purge:n.purge,skipOriginProbe:n.skipOriginProbe});for(let c of s.records)g(` ✓ ${c.host} ${c.type} → ${c.content}${c.proxied?" (proxied)":" (DNS-only)"}`);for(let c of s.settingsChanged)a(` ✓ ${c.id}: ${JSON.stringify(c.from)} → ${JSON.stringify(c.to)}`);if(s.cacheRules>0)g(` ✓ ${s.cacheRules} cache rule(s) applied`);if(s.originGuard)g(" ✓ origin guard header applied");if(s.purged)g(" ✓ edge cache purged");for(let c of s.warnings)I(` ⚠ ${c}`)}catch(s){I(`Cloudflare CDN: ${s instanceof Error?s.message:String(s)}`)}}async function ip(r,e,t,n,i,o,s=!1){let c=r.sites||{},l=e?[e]:Object.keys(c);if(l.length===0){I("No sites configured in cloud.config.ts");return}let d=null,u=!1,m=()=>{if(d||u)return d;let f=null;try{f=ce(t)}catch(h){p(h.message)}if(!f)return p(`DNS provider '${t}' is not configured. Please set the required environment variables.`),a(`
429
+ For Cloudflare: CLOUDFLARE_API_TOKEN`),a("For Porkbun: PORKBUN_API_KEY, PORKBUN_SECRET_KEY"),a("For GoDaddy: GODADDY_API_KEY, GODADDY_API_SECRET"),u=!0,null;if(f.provider==="route53"){let h=r.infrastructure?.dns?.hostedZoneId;if(h&&!f.hostedZoneId)f.hostedZoneId=h}return d={config:f,provider:Re(f)},d};for(let f of l){let h=c[f];if(!h){p(`Site '${f}' not found in configuration`);continue}if(!cl(h)){let E=kn(h);a(`Site '${f}' is ${E} — handled outside the static bucket pipeline.`);continue}let S=m();if(!S)return;let y=h.domain;if(!y){p(`Site '${f}' has no domain configured`);continue}if(w(`Deploying Site: ${f}`),a(`Domain: ${y}`),a(`Source: ${h.root}`),a(`DNS Provider: ${t}`),h.build){M(`Running build command: ${h.build}`);try{let{execSync:E}=await import("node:child_process");E(h.build,{stdio:"inherit",cwd:process.cwd()}),g("Build completed successfully")}catch(E){p(`Build failed: ${E.message}`);continue}}if(!ge(h.root)){if(p(`Source directory not found: ${h.root}`),!h.build)a('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 v=await S.provider.listRecords(y);if(v.success&&v.records.length>0){let E=y.split("."),N=E.length>2?E[0]:"@",_=E.slice(-2).join("."),B=v.records.find((V)=>V.type==="CNAME"&&(V.name===y||V.name===N||V.name===`${N}.${_}`));if(B){let V=B.content.includes("netlify"),K=B.content.includes("vercel");if(B.content.includes("cloudfront.net"))a(`Domain already points to CloudFront: ${B.content}`),a("Proceeding with file upload...");else{let G=V?"Netlify":K?"Vercel":"another provider";if(I(`
430
+ Existing CNAME record detected:`),a(` ${B.name} -> ${B.content}`),V||K)a(`
431
+ This domain is currently pointing to ${G}.`),a("Deploying will update this record to point to AWS CloudFront.");if(!(s||await O(`
432
+ Update DNS record to point to AWS CloudFront?`,!0))){a("Deployment cancelled");continue}M(`Removing old ${G} CNAME record...`);let H=await S.provider.deleteRecord(y,{name:B.name,type:"CNAME",content:B.content});if(H.success)g(`Removed CNAME: ${B.name} -> ${B.content}`);else I(`Could not remove old record: ${H.message}`),a("The deployment will attempt to update it instead.")}}}let R=h.root,b,A=!!h.installScript;if(A){let{mkdtempSync:E,copyFileSync:N,readdirSync:_,statSync:B}=await import("node:fs"),{tmpdir:V}=await import("node:os"),{join:K,resolve:oe}=await import("node:path");b=E(K(V(),"cloud-install-"));let G=oe(h.installScript);N(G,K(b,"index.html"));let L=G.split("/").pop();if(L!=="index.html")N(G,K(b,L));if(ge(h.root)){let H=_(h.root);for(let j of H){let W=K(h.root,j);if(B(W).isFile()&&j!=="index.html")N(W,K(b,j))}}R=b,a(`Install script: ${h.installScript}`),a(`Serving at: curl -fsSL https://${y} | bash`)}M("Deploying to AWS (S3 + CloudFront)...");let k=ol(r,f,h,o),T=sl(r,f);a(`Stack: ${k}`);let D=await lr({siteName:T,stackName:k,domain:y,region:n,bucket:al(r.project.slug,o,f,h.bucket),sourceDir:R,certificateArn:h.certificateArn,dnsProvider:S.config,skipDnsVerification:i,passthroughUrls:A,dynamicApp:!!r.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginDomain:r.infrastructure?.compute?.cloudFrontOriginDomain,computeOriginPort:r.infrastructure?.compute?.cloudFrontOriginPort??r.infrastructure?.api?.port??3008,computeOriginId:r.infrastructure?.compute?.cloudFrontOriginId??`${r.project.slug}-site-ec2`,onProgress:(E,N)=>{if(E==="infrastructure")M(N||"Setting up infrastructure...");else if(E==="upload"){if(N?.includes("/")&&!N.includes("1/")){let _=N.match(/(\d+)\/(\d+)/);if(_){let[,B,V]=_;if(Number(B)%10===0||B===V)a(` Uploaded ${B}/${V} files`)}}}else if(E==="invalidate")M("Invalidating CDN cache...")}});if(b){let{rmSync:E}=await import("node:fs");E(b,{recursive:!0,force:!0})}if(D.success){g(`
433
+ Deployment successful!`);let E=D.filesSkipped>0?`${D.filesUploaded} uploaded, ${D.filesSkipped} unchanged`:`${D.filesUploaded}`,N=A?`
434
+ Install: curl -fsSL https://${D.domain} | bash`:"";Qe(`Site Deployed!
435
+
436
+ Domain: https://${D.domain}
437
+ CloudFront: ${D.distributionDomain}
438
+ Bucket: ${D.bucket}
439
+ Files: ${E}${N}
440
+
441
+ Your site is now live at https://${D.domain}`,"green")}else throw Error(D.message||`Site '${f}' deployment failed`)}}import{execFileSync as op}from"node:child_process";import{createHash as sp}from"node:crypto";import{existsSync as wl,mkdirSync as ap,mkdtempSync as cp,readdirSync as lp,readFileSync as dp,rmSync as up,statSync as Sl,writeFileSync as mp}from"node:fs";import{tmpdir as pp}from"node:os";import{basename as fp,join as Zt,relative as gp,resolve as xo}from"node:path";var hp=new Set([".git","node_modules","dist","coverage"]);function $l(r,e=r){let t=[];for(let n of lp(r).sort()){if(hp.has(n))continue;let i=Zt(r,n);if(bl(i,e))continue;let o=Sl(i);if(o.isDirectory())t.push(...$l(i,e));else if(o.isFile())t.push(gp(e,i))}return t}function yp(r){let e=xo(r);if(!wl(e)||!Sl(e).isDirectory())throw Error(`Container context does not exist: ${e}`);let t=sp("sha256");for(let n of $l(e))t.update(n),t.update("\x00"),t.update(dp(Zt(e,n))),t.update("\x00");return t.digest("hex")}function vp(r){return{ecr:new Rn(r.region||"us-east-1",r.profile),run:(e,t,n)=>{op(e,t,{cwd:n?.cwd,env:n?.env,stdio:n?.stdio||"inherit"})}}}async function Cl(r,e){let t=xo(r.context),n=xo(r.dockerfile||Zt(t,"Dockerfile"));if(!wl(n))throw Error(`Dockerfile does not exist: ${n}`);if(!r.repository.split("/").every((S)=>/^[a-z0-9][a-z0-9._-]*$/.test(S)&&!/[._-]$/.test(S)&&!/[._-]{2}/.test(S)))throw Error("ECR repository name is invalid");let s=e||vp(r),c=yp(t),l=`sha-${c.slice(0,20)}`,d="";try{d=(await s.ecr.describeRepositories({repositoryNames:[r.repository]})).repositories?.[0]?.repositoryUri||""}catch{}if(!d)d=(await s.ecr.createRepository({repositoryName:r.repository,imageTagMutability:"IMMUTABLE",imageScanningConfiguration:{scanOnPush:!0},tags:[{Key:"ManagedBy",Value:"ts-cloud"}]})).repository?.repositoryUri||"";if(!d)throw Error(`Could not resolve ECR repository URI for ${r.repository}`);await s.ecr.putLifecyclePolicy({repositoryName:r.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 u=`${d}:${l}`,m=cp(Zt(pp(),"ts-cloud-container-"));try{s.run("docker",["version"],{stdio:"ignore"}),s.run("docker",["build","--platform",r.platform||"linux/amd64","--provenance=false","--sbom=false","--file",n,"--tag",u,t],{cwd:t});let S=(await s.ecr.getAuthorizationToken()).authorizationData?.[0];if(!S?.authorizationToken||!S.proxyEndpoint)throw Error("ECR did not return Docker authorization data");let y=S.proxyEndpoint.replace(/^https?:\/\//,""),v=Zt(m,"docker");ap(v,{recursive:!0}),mp(Zt(v,"config.json"),JSON.stringify({auths:{[y]:{auth:S.authorizationToken}}}),{mode:384}),s.run("docker",["push",u],{env:{...process.env,DOCKER_CONFIG:v}})}finally{up(m,{recursive:!0,force:!0})}let h=(await s.ecr.describeImages({repositoryName:r.repository,imageIds:[{imageTag:l}]})).imageDetails?.[0]?.imageDigest;if(!h||!/^sha256:[a-f0-9]{64}$/i.test(h))throw Error(`ECR did not return an immutable digest for ${fp(u)}`);return{repository:r.repository,tag:l,imageUri:u,digest:h,digestUri:`${d}@${h}`,contextSha256:c}}import{createHash as bp}from"node:crypto";function wp(r){let e=r;return String(e?.code||e?.name||e?.statusCode||"")}function Sp(r){return wp(r)==="ValidationError"&&String(r.message).includes("does not exist")}function $p(r){return Object.fromEntries((r||[]).map((e)=>[e.OutputKey,e.OutputValue]))}function Cp(r){let e=r?.Aliases?.Items??r?.Aliases;if(Array.isArray(e))return e.map(String);if(typeof e==="string")return[e];return[]}function Rl(r){if(!/^[a-z][a-z0-9-]{1,62}$/.test(r.slug))throw Error("Slug must be a lowercase DNS-safe identifier");if(!/^[A-Z0-9]{8,32}$/.test(r.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(r.expectedAlias))throw Error("Expected distribution alias is invalid");let e=r.imageUri.match(/@((?:sha256:)[a-f0-9]{64})$/i)?.[1];if(!e)throw Error("Container image must use an immutable ECR digest URI ending in @sha256:<64 hex>");let t=r.stackName||`${r.slug}-backend`;if(!/^[A-Za-z][A-Za-z0-9-]{0,127}$/.test(t))throw Error("CloudFormation stack name is invalid");let n=r.pathPattern||"/api/*";if(!/^\/[A-Za-z0-9_.*?~!$&'()+,;=:@%/-]+\*$/.test(n)||n==="/*")throw Error("Backend path must be a non-default wildcard pattern");let i=r.originId||`${r.slug}-backend`;if(!/^[A-Za-z0-9_.-]{1,128}$/.test(i))throw Error("CloudFront origin ID is invalid");if(r.originVerifySecret&&r.originVerifySecret.length<24)throw Error("Origin verification secret must contain at least 24 characters");if(!!r.originDomain!==!!r.certificateArn)throw Error("A custom origin domain and matching ACM certificate ARN must be provided together");return{stackName:t,region:r.region||"us-east-1",pathPattern:n,originId:i,digest:e}}function Eo(r){let e=r.region||"us-east-1";return{cloudformation:new ie(e,r.profile),cloudfront:new ne(r.profile),sts:new Dt(e,r.profile),fetch,sleep:(t)=>new Promise((n)=>setTimeout(n,t))}}function Ap(r){let e=Rl(r),t=kl({name:r.name,slug:r.slug,domain:r.originDomain||r.expectedAlias,imageUri:r.imageUri,certificateArn:r.certificateArn,originVerifySecret:r.originVerifySecret,desiredCount:r.desiredCount,database:r.database,cache:r.cache,queue:r.queue}),n=Al(t);return n.Description=`Managed backend for existing static frontend ${r.expectedAlias}`,n.Metadata={ManagedBy:"ts-cloud",ExistingDistributionId:r.distributionId,ExistingDistributionAlias:r.expectedAlias,BackendPathPattern:e.pathPattern,ContainerDigest:e.digest},n}async function kp(r,e,t){let n;for(let i=0;i<30;i++){let o=Date.now();try{let s=await r.fetch(e,{redirect:"manual",headers:t?{"x-origin-verify":t}:void 0}),c=Date.now()-o,l=await s.text(),d=l;try{d=JSON.parse(l)}catch{}if(s.ok)return{url:e,status:s.status,latencyMs:c,body:d};n=Error(`origin returned ${s.status}`)}catch(s){n=s}if(i<29)await r.sleep(1e4)}throw Error(`Backend origin did not become healthy: ${n instanceof Error?n.message:String(n)}`)}async function Il(r,e){let t=Rl(r),n=e||Eo({region:t.region,profile:r.profile}),[i,o]=await Promise.all([n.sts.getCallerIdentity(),n.cloudfront.getDistribution(r.distributionId)]);if(!i.Account)throw Error("AWS identity did not return an account ID");if(!o.Enabled)throw Error(`CloudFront distribution ${r.distributionId} is disabled`);if(!Cp(o).includes(r.expectedAlias))throw Error(`Distribution ${r.distributionId} does not contain expected alias ${r.expectedAlias}`);let s;try{s=(await n.cloudformation.describeStacks({stackName:t.stackName})).Stacks[0]}catch(k){if(!Sp(k))throw k}let c=Ap(r),l=JSON.stringify(c);if(Buffer.byteLength(l)>51200)throw Error("Generated template exceeds the direct CloudFormation template body limit");let d=Object.values(c.Resources||{}).reduce((k,T)=>(k[T.Type]=(k[T.Type]||0)+1,k),{}),u=[`cloud cdn:origin:remove ${r.distributionId} ${r.originDomain||"ALB_HOST"} --id ${t.originId} --path '${t.pathPattern}' --profile ${r.profile||"default"} --apply --confirm 'remove:${r.distributionId}:${t.pathPattern}'`,`After CloudFront is deployed, restore external DNS if changed and delete stack ${t.stackName} only with an approved data-retention decision.`],m={mode:r.apply?"apply":"plan",accountId:i.Account,stack:{name:t.stackName,existed:!!s,status:s?.StackStatus,templateSha256:bp("sha256").update(l).digest("hex"),resourceCount:Object.keys(c.Resources||{}).length,resourceTypes:d},distribution:{id:o.Id,alias:r.expectedAlias,domainName:o.DomainName,status:o.Status,pathPattern:t.pathPattern,originId:t.originId},artifact:{imageUri:r.imageUri,digest:t.digest},services:{database:r.database!==!1,cache:r.cache!==!1,queue:r.queue!==!1,mail:"ses",desiredCount:r.desiredCount||1},cloudFrontApplied:!1,dnsApplied:!1,applied:!1,rollback:u};if(!r.apply)return m;let f=`${r.distributionId}:${t.pathPattern}:${t.stackName}`;if(r.confirm!==f)throw Error(`Pass exact confirmation token ${f} to provision the backend and change live routing`);if(s)try{await n.cloudformation.updateStack({stackName:t.stackName,templateBody:l,capabilities:["CAPABILITY_NAMED_IAM"],tags:[{Key:"ManagedBy",Value:"ts-cloud"},{Key:"Application",Value:r.slug}]}),await n.cloudformation.waitForStack(t.stackName,"stack-update-complete")}catch(k){if(!String(k.message).includes("No updates are to be performed"))throw k}else await n.cloudformation.createStack({stackName:t.stackName,templateBody:l,capabilities:["CAPABILITY_NAMED_IAM"],onFailure:"ROLLBACK",tags:[{Key:"ManagedBy",Value:"ts-cloud"},{Key:"Application",Value:r.slug}]}),await n.cloudformation.waitForStack(t.stackName,"stack-create-complete");let h=(await n.cloudformation.describeStacks({stackName:t.stackName})).Stacks[0],S=$p(h?.Outputs),y=S.AppLoadBalancerDnsName;if(!y)throw Error("Backend stack did not return AppLoadBalancerDnsName");let v=!1;if(r.originDomain&&r.originDomain!==y){if(!n.dns)throw Error(`External DNS provider is required to map ${r.originDomain} to ${y}`);let k=await n.dns.upsertRecord(r.originDomain,{name:r.originDomain,type:"CNAME",content:y,ttl:300});if(!k.success)throw Error(k.message||"External DNS update failed");v=!0}let R=r.originDomain||y,b=r.certificateArn?"https-only":"http-only",A=r.skipHealthCheck?void 0:await kp(n,`${b==="https-only"?"https":"http"}://${R}/api/health`,r.originVerifySecret);return await n.cloudfront.upsertExistingDistributionOrigin(r.distributionId,{id:t.originId,domainName:R,pathPattern:t.pathPattern,protocolPolicy:b,customHeaders:r.originVerifySecret?{"X-Origin-Verify":r.originVerifySecret}:void 0}),{...m,outputs:S,health:A,cloudFrontApplied:!0,dnsApplied:v,applied:!0}}function Pl(r={}){let t={fargate:730*(r.desiredCount||1)*0.012342500000000001,applicationLoadBalancer:16.425,natGateway:32.85,postgres:730*(r.multiAzDatabase?0.032:0.016),redis:23.36};return{monthlyUsd:Number(Object.values(t).reduce((n,i)=>n+i,0).toFixed(2)),components:Object.fromEntries(Object.entries(t).map(([n,i])=>[n,Number(i.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 Do(r){r.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(e,t)=>{try{let n=await Cl({repository:e,context:t.context||".",dockerfile:t.dockerfile,platform:t.platform,region:t.region,profile:t.profile});g("Immutable container artifact published."),a(JSON.stringify(n,null,2))}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}}),r.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(e,t,n)=>{try{if(!n.slug)throw Error("--slug is required");if(!n.image)throw Error("--image must be an immutable digest URI from container:artifact");let i=n.originSecretEnv||"TS_CLOUD_ORIGIN_SECRET",o=process.env[i];if(n.apply&&!o)throw Error(`${i} must contain a stable random CloudFront-to-ALB secret before apply`);let s=Eo({region:n.region,profile:n.profile}),c=n.originDomain&&n.apply?{...s,dns:Be(n.dnsProvider)}:s,l=await Il({name:n.name||n.slug,slug:n.slug,imageUri:n.image,distributionId:e,expectedAlias:t,stackName:n.stack,pathPattern:n.path,originId:n.originId,originDomain:n.originDomain,certificateArn:n.certificateArn,originVerifySecret:o,desiredCount:Number(n.desiredCount)||1,database:n.database!==!1,cache:n.cache!==!1,queue:n.queue!==!1,region:n.region,profile:n.profile,skipHealthCheck:!!n.skipHealthCheck,apply:!!n.apply,confirm:n.confirm},c);if(a(JSON.stringify(l,null,2)),l.applied)g("Full-stack backend is healthy and the CloudFront path patch was submitted.");else a(`Plan only. Apply with --confirm '${e}:${l.distribution.pathPattern}:${l.stack.name}'.`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}}),r.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((e)=>a(JSON.stringify(Pl({desiredCount:Number(e.desiredCount)||1,multiAzDatabase:!!e.multiAzDatabase}),null,2)))}function To(r){r.command("destroy","Destroy the single-server compute (instance + firewall)").option("--env <env>","Environment",{default:"production"}).option("--force","Skip the confirmation prompt").action(async(e)=>{w("Destroy Compute");let t=await P(),n=e?.env||"production",i=Yt(t),o=Ke({config:t,provider:i});if(!o.destroyCompute){p(`The ${i} driver does not support compute teardown`);return}if(I(`This terminates the ${i} server for ${t.project.slug}/${n} and deletes its firewall.`),!e?.force){if(!await O("This is irreversible. Continue?",!1)){a("Cancelled");return}}let s=new C("Destroying compute...");s.start();try{let{destroyed:c}=await o.destroyCompute({config:t,environment:n});if(s.succeed("Compute destroyed"),c.length>0)c.forEach((l)=>a(` removed ${l}`));else a("Nothing to destroy (no matching resources found)")}catch(c){s.fail("Teardown failed"),p(c instanceof Error?c.message:String(c))}})}import{statSync as Rp,writeFileSync as Ip}from"node:fs";function No(r){r.command("stack:list","List all CloudFormation stacks").action(async()=>{w("CloudFormation Stacks");try{let t=(await P()).project.region||"us-east-1",n=new ie(t),i=new C("Loading stacks...");i.start();let o=await n.listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS"]);if(i.succeed(`Found ${o.StackSummaries.length} stacks`),o.StackSummaries.length===0){a("No stacks found");return}let s=["Stack Name","Status","Created","Updated"],c=o.StackSummaries.map((l)=>[l.StackName,l.StackStatus,new Date(l.CreationTime).toLocaleString(),l.LastUpdatedTime?new Date(l.LastUpdatedTime).toLocaleString():"Never"]);x(s,c)}catch(e){p(`Failed to list stacks: ${e.message}`)}}),r.command("stack:describe STACK_NAME","Describe a CloudFormation stack").action(async(e)=>{w(`Stack: ${e}`);try{let n=(await P()).project.region||"us-east-1",i=new ie(n),o=new C("Loading stack details...");o.start();let s=await i.describeStacks({stackName:e});if(!s.Stacks||s.Stacks.length===0){o.fail("Stack not found");return}let c=s.Stacks[0];if(o.succeed("Stack details loaded"),a(`
442
+ Stack Information:`),a(` - Name: ${c.StackName}`),a(` - Status: ${c.StackStatus}`),a(` - Created: ${new Date(c.CreationTime).toLocaleString()}`),c.LastUpdatedTime)a(` - Updated: ${new Date(c.LastUpdatedTime).toLocaleString()}`);if(c.Parameters&&c.Parameters.length>0){a(`
443
+ Parameters:`);for(let d of c.Parameters)a(` - ${d.ParameterKey}: ${d.ParameterValue}`)}if(c.Outputs&&c.Outputs.length>0){a(`
444
+ Outputs:`);for(let d of c.Outputs)if(a(` - ${d.OutputKey}: ${d.OutputValue}`),d.Description)a(` ${d.Description}`)}if(c.Tags&&c.Tags.length>0){a(`
445
+ Tags:`);for(let d of c.Tags)a(` - ${d.Key}: ${d.Value}`)}M(`
446
+ Loading stack resources...`);let l=await i.listStackResources(e);if(l.StackResourceSummaries.length>0){a(`
447
+ Resources (${l.StackResourceSummaries.length}):`);let d=["Logical ID","Type","Status"],u=l.StackResourceSummaries.slice(0,10).map((m)=>[m.LogicalResourceId,m.ResourceType,m.ResourceStatus]);if(x(d,u),l.StackResourceSummaries.length>10)a(`
448
+ ... and ${l.StackResourceSummaries.length-10} more resources`)}}catch(t){p(`Failed to describe stack: ${t.message}`)}}),r.command("stack:delete STACK_NAME","Delete a CloudFormation stack").option("--yes","Skip the confirmation prompt").action(async(e,t)=>{w(`Delete Stack: ${e}`);try{let i=(await P()).project.region||"us-east-1";if(I("This will permanently delete the stack and all its resources!"),!(t?.yes||await O(`
449
+ Are you sure you want to delete this stack?`,!1))){a("Deletion cancelled");return}let s=new ie(i),c=new C("Deleting stack...");c.start(),await s.deleteStack(e),c.succeed("Stack deletion initiated"),M("Waiting for stack deletion..."),await s.waitForStack(e,"stack-delete-complete"),g("Stack deleted successfully!")}catch(n){p(`Failed to delete stack: ${n.message}`)}}),r.command("stack:events STACK_NAME","Show stack events").option("--limit <number>","Limit number of events",{default:"20"}).action(async(e,t)=>{w(`Stack Events: ${e}`);try{let i=(await P()).project.region||"us-east-1",o=new ie(i),s=new C("Loading events...");s.start();let c=await o.describeStackEvents(e);s.succeed(`Found ${c.StackEvents.length} events`);let l=t?.limit?Number.parseInt(t.limit):20,d=c.StackEvents.slice(0,l);if(d.length===0){a("No events found");return}let u=["Time","Resource","Status","Reason"],m=d.map((f)=>[new Date(f.Timestamp).toLocaleString(),f.LogicalResourceId,f.ResourceStatus,f.ResourceStatusReason||""]);x(u,m)}catch(n){p(`Failed to load events: ${n.message}`)}}),r.command("stack:outputs STACK_NAME","Show stack outputs").action(async(e)=>{w(`Stack Outputs: ${e}`);try{let n=(await P()).project.region||"us-east-1",i=new ie(n),o=new C("Loading stack outputs...");o.start();let s=await i.describeStacks({stackName:e});if(!s.Stacks||s.Stacks.length===0){o.fail("Stack not found");return}let c=s.Stacks[0];if(o.succeed("Stack outputs loaded"),!c.Outputs||c.Outputs.length===0){a("No outputs found for this stack");return}let l=["Key","Value","Description","Export Name"],d=c.Outputs.map((u)=>[u.OutputKey||"",u.OutputValue||"",u.Description||"",u.ExportName||""]);x(l,d),a(`
450
+ Copy-friendly format:`);for(let u of c.Outputs)a(`${u.OutputKey}=${u.OutputValue}`)}catch(t){p(`Failed to load outputs: ${t.message}`)}}),r.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(e,t)=>{w(`Export Stack: ${e}`);try{let i=(await P()).project.region||"us-east-1",o=new ie(i),s=new C("Fetching stack template...");s.start();let c=await o.getTemplate(e);if(!c.TemplateBody){s.fail("Template not found");return}s.succeed("Template fetched");let l=t?.format||"json",d=c.TemplateBody;if(l==="json"){let u=JSON.parse(d);d=JSON.stringify(u,null,2)}if(t?.output){let u=t.output;Ip(u,d,"utf-8"),g(`Template exported to: ${u}`);let f=(Rp(u).size/1024).toFixed(2);a(`File size: ${f} KB`)}else a(`
451
+ Template:`),console.log(d)}catch(n){p(`Failed to export template: ${n.message}`)}})}import{existsSync as Pp}from"node:fs";function Oo(r,e){r.command("upgrade","Upgrade CLI to latest version").action(async()=>{be("upgrade",{message:`Self-update is disabled for this ${e} build because no signed update-manifest source is configured.`,nextAction:"Install a verified release with your package manager or configure platform maintenance updates."})}),r.command("doctor","Check system requirements and AWS credentials").action(async()=>{if(w("System Diagnostics"),M("Checking Bun..."),g(`Bun ${process.versions.bun}`),M("Checking AWS CLI..."),await xl())g("AWS CLI is installed");else p("AWS CLI is not installed"),a("Install: https://aws.amazon.com/cli/");if(M("Checking AWS credentials..."),await El()){g("AWS credentials are configured");let o=await Dl();if(o)a(`Account ID: ${o}`)}else p("AWS credentials are not configured"),a("Run: aws configure");M("Checking CloudFront list access...");let i=!1;try{let{CloudFrontClient:o}=await import("./chunk-xv4bscrm.js");await new o().listDistributions(),g("CloudFront list access is enabled"),i=!0}catch(o){I(`CloudFront list check failed: ${o.message}`)}if(i){M("Checking CloudFront create access...");try{let o=new ie("us-east-1"),s=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 o.validateTemplate(s),g("CloudFront create access appears enabled")}catch(o){if(o.message?.includes("403")||o.message?.includes("AccessDenied")||o.message?.includes("must be verified"))p("CloudFront create access denied - account verification required"),a(""),a("Your AWS account needs to be verified for CloudFront."),a("This is required before you can create CloudFront distributions."),a(""),a("To verify your account:"),a(" 1. Go to: https://console.aws.amazon.com/support/home#/"),a(" 2. Create a support case"),a(" 3. Select: Service limit increase > CloudFront"),a(' 4. Request: "Please verify my account for CloudFront access"'),a(""),a("Verification usually takes 1-2 business days.");else g("CloudFront create access appears enabled (validation passed)")}}M("Checking S3 access...");try{let{S3Client:o}=await import("./chunk-w5d6tr2x.js");await new o("us-east-1").listBuckets(),g("S3 access is enabled")}catch(o){I(`S3 check failed: ${o.message}`)}M("Checking ACM (SSL certificates) access...");try{await new vt("us-east-1").listCertificates(),g("ACM access is enabled")}catch(o){I(`ACM check failed: ${o.message}`)}if(M("Checking configuration..."),Pp("cloud.config.ts"))g("cloud.config.ts found");else I("cloud.config.ts not found"),a("Run: cloud init")}),r.command("regions","List available AWS regions").action(async()=>{w("AWS Regions");let t=new C("Fetching regions...");t.start();let n=await Tl();t.stop(),n.forEach((i)=>{console.log(` ${i}`)})}),r.command("version","Show the version of the CLI").alias("v").action(()=>{console.log(`ts-cloud v${e}`)})}function Lo(r){r.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(e)=>{w("Analytics Sites");try{let t=new Te(e.region),n=new C("Fetching sites...");n.start();let i=await t.scan({TableName:e.table,FilterExpression:"begins_with(pk, :pk)",ExpressionAttributeValues:{":pk":{S:"SITE#"}}});if(n.succeed(`Found ${i.Items?.length||0} site(s)`),!i.Items||i.Items.length===0){a("No analytics sites found"),a("Use `cloud analytics:sites:create` to create a new site");return}let o=i.Items.map((s)=>Te.unmarshal(s));x(["ID","Name","Domains","Active","Created"],o.map((s)=>[s.siteId||"N/A",s.name||"Unnamed",(s.domains||[]).join(", ")||"-",s.isActive?"Yes":"No",s.createdAt?new Date(s.createdAt).toLocaleDateString():"N/A"]))}catch(t){p(`Failed to list sites: ${t.message}`),process.exit(1)}}),r.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(e)=>{w("Create Analytics Site");try{let t=new Te(e.region),n=e.name||await Z("Site name","My Site"),i=[];if(e.domain)i=Array.isArray(e.domain)?e.domain:[e.domain];else i=(await Z("Site domains (comma-separated)","example.com")).split(",").map((u)=>u.trim()).filter(Boolean);if(a(`
452
+ Creating site: ${n}`),a(`Domains: ${i.join(", ")}`),!await O(`
453
+ Create this site?`,!0)){a("Operation cancelled");return}let s=new C("Creating site...");s.start();let c=`site_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,l=new Date().toISOString();await t.putItem({TableName:e.table,Item:{pk:{S:`SITE#${c}`},sk:{S:`SITE#${c}`},siteId:{S:c},name:{S:n},domains:{L:i.map((d)=>({S:d}))},isActive:{BOOL:!0},createdAt:{S:l},updatedAt:{S:l}}}),s.succeed("Site created successfully"),g(`
454
+ Site ID: ${c}`),a(`
455
+ Add the tracking script to your website:`),a(` <script src="https://analytics.stacksjs.com/track.js" data-site-id="${c}"></script>`)}catch(t){p(`Failed to create site: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("Update Analytics Site");try{let n=new Te(t.region),i=await n.getItem({TableName:t.table,Key:{pk:{S:`SITE#${e}`},sk:{S:`SITE#${e}`}}});if(!i.Item)p(`Site not found: ${e}`),process.exit(1);let o=Te.unmarshal(i.Item);a(`Updating site: ${o.name||"Unnamed"} (${e})`),a("");let s=[],c={},l={};if(t.name)s.push("#n = :name"),c["#n"]="name",l[":name"]={S:t.name},a(` Name: ${o.name} -> ${t.name}`);if(t.domain!==void 0){let d=Array.isArray(t.domain)?t.domain:[t.domain];s.push("domains = :domains"),l[":domains"]={L:d.map((u)=>({S:u}))},a(` Domains: ${JSON.stringify(o.domains||[])} -> ${JSON.stringify(d)}`)}if(t.active!==void 0){let d=t.active==="true"||t.active==="1";s.push("isActive = :active"),l[":active"]={BOOL:d},a(` Active: ${o.isActive} -> ${d}`)}if(s.length===0){I("No updates specified. Use --name, --domain, or --active options.");return}s.push("updatedAt = :updatedAt"),l[":updatedAt"]={S:new Date().toISOString()},await n.updateItem({TableName:t.table,Key:{pk:{S:`SITE#${e}`},sk:{S:`SITE#${e}`}},UpdateExpression:`SET ${s.join(", ")}`,ExpressionAttributeNames:Object.keys(c).length>0?c:void 0,ExpressionAttributeValues:l}),a(""),g("Site updated successfully")}catch(n){p(`Failed to update site: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w(`Analytics Data: ${e}`);try{let n=new Te(t.region),i=new C("Querying data...");i.start();let o=await n.query({TableName:t.table,KeyConditionExpression:"pk = :pk",ExpressionAttributeValues:{":pk":{S:`SITE#${e}`}},ScanIndexForward:!1,Limit:parseInt(t.limit,10)});if(i.succeed(`Found ${o.Items?.length||0} items`),!o.Items||o.Items.length===0){a("No data found for this site");return}let s=o.Items.map((c)=>Te.unmarshal(c));for(let c of s){if(a(""),a(`SK: ${c.sk}`),c.path)a(` Path: ${c.path}`);if(c.timestamp)a(` Time: ${c.timestamp}`);if(c.visitorId)a(` Visitor: ${c.visitorId}`);if(c.browser)a(` Browser: ${c.browser}`);if(c.country)a(` Country: ${c.country}`)}}catch(n){p(`Failed to query data: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w(`Realtime: ${e}`);try{let n=new Te(t.region),i=new C("Checking realtime...");i.start();let o=parseInt(t.minutes,10),s=new Date(Date.now()-o*60*1000),c=await n.query({TableName:t.table,KeyConditionExpression:"pk = :pk AND sk BETWEEN :start AND :end",ExpressionAttributeValues:{":pk":{S:`SITE#${e}`},":start":{S:`PAGEVIEW#${s.toISOString()}`},":end":{S:"PAGEVIEW#Z"}},ScanIndexForward:!1});if(i.succeed(`Found ${c.Items?.length||0} pageviews in last ${o} minutes`),!c.Items||c.Items.length===0){I("No recent pageviews found"),a(""),a("Possible issues:"),a(" 1. Tracking script not installed correctly"),a(" 2. Site ID mismatch"),a(" 3. CORS or network issues");return}let l=c.Items.map((m)=>Te.unmarshal(m)),d=new Set(l.map((m)=>m.visitorId)).size;a(""),g(`${d} unique visitor(s) online`),a("");let u={};for(let m of l){let f=m.path||"/";u[f]=(u[f]||0)+1}a("Active pages:");for(let[m,f]of Object.entries(u).sort((h,S)=>S[1]-h[1]).slice(0,5))a(` ${m}: ${f} view(s)`);a(""),a("Recent pageviews:");for(let m of l.slice(0,5))a(` ${m.timestamp} - ${m.path} (${m.browser||"Unknown"})`)}catch(n){p(`Failed to check realtime: ${n.message}`),process.exit(1)}})}import{chmodSync as xp,mkdirSync as Ep,readFileSync as Dp,writeFileSync as Tp}from"node:fs";import{dirname as Np,resolve as fr}from"node:path";function Q(r){return new Ll(r?{path:fr(r)}:{})}function Op(r){let e=r.health();w("Control-plane diagnostics"),a(`Database: ${e.path}`),a(`Schema: ${e.schemaVersion}/${e.supportedSchemaVersion}`),a(`Integrity: ${e.integrity}`),a(`Journal: ${e.journalMode}`),a(`Size: ${e.databaseBytes.toLocaleString()} bytes`),a(`Last backup: ${e.lastBackupAt??"never"}`),a(`Operations: ${Object.entries(e.operations).map(([t,n])=>`${t}=${n}`).join(", ")}`),a(`Pending/retryable: ${e.pendingRetryableOperations}`)}function st(r,e){let t=r.getOrganization(e)??r.getOrganizationBySlug(e);if(!t)throw Error(`Organization '${e}' was not found.`);return t}function dr(r,e){let t=r??"organization";if(t==="organization")return{type:"organization"};if(!["project","environment","resource"].includes(t)||!e)throw Error("Scoped access requires --scope project|environment|resource and --scope-id <id>.");return{type:t,id:e}}function ur(r,e){let t=e.includes("@")?r.getIdentityByEmail(e):r.getIdentityByUsername(e);if(!t)throw Error(`Authentication identity '${e}' was not found.`);return t}function mr(r,e){return new Tt(r,{encryptionKey:le(fr(e??process.cwd()))})}function Nl(r,e="TS_CLOUD_API_TOKEN"){let t=process.env[e]?.trim();if(!t)throw Error(`Set ${e} to a one-time-issued API token.`);return new jl({baseUrl:r??process.env.TS_CLOUD_API_URL??"http://127.0.0.1:7676",token:t})}function jo(r){r.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(e)=>{let t=await gr({host:e?.host,port:Number(e?.port??7676),environment:e?.env,box:e?.box,verbose:e?.verbose});w("ts-cloud Local Dashboard"),g(`Serving ${t.url}`),a("Use Ctrl+C to stop."),await new Promise((n)=>{let i=()=>{t.server.stop(!0),n()};process.once("SIGINT",i),process.once("SIGTERM",i)})}),r.command("control-plane:status","Inspect local control-plane storage health").option("--path <path>","Use a non-default control-plane database").action((e)=>{let t=Q(e?.path);try{Op(t)}finally{t.close()}}),r.command("control-plane:backup","Create a consistent local control-plane backup").option("--path <path>","Use a non-default control-plane database").action((e)=>{let t=Q(e?.path);try{let n=t.createBackup("cli");g(`Backup written to ${n}`)}finally{t.close()}}),r.command("control-plane:export <file>","Export portable control-plane metadata and history").option("--path <path>","Use a non-default control-plane database").action((e,t)=>{let n=fr(e),i=Q(t?.path);try{Ep(Np(n),{recursive:!0}),Tp(n,`${JSON.stringify(i.exportSnapshot(),null,2)}
456
+ `,{mode:384}),xp(n,384),g(`Control-plane snapshot exported to ${n}`)}finally{i.close()}}),r.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((e,t)=>{let n=fr(e),i=JSON.parse(Dp(n,"utf8")),o=Q(t?.path);try{let s=t?.replace?o.createBackup("pre-import"):void 0;if(o.importSnapshot(i,{replace:t?.replace}),g(`Imported control-plane snapshot from ${n}`),s)a(`Previous state backed up to ${s}`)}finally{o.close()}}),r.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((e)=>{let t=Q(e?.path);try{let n=t.compact({eventRetentionDays:Number(e?.eventDays??90),operationRetentionDays:Number(e?.operationDays??365),vacuum:e?.vacuum!==!1});if(g(`Removed ${n.deletedEvents} event(s) and ${n.deletedOperations} terminal operation(s).`),n.vacuumed)a("Database file compacted.")}finally{t.close()}}),r.command("organization:list","List organizations in the local control plane").option("--path <path>","Use a non-default control-plane database").action((e)=>{let t=Q(e?.path);try{w("Organizations");for(let n of t.listOrganizations())a(`${n.slug} ${n.name} ${n.id}`)}finally{t.close()}}),r.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((e,t)=>{let n=Q(t?.path);try{let i=st(n,e);w(`${i.name} members`);for(let o of n.listMemberships(i.id,{includeRevoked:t?.all})){let s=n.getActor(o.actorId);a(`${s?.displayName??o.actorId} ${o.roleTemplate} ${o.scope.type}:${o.scope.id??i.slug} ${o.status} ${o.id}`);for(let c of n.listGrants(o.id))a(` ${c.effect} ${c.capability} @ ${c.scope.type}:${c.scope.id??i.slug}`)}}finally{n.close()}}),r.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((e,t,n)=>{let i=Q(n?.path);try{let o=st(i,e);if(!["owner","admin","deployer","operator","viewer","auditor"].includes(n?.role??"viewer"))throw Error("Unknown role template.");let s=i.createInvitation({organizationId:o.id,email:t,roleTemplate:n?.role??"viewer",scope:dr(n?.scope,n?.scopeId),expiresInMs:Number(n?.days??7)*86400000});g(`Invitation created for ${s.invitation.email}; expires ${s.invitation.expiresAt}`),a(`${String(n?.baseUrl??"http://127.0.0.1:7676").replace(/\/$/,"")}/accept-invitation?token=${encodeURIComponent(s.token)}`),a("This token is shown once. Resending revokes it and creates a new token.")}finally{i.close()}}),r.command("organization:invitations <organization>","List invitation states without exposing tokens").option("--path <path>","Use a non-default control-plane database").action((e,t)=>{let n=Q(t?.path);try{let i=st(n,e);w(`${i.name} invitations`);for(let o of n.listInvitations(i.id))a(`${o.email} ${o.roleTemplate} ${o.scope.type}:${o.scope.id??i.slug} ${o.state} ${o.id}`)}finally{n.close()}}),r.command("organization:revoke-invitation <id>","Revoke a pending organization invitation").option("--path <path>","Use a non-default control-plane database").action((e,t)=>{let n=Q(t?.path);try{let i=n.revokeInvitation(e);g(`Invitation ${i.id} is ${i.state}.`)}finally{n.close()}}),r.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((e,t,n,i)=>{let o=Q(i?.path);try{let s=st(o,e);if(!pr.includes(n))throw Error(`Unknown capability '${n}'.`);if(i?.effect!==void 0&&!["allow","deny"].includes(i.effect))throw Error("Effect must be allow or deny.");let c=o.upsertGrant({organizationId:s.id,membershipId:t,effect:i?.effect??"allow",capability:n,scope:dr(i?.scope,i?.scopeId)});g(`${c.effect} ${c.capability} added at ${c.scope.type}:${c.scope.id??s.slug}.`)}finally{o.close()}}),r.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((e,t)=>{if(t?.confirm!==e)throw Error(`Pass --confirm ${e} to revoke this membership.`);let n=Q(t?.path);try{let i=n.revokeMembership(e);g(`Membership ${i.id} revoked; session version is now ${i.sessionVersion}.`)}finally{n.close()}}),r.command("auth:identities","List local authentication identities without credential material").option("--path <path>","Use a non-default control-plane database").action((e)=>{let t=Q(e?.path);try{let n=new Tt(t);w("Authentication identities");for(let i of n.listIdentities())a(`${i.username} ${i.email??"no-email"} ${i.disabledAt?"disabled":"active"} ${i.id}`)}finally{t.close()}}),r.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((e,t)=>{let n=Q(t?.path);try{let i=new Tt(n),o=ur(i,e);i.revokeActionTokens(o.id,"password_reset");let s=i.createActionToken(o.id,"password_reset");g(`Created a one-time reset link for ${o.username}; it expires ${s.actionToken.expiresAt}.`),a(`${String(t?.baseUrl??"http://127.0.0.1:7676").replace(/\/$/,"")}/reset-password?token=${encodeURIComponent(s.token)}`)}finally{n.close()}}),r.command("auth:sessions <identity>","List active and expired sessions for an identity").option("--path <path>","Use a non-default control-plane database").action((e,t)=>{let n=Q(t?.path);try{let i=new Tt(n),o=ur(i,e);w(`${o.username} sessions`);for(let s of i.listSessions(o.id,{includeInactive:!0}))a(`${s.state} ${s.authMethod} ${s.userAgent??"unknown-device"} ${s.lastUsedAt} ${s.id}`)}finally{n.close()}}),r.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((e,t)=>{if(t?.confirm!==e)throw Error(`Pass --confirm ${e} to revoke these sessions.`);let n=Q(t?.path);try{let i=new Tt(n),o=ur(i,e),s=i.listSessions(o.id);for(let c of s)i.revokeSession(o.id,c.id);g(`Revoked ${s.length} session(s) for ${o.username}.`)}finally{n.close()}}),r.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((e,t)=>{if(t?.confirm!==e)throw Error(`Pass --confirm ${e} to disable MFA.`);let n=Q(t?.path);try{let i=new Tt(n),o=ur(i,e);i.disableMfa(o.id);for(let s of i.listSessions(o.id))i.revokeSession(o.id,s.id);g(`Disabled MFA and revoked active sessions for ${o.username}.`)}finally{n.close()}}),r.command("auth:oidc:list <organization>","List OIDC providers without credential material").option("--path <path>","Use a non-default control-plane database").action((e,t)=>{let n=Q(t?.path);try{let i=st(n,e),o=mr(n);w(`${i.name} OIDC providers`);let s=o.listOidcProviders(i.id,{includeDisabled:!0});if(s.length===0)a("No providers configured.");for(let c of s)a(`${c.enabled?"enabled":"disabled"} ${c.slug} ${c.issuer} role=${c.defaultRole} enforce=${c.enforceSso}`)}finally{n.close()}}),r.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((e,t,n)=>{let i=Q(n?.path);try{let o=st(i,e),s=mr(i,n?.authRoot),c=s.getOidcProviderBySlug(t),l=n?.secretEnv??"TS_CLOUD_OIDC_CLIENT_SECRET",d=process.env[l]?.trim();if(!c&&!d)throw Error(`Set ${l} to the client secret before creating this provider.`);let u=(n?.domains??c?.allowedDomains.join(",")??"").split(",").map((h)=>h.trim()).filter(Boolean),m=n?.defaultRole??c?.defaultRole??"viewer";if(!["admin","deployer","operator","viewer","auditor"].includes(m))throw Error("Default role must be admin, deployer, operator, viewer, or auditor.");let f=s.upsertOidcProvider({id:c?.id,organizationId:o.id,slug:t,name:n?.name??c?.name??t,issuer:n?.issuer??c?.issuer??"",clientId:n?.clientId??c?.clientId??"",clientSecret:d,allowedDomains:u,scopes:(n?.scopes??c?.scopes.join(",")??"email,profile").split(",").map((h)=>h.trim()).filter(Boolean),defaultRole:m,enforceSso:n?.enforceSso??c?.enforceSso??!1,enabled:n?.disabled?!1:c?.enabled??!0});if(g(`${f.name} saved as ${f.enabled?"enabled":"disabled"} with encrypted credentials.`),a(`Callback path: /auth/oidc/${f.slug}/callback`),f.enforceSso)a(`Offline recovery: cloud auth:oidc:disable ${f.slug} --confirm ${f.slug}`)}finally{i.close()}}),r.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(e,t)=>{let n=Q(t?.path);try{let o=mr(n,t?.authRoot).getOidcProviderBySlug(e);if(!o)throw Error(`OIDC provider '${e}' was not found.`);let s=await Ol(o);g(`${o.name} discovery is valid and issuer-bound.`),a(`Authorization: ${s.authorization_endpoint}`),a(`Token: ${s.token_endpoint}`),a(`JWKS: ${s.jwks_uri}`)}finally{n.close()}}),r.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((e,t)=>{if(t?.confirm!==e)throw Error(`Pass --confirm ${e} to disable this provider.`);let n=Q(t?.path);try{let i=mr(n,t?.authRoot),o=i.getOidcProviderBySlug(e);if(!o)throw Error(`OIDC provider '${e}' was not found.`);i.setOidcProviderEnabled(o.id,!1),g(`Disabled ${o.name}, cancelled pending sign-ins, and removed SSO enforcement.`)}finally{n.close()}}),r.command("api:service-accounts <organization>","List service accounts and safe token metadata").option("--path <path>","Use a non-default control-plane database").action((e,t)=>{let n=Q(t?.path);try{let i=st(n,e),o=new Qt(n);w(`${i.name} service accounts`);for(let s of o.listServiceAccounts(i.id,{includeDisabled:!0})){a(`${s.state} ${s.slug} ${s.name} ${s.id}`);for(let c of o.listTokens(s.id,{includeInactive:!0}))a(` ${c.state} ${c.prefix} ${c.name} expires=${c.expiresAt} last-used=${c.lastUsedAt??"never"}`)}}finally{n.close()}}),r.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((e,t,n)=>{let i=Q(n?.path);try{let o=st(i,e),s=n?.role??"deployer";if(!["admin","deployer","operator","viewer","auditor"].includes(s))throw Error("Service-account role cannot be owner and must be a supported role.");let l=new Qt(i).createServiceAccount({organizationId:o.id,slug:t,name:n?.name??t,roleTemplate:s,scope:dr(n?.scope,n?.scopeId)});g(`Created ${l.serviceAccount.name} (${l.serviceAccount.id}) with ${l.membership.roleTemplate} access.`)}finally{i.close()}}),r.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((e,t,n)=>{let i=Q(n?.path);try{let o=st(i,e),s=new Qt(i),c=s.getServiceAccount(t)??s.getServiceAccountBySlug(o.id,t);if(!c||c.organizationId!==o.id)throw Error(`Service account '${t}' was not found.`);let l=(n?.capabilities??"").split(",").map((m)=>m.trim()).filter((m)=>pr.includes(m));if(!l.length)throw Error(`Pass --capabilities with one or more of: ${pr.join(", ")}`);let d=Math.min(365,Math.max(1,Number(n?.expiresDays??90))),u=s.createToken({serviceAccountId:c.id,name:n?.name??"CLI token",capabilities:l,scope:dr(n?.scope,n?.scopeId),expiresAt:new Date(Date.now()+d*24*60*60*1000).toISOString()});g(`Created ${u.token.prefix}; copy this secret now. It will not be shown again.`),a(u.secret)}finally{i.close()}}),r.command("api:token:rotate <token>","Create an overlapping replacement for an API token").option("--path <path>","Use a non-default control-plane database").action((e,t)=>{let n=Q(t?.path);try{let i=new Qt(n).rotateToken(e);g(`Created overlapping replacement ${i.token.prefix}; revoke the old token after rollout.`),a(i.secret)}finally{n.close()}}),r.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((e,t)=>{if(t?.confirm!==e)throw Error(`Pass --confirm ${e} to revoke this token.`);let n=Q(t?.path);try{let i=new Qt(n).revokeToken(e);g(`Revoked ${i.prefix}; bearer requests stop immediately.`)}finally{n.close()}}),r.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(e)=>{let t=await Nl(e?.baseUrl,e?.tokenEnv).listProjects();for(let n of t.data)a(`${n.slug} ${n.name} ${n.id}`)}),r.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(e,t,n)=>{let i=n?.idempotencyKey??`cli-${Date.now()}-${crypto.randomUUID().slice(0,8)}`,o=await Nl(n?.baseUrl,n?.tokenEnv).createDeployment({projectId:e,environmentId:t,serviceId:n?.service,revision:n?.revision},i);g(`${o.idempotentReplay?"Replayed":"Queued"} operation ${o.operation.id} (${o.operation.state}).`)})}import{createHash as Lp}from"node:crypto";var jp=`let coldStart = true
447
457
  const initializedAt = new Date().toISOString()
448
458
  export const handler = async (event) => {
449
459
  const currentColdStart = coldStart
@@ -456,200 +466,200 @@ export const handler = async (event) => {
456
466
  body: JSON.stringify({ ok: statusCode === 200, service: 'ts-cloud-static-api', path, coldStart: currentColdStart, initializedAt })
457
467
  }
458
468
  }
459
- `;function ZY(G){let $=G;return String($?.code||$?.name||$?.statusCode||"")}function eX(G){return["NoSuchEntity","ResourceNotFoundException","404"].includes(ZY(G))}function XY(G){return["EntityAlreadyExists","ResourceAlreadyExistsException","ResourceConflictException","409"].includes(ZY(G))}function QY(G,$){let J=G?.Items;if(!J)return[];if(Array.isArray(J))return J;let Y=J[$];if(Y===void 0)return[];return Array.isArray(Y)?Y:[Y]}function T5(G){let $=G?.Aliases?.Items;if(Array.isArray($))return $.map(String);if(typeof $==="string")return[$];return[]}function oX(G){if(!/^[A-Z0-9]{8,32}$/.test(G.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(G.expectedAlias))throw Error("Expected CloudFront alias is invalid");if(!/^[A-Za-z0-9-_]{1,64}$/.test(G.functionName))throw Error("Lambda function name is invalid");let $=G.pathPattern?.startsWith("/")?G.pathPattern:`/${G.pathPattern||"api/*"}`;if($==="/"||$==="/*"||!$.endsWith("*"))throw Error("API path must be a non-default wildcard pattern such as /api/*");let J=G.originId||`${G.functionName}-url`,Y=G.roleName||`${G.functionName}-execution`;if(!/^[A-Za-z0-9_.-]{1,64}$/.test(Y))throw Error("IAM role name is invalid");let X=G.memorySize??256,Q=G.timeout??10,Z=G.logRetentionDays??14;if(X<128||X>10240)throw Error("Lambda memory must be between 128 and 10240 MB");if(Q<1||Q>900)throw Error("Lambda timeout must be between 1 and 900 seconds");return{region:G.region||"us-east-1",pathPattern:$,originId:J,roleName:Y,memorySize:X,timeout:Q,logRetentionDays:Z}}function $Q(G){let $=G.region||"us-east-1";return{cloudfront:new $$(G.profile),iam:new K$($,G.profile),lambda:new e$($,G.profile),logs:new o$($,G.profile),sts:new P0($,G.profile),sleep:(J)=>new Promise((Y)=>setTimeout(Y,J))}}async function WY(G,$){let J=oX(G),Y=$||$Q({profile:G.profile,region:J.region}),[X,Q,Z]=await Promise.all([Y.sts.getCallerIdentity(),Y.cloudfront.getDistribution(G.distributionId),Y.cloudfront.getDistributionConfig(G.distributionId)]);if(!X.Account)throw Error("AWS identity did not return an account ID");if(!Q.Enabled)throw Error(`CloudFront distribution ${G.distributionId} is disabled`);if(!(T5(Q).length?T5(Q):T5(Z.DistributionConfig)).includes(G.expectedAlias))throw Error(`Distribution ${G.distributionId} does not contain expected alias ${G.expectedAlias}`);let F,U=!0;try{F=await Y.iam.getRole({RoleName:J.roleName})}catch(l){if(!eX(l))throw l;U=!1}let M=F?.Arn||`arn:aws:iam::${X.Account}:role/${J.roleName}`,H=await Y.lambda.functionExists(G.functionName),j=H?await Y.lambda.getFunctionUrl(G.functionName):null,V=await Y.cloudfront.listOriginAccessControls(),T=`${G.functionName}-lambda-url`,A=V.some((l)=>l.Name===T&&l.OriginAccessControlOriginType==="lambda"),q=QY(Z.DistributionConfig.Origins,"Origin"),O=QY(Z.DistributionConfig.CacheBehaviors,"CacheBehavior"),L=q.some((l)=>String(l.Id)===J.originId),D=O.some((l)=>String(l.PathPattern)===J.pathPattern),P=[...!U?[`create IAM execution role ${J.roleName}`]:[`reconcile IAM execution policy ${J.roleName}`],...H?[`update Lambda ${G.functionName} code and configuration`]:[`create Lambda ${G.functionName}`],...!j?["create private AWS_IAM function URL"]:[],...!A?[`create Lambda origin access control ${T}`]:[],"grant CloudFront function-URL and invocation permissions",`${D?"reconcile":"add"} ${J.pathPattern} behavior without changing the default behavior`],b={command:`cloud cdn:origin:remove ${G.distributionId} FUNCTION_URL_HOST --id ${J.originId} --path '${J.pathPattern}' --apply --confirm 'remove:${G.distributionId}:${J.pathPattern}'`,removeFunction:!H,removeRole:!U},_={mode:G.apply?"apply":"plan",accountId:X.Account,identityArn:X.Arn,distribution:{id:Q.Id,arn:Q.ARN,alias:G.expectedAlias,domainName:Q.DomainName,status:Q.Status},function:{name:G.functionName,existed:H,runtime:"nodejs22.x",urlExisted:!!j,url:j?.FunctionUrl},role:{name:J.roleName,existed:U,arn:M},origin:{id:J.originId,pathPattern:J.pathPattern,existed:L,behaviorExisted:D,accessControlExisted:A},changes:P,rollback:b,applied:!1};if(!G.apply)return _;let S=`${G.distributionId}:${J.pathPattern}`;if(G.confirm!==S)throw Error(`Pass exact confirmation token ${S} to mutate the live distribution`);if(!F)F=await Y.iam.createRole({RoleName:J.roleName,Description:`Execution role for ${G.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 Y.iam.putRolePolicy({RoleName:J.roleName,PolicyName:"lambda-logs",PolicyDocument:JSON.stringify({Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:`arn:aws:logs:${J.region}:${X.Account}:log-group:/aws/lambda/${G.functionName}:*`}]})});try{await Y.logs.createLogGroup(`/aws/lambda/${G.functionName}`,{ManagedBy:"ts-cloud"})}catch(l){if(!XY(l))throw l}await Y.logs.putRetentionPolicy(`/aws/lambda/${G.functionName}`,J.logRetentionDays);let I=G.code||tX;if(H)await Y.lambda.updateFunctionCodeInline(G.functionName,I,"index.mjs"),await Y.lambda.waitForFunctionActive(G.functionName),await Y.lambda.updateFunctionConfiguration({FunctionName:G.functionName,Runtime:"nodejs22.x",Role:F.Arn,Handler:"index.handler",Description:`Private API origin for ${G.expectedAlias}`,Timeout:J.timeout,MemorySize:J.memorySize});else{let l;for(let s=0;s<8;s++)try{await Y.lambda.createFunctionWithCode({FunctionName:G.functionName,Runtime:"nodejs22.x",Role:F.Arn,Handler:"index.handler",Code:I,Filename:"index.mjs",Description:`Private API origin for ${G.expectedAlias}`,Timeout:J.timeout,MemorySize:J.memorySize}),l=void 0;break}catch(e){if(l=e,!String(e.message).toLowerCase().includes("role"))throw e;await Y.sleep(1000)}if(l)throw l}await Y.lambda.waitForFunctionActive(G.functionName);let g=j||await Y.lambda.createFunctionUrl({FunctionName:G.functionName,AuthType:"AWS_IAM",InvokeMode:"BUFFERED"});if(!g.FunctionUrl)throw Error("Lambda did not return a function URL");let f=new URL(g.FunctionUrl).hostname,d=await Y.cloudfront.findOrCreateOriginAccessControl(T,"lambda"),r=Q.ARN||`arn:aws:cloudfront::${X.Account}:distribution/${G.distributionId}`,W$=[{FunctionName:G.functionName,StatementId:"AllowCloudFrontFunctionUrl",Action:"lambda:InvokeFunctionUrl",Principal:"cloudfront.amazonaws.com",SourceArn:r,FunctionUrlAuthType:"AWS_IAM"},{FunctionName:G.functionName,StatementId:"AllowCloudFrontInvoke",Action:"lambda:InvokeFunction",Principal:"cloudfront.amazonaws.com",SourceArn:r,InvokedViaFunctionUrl:!0}];for(let l of W$)try{await Y.lambda.addPermission(l)}catch(s){if(!XY(s))throw s}return await Y.cloudfront.upsertExistingDistributionOrigin(G.distributionId,{id:J.originId,domainName:f,pathPattern:J.pathPattern,originAccessControlId:d.Id}),{..._,function:{..._.function,url:g.FunctionUrl},applied:!0}}async function zY(G){let $=`https://${G.alias}/`,J=`https://${G.alias}/api/health`,Y=await fetch($,{redirect:"follow",headers:{"cache-control":"no-cache"}}),X=new Uint8Array(await Y.arrayBuffer()),Q=iX("sha256").update(X).digest("hex"),Z=Date.now(),z=await fetch(J,{redirect:"follow",headers:{"cache-control":"no-cache"}}),F=Date.now()-Z,U=await z.text(),M=U;try{M=JSON.parse(U)}catch{}let H;if(G.logs&&G.functionName){let j=await G.logs.filterLogEvents({logGroupName:`/aws/lambda/${G.functionName}`,startTime:Z-60000,filterPattern:'"Init Duration"',limit:25});for(let V of j.events||[]){let T=V.message?.match(/Init Duration:\s*([0-9.]+)\s*ms/i);if(T)H=Number(T[1])}}return{frontend:{url:$,status:Y.status,sha256:Q,unchanged:G.expectedFrontendSha256?Q===G.expectedFrontendSha256:void 0,bytes:X.byteLength},api:{url:J,status:z.status,latencyMs:F,healthy:z.ok&&!!M?.ok,body:M},coldStart:{observed:H!==void 0,initDurationMs:H},verifiedAt:(G.now?.()||new Date).toISOString()}}function FY(G){let $=(G.memoryMb||256)/1024,J=Math.max(0,G.requests-(G.includeFreeTier===!1?0:1e6)),Y=Math.max(0,G.requests*(G.averageDurationMs/1000)*$-(G.includeFreeTier===!1?0:400000)),X=J/1e6*0.2+Y*0.0000166667,Q=9.010025,Z=16.425;return{lambdaUsd:Number(X.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 O5(G){G.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($,J,Y)=>{try{let X=await WY({distributionId:$,expectedAlias:J,functionName:Y.function||"ts-cloud-static-api",pathPattern:Y.path,originId:Y.originId,roleName:Y.role,region:Y.region,profile:Y.profile,memorySize:Number(Y.memory),timeout:Number(Y.timeout),logRetentionDays:Number(Y.retention),apply:!!Y.apply,confirm:Y.confirm});if(W(JSON.stringify(X,null,2)),X.applied)B("Private API origin submitted to CloudFront. Run cdn:api:verify after propagation.");else W(`Plan only. Re-run with --apply --confirm '${$}:${X.origin.pathPattern}'.`)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}}),G.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($,J)=>{try{let Y=new o$(J.region||"us-east-1",J.profile),X=await zY({alias:$,expectedFrontendSha256:J.frontendSha256,logs:Y,functionName:J.function||"ts-cloud-static-api"});if(W(JSON.stringify(X,null,2)),!X.api.healthy||X.frontend.unchanged===!1)throw Error("Static API verification failed");B("Static frontend and Lambda API verification passed.")}catch(Y){K(Y instanceof Error?Y.message:String(Y)),process.exitCode=1}}),G.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(($,J)=>{let Y=FY({requests:Number($),averageDurationMs:Number(J.duration),memoryMb:Number(J.memory),includeFreeTier:J.freeTier!==!1});W(JSON.stringify(Y,null,2))}),G.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($,J,Y)=>{try{let X=new $$(Y.profile),Q={id:Y.id??"api-backend",domainName:J,pathPattern:Y.path??"/api/*",replaceExisting:!!Y.replace},Z=await X.upsertExistingDistributionOrigin($,{...Q,dryRun:!0});if(W(JSON.stringify(Z,null,2)),!Y.apply){W("Dry run only. Re-run with --apply and the exact --confirm token.");return}let z=`${$}:${Z.pathPattern}`;if(Y.confirm!==z)throw Error(`Pass --confirm "${z}" to apply this live distribution change`);let F=await X.upsertExistingDistributionOrigin($,Q);B(F.changed?"Backend origin patch submitted.":"Backend origin already matches."),W(JSON.stringify(F,null,2))}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}}),G.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($,J,Y)=>{try{let X=new $$(Y.profile),Q={id:Y.id??"api-backend",domainName:J,pathPattern:Y.path??"/api/*"},Z=await X.removeExistingDistributionOrigin($,{...Q,dryRun:!0});if(W(JSON.stringify(Z,null,2)),!Y.apply){W("Dry run only. Re-run with --apply and the exact --confirm token.");return}let z=`remove:${$}:${Z.pathPattern}`;if(Y.confirm!==z)throw Error(`Pass --confirm "${z}" to apply this live distribution rollback`);let F=await X.removeExistingDistributionOrigin($,Q);B(F.changed?"Backend behavior rollback submitted.":"Backend behavior was already absent."),W(JSON.stringify(F,null,2))}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}}),G.command("cdn:list","List all CloudFront distributions").action(async()=>{R("CloudFront Distributions");try{let $=await x(),J=new $$,Y=new E("Fetching distributions...");Y.start();let X=await J.listDistributions();if(Y.succeed(`Found ${X.length} distribution(s)`),X.length===0){W("No CloudFront distributions found"),W("Use `cloud cdn:create` to create a new distribution");return}w(["ID","Domain","Status","Enabled","Aliases"],X.map((Q)=>[Q.Id||"N/A",Q.DomainName||"N/A",Q.Status||"N/A",Q.Enabled?"Yes":"No",(Q.Aliases?.Items?.length||0).toString()]))}catch($){K(`Failed to list distributions: ${$.message}`),process.exit(1)}}),G.command("cdn:status <distributionId>","Get CloudFront distribution status").action(async($)=>{R(`CloudFront Distribution: ${$}`);try{let J=new $$,Y=new E("Fetching distribution details...");Y.start();let X=await J.getDistribution($);if(Y.succeed("Distribution details loaded"),W(`
460
- Distribution Information:`),W(` ID: ${X.Id}`),W(` Domain: ${X.DomainName}`),W(` Status: ${X.Status}`),W(` ARN: ${X.ARN}`),W(` Enabled: ${X.Enabled?"Yes":"No"}`),X.Aliases?.Items?.length)W(` Aliases: ${X.Aliases.Items.join(", ")}`);let Q=await J.getDistributionConfig($);W(`
461
- Origins:`);let Z=Q.DistributionConfig?.Origins?.Items||[];if(Array.isArray(Z))for(let z of Z)W(` - ${z.Id}: ${z.DomainName}`);if(Q.DistributionConfig?.DefaultCacheBehavior){let z=Q.DistributionConfig.DefaultCacheBehavior;W(`
462
- Default Cache Behavior:`),W(` Target Origin: ${z.TargetOriginId}`),W(` Viewer Protocol: ${z.ViewerProtocolPolicy}`)}}catch(J){K(`Failed to get distribution: ${J.message}`),process.exit(1)}}),G.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($)=>{R("Create CloudFront Distribution");try{if(!$.bucket)K("--bucket is required"),W("Example: cloud cdn:create --bucket my-bucket --region us-east-1"),process.exit(1);let J=new $$;if(W(`Bucket: ${$.bucket}`),W(`Region: ${$.region}`),$.alias)W(`Alias: ${$.alias}`);if(!await k(`
463
- Create this distribution?`,!0)){W("Operation cancelled");return}let X=new E("Setting up Origin Access Control...");X.start();let Q=await J.findOrCreateOriginAccessControl(`OAC-${$.bucket}`);if(Q.isNew)X.text="Created new Origin Access Control";X.text="Creating distribution...";let Z=$.alias?[$.alias]:[],z=await J.createDistributionForS3({bucketName:$.bucket,bucketRegion:$.region,originAccessControlId:Q.Id,aliases:Z,certificateArn:$.certificate,comment:$.comment||"Created by ts-cloud CLI"});X.succeed("Distribution created"),B(`
464
- Distribution ID: ${z.Id}`),W(`Domain: ${z.DomainName}`),W(`Status: ${z.Status}`),W(`
465
- Note: Distribution deployment may take 15-30 minutes.`),W(`
466
- IMPORTANT: Update your S3 bucket policy to allow CloudFront access:`);let F=$$.getS3BucketPolicyForCloudFront($.bucket,z.ARN);W(JSON.stringify(F,null,2))}catch(J){K(`Failed to create distribution: ${J.message}`),process.exit(1)}}),G.command("cdn:invalidate <distributionId>","Invalidate CloudFront cache").option("--paths <paths>","Paths to invalidate (comma-separated)",{default:"/*"}).action(async($,J)=>{R("Invalidate CloudFront Cache");try{let Y=new $$,X=J.paths.split(",").map((F)=>F.trim());if(W(`Distribution: ${$}`),W(`Paths: ${X.join(", ")}`),!await k(`
467
- Create invalidation?`,!0)){W("Operation cancelled");return}let Z=new E("Creating invalidation...");Z.start();let z=await Y.createInvalidation({distributionId:$,paths:X,callerReference:`cli-${Date.now()}`});Z.succeed("Invalidation created"),B(`
468
- Invalidation ID: ${z.Id}`),W(`Status: ${z.Status}`),W(`
469
- Note: Invalidation typically completes in 5-10 minutes.`)}catch(Y){K(`Failed to create invalidation: ${Y.message}`),process.exit(1)}}),G.command("cdn:disable <distributionId>","Disable a CloudFront distribution").action(async($)=>{R("Disable CloudFront Distribution");try{let J=new $$;if(N(`This will disable distribution: ${$}`),W("The distribution will stop serving content."),!await k(`
470
- Disable this distribution?`,!1)){W("Operation cancelled");return}let X=new E("Disabling distribution...");X.start(),await J.disableDistribution($),X.succeed("Distribution disabled"),W(`
471
- Note: Changes may take 15-30 minutes to propagate.`),W("To delete the distribution, wait for it to be fully disabled first.")}catch(J){K(`Failed to disable distribution: ${J.message}`),process.exit(1)}}),G.command("cdn:delete <distributionId>","Delete a CloudFront distribution").action(async($)=>{R("Delete CloudFront Distribution");try{let J=new $$;if(N(`This will permanently delete distribution: ${$}`),N("The distribution must be disabled first."),!await k(`
472
- Delete this distribution?`,!1)){W("Operation cancelled");return}let X=new E("Checking distribution status...");X.start();let Q=await J.getDistribution($);if(Q.Status!=="Deployed"||Q.Enabled){X.fail("Distribution must be disabled and deployed before deletion"),W('\nRun `cloud cdn:disable` first and wait for status to be "Deployed"');return}X.text="Deleting distribution...",await J.deleteDistribution($),X.succeed("Distribution deleted")}catch(J){K(`Failed to delete distribution: ${J.message}`),process.exit(1)}})}import{readdirSync as JQ,statSync as YQ}from"node:fs";import{join as jY,relative as GQ}from"node:path";async function _$(G){let $=await x(),J=u(process.cwd(),$),Y=G??Object.keys($.environments??{})[0]??"production",X=J.environments.get(Y);if(!X)throw J.store.close(),Error(`Environment ${Y} was not found.`);let Q=J.store.getActorByExternalId("system","cli")??J.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"}),Z=new KY(J.store),z=new UY(Z,[new MY,new HY(jY(process.cwd(),".ts-cloud","volumes"))]);return{controlPlane:J,environmentRecord:X,actor:Q,store:Z,service:z}}function XQ(G){return G.map(($)=>[$.name,`${$.provider}/${$.type}`,$.status,$.capacityBytes==null?"—":A1($.capacityBytes),$.usedBytes==null?"—":A1($.usedBytes),String($.attachments.filter((J)=>J.observedState==="attached").length),$.backupState,$.orphaned?"orphan":"managed"])}function w5(G){G.command("storage:list","List all S3 buckets").option("--region <region>","AWS region").action(async(J)=>{R("S3 Buckets");try{let Y=await x(),X=J.region||Y.project.region||"us-east-1",Q=new Z$(X),Z=new E("Fetching buckets...");Z.start();let F=(await Q.listBuckets()).Buckets||[];if(Z.succeed(`Found ${F.length} bucket(s)`),F.length===0){W("No S3 buckets found"),W("Use `cloud storage:create` to create a new bucket");return}w(["Name","Created"],F.map((U)=>[U.Name||"N/A",U.CreationDate?new Date(U.CreationDate).toLocaleDateString():"N/A"]))}catch(Y){K(`Failed to list buckets: ${Y.message}`),process.exit(1)}}),G.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(J,Y)=>{R("Create S3 Bucket");try{let X=new Z$(Y.region);if(W(`Bucket name: ${J}`),W(`Region: ${Y.region}`),W(`Public access: ${Y.public?"Yes":"No"}`),W(`Versioning: ${Y.versioning?"Yes":"No"}`),!await k(`
473
- Create this bucket?`,!0)){W("Operation cancelled");return}let Z=new E("Creating bucket...");if(Z.start(),await X.createBucket(J),!Y.public)Z.text="Configuring public access block...",await X.putPublicAccessBlock(J,{BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!0,RestrictPublicBuckets:!0});if(Y.versioning)Z.text="Enabling versioning...",await X.putBucketVersioning(J,"Enabled");Z.succeed("Bucket created"),B(`
474
- Bucket: ${J}`),W(`Region: ${Y.region}`),W(`URL: s3://${J}`)}catch(X){K(`Failed to create bucket: ${X.message}`),process.exit(1)}}),G.command("storage:delete <name>","Delete an S3 bucket").option("--force","Delete all objects first").action(async(J,Y)=>{R("Delete S3 Bucket");try{let X=new Z$("us-east-1");if(N(`This will permanently delete bucket: ${J}`),Y.force)N("All objects in the bucket will be deleted!");if(!await k(`
475
- Delete this bucket?`,!1)){W("Operation cancelled");return}let Z=new E("Deleting bucket...");if(Z.start(),Y.force)Z.text="Emptying bucket...",await X.emptyBucket(J);await X.deleteBucket(J),Z.succeed("Bucket deleted")}catch(X){K(`Failed to delete bucket: ${X.message}`),process.exit(1)}}),G.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(J,Y,X)=>{R("Sync to S3");try{let F=function(j,V){let T=JQ(j,{withFileTypes:!0});for(let A of T){let q=jY(j,A.name);if(A.isDirectory())F(q,V);else{let O=GQ(V,q),L=X.prefix?`${X.prefix}/${O}`:O,D=YQ(q);z.push({path:q,key:L,size:D.size})}}},Q=new Z$("us-east-1");if(W(`Source: ${J}`),W(`Destination: s3://${Y}/${X.prefix||""}`),X.dryRun)W("Dry run mode - no changes will be made");let Z=new E("Scanning files...");Z.start();let z=[];if(F(J,J),Z.succeed(`Found ${z.length} local file(s)`),z.length===0){W("No files to sync");return}W(`
476
- Files to sync:`);for(let j of z.slice(0,10))W(` ${j.key} (${(j.size/1024).toFixed(2)} KB)`);if(z.length>10)W(` ... and ${z.length-10} more`);if(X.dryRun){W(`
477
- Dry run complete - no changes made`);return}if(!await k(`
478
- Sync these files?`,!0)){W("Operation cancelled");return}let M=new E("Uploading files...");M.start();let H=0;for(let j of z){M.text=`Uploading ${j.key}... (${H+1}/${z.length})`;let T=await Bun.file(j.path).arrayBuffer();await Q.putObject({bucket:Y,key:j.key,body:Buffer.from(T),contentType:QQ(j.key)}),H++}if(M.succeed(`Uploaded ${H} file(s)`),X.delete){let j=new E("Checking for files to delete...");j.start();let V=await Q.listObjects({bucket:Y,prefix:X.prefix}),T=new Set(z.map((q)=>q.key)),A=V.objects.filter((q)=>q.Key&&!T.has(q.Key)).map((q)=>q.Key);if(A.length>0){j.text=`Deleting ${A.length} remote file(s)...`;for(let q of A)await Q.deleteObject(Y,q);j.succeed(`Deleted ${A.length} remote file(s)`)}else j.succeed("No files to delete")}B(`
479
- Sync complete!`)}catch(Q){K(`Failed to sync: ${Q.message}`),process.exit(1)}}),G.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(J,Y)=>{R("S3 Bucket Policy");try{let X=new Z$("us-east-1");if(Y.delete){if(N(`This will delete the policy for bucket: ${J}`),!await k(`
480
- Delete bucket policy?`,!1)){W("Operation cancelled");return}let z=new E("Deleting policy...");z.start(),await X.deleteBucketPolicy(J),z.succeed("Policy deleted");return}if(Y.publicRead){if(N(`This will make bucket ${J} publicly readable!`),!await k(`
481
- Set public read policy?`,!1)){W("Operation cancelled");return}let z=new E("Setting policy...");z.start();let F={Version:"2012-10-17",Statement:[{Sid:"PublicReadGetObject",Effect:"Allow",Principal:"*",Action:"s3:GetObject",Resource:`arn:aws:s3:::${J}/*`}]};await X.putBucketPolicy(J,F),z.succeed("Public read policy set");return}if(Y.set){let Z=new E("Setting policy...");Z.start();let F=await Bun.file(Y.set).text();await X.putBucketPolicy(J,F),Z.succeed("Policy set");return}let Q=new E("Fetching policy...");Q.start();try{let Z=await X.getBucketPolicy(J);Q.succeed("Policy loaded"),W(`
482
- Bucket Policy:`),console.log(JSON.stringify(Z||{},null,2))}catch(Z){if(Z.message?.includes("NoSuchBucketPolicy"))Q.succeed("No policy set"),W("This bucket has no policy configured.");else throw Z}}catch(X){K(`Failed to manage policy: ${X.message}`),process.exit(1)}}),G.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(J,Y)=>{R(`Objects in ${J}`);try{let X=new Z$("us-east-1"),Q=new E("Listing objects...");Q.start();let Z=await X.listObjects({bucket:J,prefix:Y.prefix,maxKeys:Number.parseInt(Y.limit||"100")}),z=Z.objects;if(Q.succeed(`Found ${z.length} object(s)${Z.nextContinuationToken?" (truncated)":""}`),z.length===0){W("No objects found");return}if(w(["Key","Size","Last Modified"],z.map((F)=>[F.Key||"N/A",A1(F.Size||0),F.LastModified?new Date(F.LastModified).toLocaleString():"N/A"])),Z.nextContinuationToken)W(`
483
- More objects available. Use --limit to see more.`)}catch(X){K(`Failed to list objects: ${X.message}`),process.exit(1)}});let $=(J,Y)=>G.command(J,Y).option("--env <environment>","Dashboard environment");$("volume:list","List persistent volumes, consumers, usage, and protection").option("--json","Print structured JSON").action(async(J)=>{let Y=await _$(J.env);try{let X=Y.store.inventory(Y.controlPlane.project.id,Y.environmentRecord.id);if(J.json)console.log(JSON.stringify(X,null,2));else w(["Name","Driver","Status","Capacity","Used","Consumers","Backup","Ownership"],XQ(X))}catch(X){K(X instanceof Error?X.message:String(X))}finally{Y.controlPlane.store.close()}}),$("volume:show <name>","Inspect a persistent volume and its dependency graph").option("--json","Print structured JSON").action(async(J,Y)=>{let X=await _$(Y.env);try{let Q=X.store.inventory(X.controlPlane.project.id,X.environmentRecord.id).find((Z)=>Z.id===J||Z.name===J);if(!Q)throw Error(`Volume ${J} was not found.`);if(Y.json)console.log(JSON.stringify(Q,null,2));else w(["Property","Value"],[["ID",Q.id],["Driver",`${Q.provider}/${Q.type}`],["Provider ID",Q.providerId??"—"],["Status",Q.status],["Capacity",Q.capacityBytes==null?"—":A1(Q.capacityBytes)],["Used",Q.usedBytes==null?"—":A1(Q.usedBytes)],["Backup",Q.backupState]]),w(["Resource","Target","Mode","State"],Q.attachments.map((Z)=>[Z.resourceId,Z.targetPath,Z.readOnly?"read only":"read/write",Z.observedState]))}catch(Q){K(Q instanceof Error?Q.message:String(Q))}finally{X.controlPlane.store.close()}}),$("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(J,Y)=>{let X=await _$(Y.env);try{let Q=Y.driver==="server";if(!Q&&Y.driver!=="docker")throw Error("--driver must be docker or server.");let Z=X.service.create({organizationId:X.controlPlane.organization.id,projectId:X.controlPlane.project.id,environmentId:X.environmentRecord.id,name:J,provider:Q?"server":"docker",type:Q?"server_path":"docker",capacityBytes:Y.capacityBytes?Number(Y.capacityBytes):void 0,encrypted:!!Y.encrypted,actorId:X.actor.id});B(`Volume create queued: ${Z.operation.id}`)}catch(Q){K(Q instanceof Error?Q.message:String(Q))}finally{X.controlPlane.store.close()}}),$("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(J,Y,X,Q)=>{let Z=await _$(Q.env);try{let z=Z.store.list({projectId:Z.controlPlane.project.id,environmentId:Z.environmentRecord.id}).find((M)=>M.id===J||M.name===J),F=Z.controlPlane.store.listResources(Z.controlPlane.project.id,Z.environmentRecord.id).find((M)=>M.id===Y||M.slug===Y);if(!z||!F)throw Error("Volume or resource was not found in the selected environment.");let U=Z.service.attach(z.id,{resourceId:F.id,targetPath:X,readOnly:!!Q.readOnly,uid:Q.uid?Number(Q.uid):void 0,gid:Q.gid?Number(Q.gid):void 0,mode:Q.mode,actorId:Z.actor.id});B(`Volume attachment queued: ${U.operation.id}`)}catch(z){K(z instanceof Error?z.message:String(z))}finally{Z.controlPlane.store.close()}}),$("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(J,Y,X)=>{let Q=await _$(X.env);try{let Z=Q.store.list({projectId:Q.controlPlane.project.id,environmentId:Q.environmentRecord.id}).find((F)=>F.id===J||F.name===J);if(!Z)throw Error("Volume was not found.");let z=Q.service.detach(Z.id,Y,{drained:!0,unmounted:!0,force:!!X.force,confirmation:X.confirm,actorId:Q.actor.id});B(`Volume detach queued: ${z.operation.id}`)}catch(Z){K(Z instanceof Error?Z.message:String(Z))}finally{Q.controlPlane.store.close()}}),$("volume:resize <volume> <bytes>","Grow a capable persistent volume").option("--drained","Confirm offline workload drain").action(async(J,Y,X)=>{let Q=await _$(X.env);try{let Z=Q.store.list({projectId:Q.controlPlane.project.id,environmentId:Q.environmentRecord.id}).find((F)=>F.id===J||F.name===J);if(!Z)throw Error("Volume was not found.");let z=Q.service.resize(Z.id,Number(Y),{drained:!!X.drained,actorId:Q.actor.id});B(`Volume resize queued: ${z.operation.id}`)}catch(Z){K(Z instanceof Error?Z.message:String(Z))}finally{Q.controlPlane.store.close()}}),$("volume:snapshot <volume>","Create a provider snapshot when supported").action(async(J,Y)=>{let X=await _$(Y.env);try{let Q=X.store.list({projectId:X.controlPlane.project.id,environmentId:X.environmentRecord.id}).find((z)=>z.id===J||z.name===J);if(!Q)throw Error("Volume was not found.");let Z=X.service.snapshot(Q.id,{actorId:X.actor.id});B(`Volume snapshot queued: ${Z.operation.id}`)}catch(Q){K(Q instanceof Error?Q.message:String(Q))}finally{X.controlPlane.store.close()}}),$("volume:restore <snapshot> <name>","Restore a snapshot into a replacement volume").action(async(J,Y,X)=>{let Q=await _$(X.env);try{let Z=Q.service.restore(J,{name:Y,actorId:Q.actor.id});B(`Volume restore queued: ${Z.operation.id}`)}catch(Z){K(Z instanceof Error?Z.message:String(Z))}finally{Q.controlPlane.store.close()}}),$("volume:discover","Discover provider volumes without adopting or deleting them").action(async(J)=>{let Y=await _$(J.env);try{let X=await Y.service.discover({organizationId:Y.controlPlane.organization.id,projectId:Y.controlPlane.project.id,environmentId:Y.environmentRecord.id,actorId:Y.actor.id});B(`Discovered ${X.filter((Q)=>Q.status==="orphaned").length} orphan volume(s).`)}catch(X){K(X instanceof Error?X.message:String(X))}finally{Y.controlPlane.store.close()}}),$("volume:adopt <volume> <name>","Adopt a discovered orphan under a managed name").action(async(J,Y,X)=>{let Q=await _$(X.env);try{Q.service.adopt(J,{name:Y,actorId:Q.actor.id}),B(`Adopted ${Y}.`)}catch(Z){K(Z instanceof Error?Z.message:String(Z))}finally{Q.controlPlane.store.close()}}),$("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(J,Y)=>{let X=await _$(Y.env);try{let Q=X.store.list({projectId:X.controlPlane.project.id,environmentId:X.environmentRecord.id}).find((z)=>z.id===J||z.name===J);if(!Q)throw Error("Volume was not found.");let Z=X.service.delete(Q.id,{recentAuthAt:new Date().toISOString(),confirmation:Y.confirm,backupOverride:!!Y.withoutBackup,backupOverrideConfirmation:Y.backupConfirm,actorId:X.actor.id});B(`Volume deletion queued: ${Z.operation.id}`)}catch(Q){K(Q instanceof Error?Q.message:String(Q))}finally{X.controlPlane.store.close()}})}function QQ(G){let $=G.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"}[$||""]||"application/octet-stream"}function A1(G){if(G===0)return"0 B";let $=1024,J=["B","KB","MB","GB","TB"],Y=Math.floor(Math.log(G)/Math.log($));return`${Number.parseFloat((G/$**Y).toFixed(2))} ${J[Y]}`}var BY=["aws","backblaze","hetzner"];function VY(G,$){let J=G.indexOf(":");if(J===-1)throw Error(`--${$} must be in the form <provider:bucket> (e.g. aws:my-bucket), got "${G}"`);let Y=G.slice(0,J),X=G.slice(J+1);if(!BY.includes(Y))throw Error(`Unknown provider "${Y}" for --${$}. Supported: ${BY.join(", ")}`);if(!X)throw Error(`--${$} is missing a bucket name (expected <provider:bucket>)`);return{provider:Y,bucket:X}}function AY(G){if(!G)return;let $=G.split(",").map((J)=>J.trim()).filter(Boolean);return $.length>0?$:void 0}function S5(G){G.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($)=>{R("Migrate Object Storage");try{if(!$.from||!$.to)throw Error("Both --from <provider:bucket> and --to <provider:bucket> are required");let J=VY($.from,"from"),Y=VY($.to,"to"),X=AY($.include),Q=AY($.exclude),Z=Number.parseInt($.concurrency||"8",10),z={provider:J.provider,bucket:J.bucket,region:$.fromRegion,endpoint:$.fromEndpoint,prefix:$.fromPrefix},F={provider:Y.provider,bucket:Y.bucket,region:$.toRegion,endpoint:$.toEndpoint,prefix:$.toPrefix};if(W(`Source: ${J.provider}:${J.bucket}${$.fromPrefix?`/${$.fromPrefix}`:""}`),W(`Destination: ${Y.provider}:${Y.bucket}${$.toPrefix?`/${$.toPrefix}`:""}`),X)W(`Include: ${X.join(", ")}`);if(Q)W(`Exclude: ${Q.join(", ")}`);if(W(`Concurrency: ${Z}`),$.dryRun)N("Dry run — no objects will be written");if($.deleteExtraneous&&!$.dryRun)N("Delete-extraneous ON — destination keys not in the source will be removed");let U=new E("Listing source objects...");U.start();let M=0,H=await P5({from:z,to:F,include:X,exclude:Q,dryRun:$.dryRun,force:$.force,deleteExtraneous:$.deleteExtraneous,concurrency:Z,verify:$.verify,onProgress:(j)=>{if(j.index-M>=1||j.index===j.total){M=j.index;let V=j.action==="planned"?"plan":j.action;U.text=`[${j.index}/${j.total}] ${V}: ${j.key}`}}});if(U.succeed($.dryRun?"Plan ready":"Migration complete"),$.dryRun&&H.plan){W(`
484
- Would copy ${H.plan.length} object(s):`);for(let j of H.plan.slice(0,50))W(` ${j.key} -> ${j.destKey} (${q1(j.size)})`);if(H.plan.length>50)W(` ... and ${H.plan.length-50} more`)}if(H.excludedKeys.length>0){W(`
485
- EXCLUDED ${H.excludedKeys.length} key(s) (deliberately not migrated):`);for(let j of H.excludedKeys.slice(0,50))W(` ${j}`);if(H.excludedKeys.length>50)W(` ... and ${H.excludedKeys.length-50} more`)}if(H.deleted.length>0){N(`
486
- Deleted ${H.deleted.length} extraneous destination key(s):`);for(let j of H.deleted.slice(0,50))N(` ${j}`)}if(W(`
487
- Summary:`),$.dryRun)W(` would copy: ${H.plan?.length??0}`);else B(` copied: ${H.copied} (${q1(H.bytesCopied)})`),W(` skipped: ${H.skipped} (already present)`);if(W(` excluded: ${H.excluded}`),H.deleted.length>0)W(` deleted: ${H.deleted.length}`);if(H.errors.length>0){K(` errors: ${H.errors.length}`);for(let j of H.errors.slice(0,20))K(` ${j.key}: ${j.message}`)}if(H.verification){let j=H.verification;if(j.ok)B(`
488
- Verification PASSED — ${j.matched} object(s) present at destination with matching sizes`);else{K(`
489
- Verification FAILED — ${j.missing.length} missing, ${j.sizeMismatches.length} size mismatch(es)`);for(let V of j.missing.slice(0,20))K(` missing: ${V}`);for(let V of j.sizeMismatches.slice(0,20))K(` size: ${V.key} expected ${V.expected} got ${V.actual}`)}}if(H.errors.length>0||H.verification?.ok===!1)process.exit(1)}catch(J){K(`Migration failed: ${J.message}`),process.exit(1)}})}function _5(G){G.command("cache:list","List all ElastiCache clusters").option("--region <region>","AWS region").action(async($)=>{R("ElastiCache Clusters");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new m$(Y),Q=new E("Fetching clusters...");Q.start();let z=(await X.describeCacheClusters()).CacheClusters||[];if(Q.succeed(`Found ${z.length} cluster(s)`),z.length===0){W("No ElastiCache clusters found"),W("Use `cloud cache:create` to create a new cluster");return}w(["Cluster ID","Engine","Node Type","Nodes","Status"],z.map((F)=>[F.CacheClusterId||"N/A",`${F.Engine||"N/A"} ${F.EngineVersion||""}`,F.CacheNodeType||"N/A",(F.NumCacheNodes||0).toString(),F.CacheClusterStatus||"N/A"]))}catch(J){K(`Failed to list clusters: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("Create ElastiCache Cluster");try{let Y=new m$(J.region);if(W(`Cluster ID: ${$}`),W(`Engine: ${J.engine}`),W(`Node Type: ${J.nodeType}`),W(`Number of Nodes: ${J.nodes}`),W(`Region: ${J.region}`),!await k(`
490
- Create this cluster?`,!0)){W("Operation cancelled");return}let Q=new E("Creating cluster...");Q.start(),await Y.createCacheCluster({cacheClusterId:$,engine:J.engine,cacheNodeType:J.nodeType,numCacheNodes:Number.parseInt(J.nodes),engineVersion:J.version}),Q.succeed("Cluster creation initiated"),B(`
491
- Cluster: ${$}`),W("Status: creating"),W(`
492
- Note: Cluster creation may take several minutes.`),W("Use `cloud cache:list` to check status.")}catch(Y){K(`Failed to create cluster: ${Y.message}`),process.exit(1)}}),G.command("cache:delete <clusterId>","Delete an ElastiCache cluster").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Delete ElastiCache Cluster");try{let Y=new m$(J.region);if(N(`This will permanently delete cluster: ${$}`),N("All data in the cluster will be lost!"),!await k(`
493
- Delete this cluster?`,!1)){W("Operation cancelled");return}let Q=new E("Deleting cluster...");Q.start(),await Y.deleteCacheCluster($),Q.succeed("Cluster deletion initiated"),W(`
494
- Note: Cluster deletion may take several minutes.`)}catch(Y){K(`Failed to delete cluster: ${Y.message}`),process.exit(1)}}),G.command("cache:stats <clusterId>","Show ElastiCache cluster statistics").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R(`ElastiCache Stats: ${$}`);try{let Y=new m$(J.region),X=new E("Fetching cluster details...");X.start();let Z=(await Y.describeCacheClusters($)).CacheClusters?.[0];if(!Z){X.fail("Cluster not found");return}if(X.succeed("Cluster details loaded"),W(`
495
- Cluster Information:`),W(` Cluster ID: ${Z.CacheClusterId}`),W(` Engine: ${Z.Engine} ${Z.EngineVersion}`),W(` Node Type: ${Z.CacheNodeType}`),W(` Status: ${Z.CacheClusterStatus}`),W(` Nodes: ${Z.NumCacheNodes}`),W(` Availability Zone: ${Z.PreferredAvailabilityZone||"Not set"}`),Z.CacheNodes&&Z.CacheNodes.length>0){W(`
496
- Cache Nodes:`);for(let z of Z.CacheNodes)if(W(` - ${z.CacheNodeId}: ${z.CacheNodeStatus}`),z.Endpoint)W(` Endpoint: ${z.Endpoint.Address}:${z.Endpoint.Port}`)}}catch(Y){K(`Failed to get cluster stats: ${Y.message}`),process.exit(1)}}),G.command("cache:flush <clusterId>","Flush all data from a Redis cluster").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Flush ElastiCache Cluster");try{let Y=new m$(J.region),X=new E("Getting cluster endpoint...");X.start();let Z=(await Y.describeCacheClusters($)).CacheClusters?.[0];if(!Z){X.fail("Cluster not found");return}if(Z.Engine!=="redis"){X.fail("Flush is only supported for Redis clusters"),W("For Memcached, items expire automatically based on TTL.");return}X.stop();let z=Z.CacheNodes?.[0]?.Endpoint;if(!z){K("Could not determine cluster endpoint");return}if(N(`This will delete ALL data in cluster: ${$}`),N(`Endpoint: ${z.Address}:${z.Port}`),N("This action cannot be undone!"),!await k(`
497
- Flush all data?`,!1)){W("Operation cancelled");return}W(`
498
- To flush the Redis cluster, connect and run FLUSHALL:`),W(` redis-cli -h ${z.Address} -p ${z.Port} FLUSHALL`),W(`
499
- Note: Direct FLUSHALL via AWS API is not supported.`),W("You must connect to the cluster directly to execute this command.")}catch(Y){K(`Failed to get cluster info: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Reboot ElastiCache Cluster");try{let Y=new m$(J.region),X=new E("Getting cluster info...");X.start();let Z=(await Y.describeCacheClusters($)).CacheClusters?.[0];if(!Z){X.fail("Cluster not found");return}X.stop();let z=J.node?[J.node]:Z.CacheNodes?.map((M)=>M.CacheNodeId).filter(Boolean)||[];if(z.length===0){K("No nodes found to reboot");return}if(N(`This will reboot the following nodes: ${z.join(", ")}`),N("The cluster may be temporarily unavailable during reboot."),!await k(`
500
- Reboot these nodes?`,!1)){W("Operation cancelled");return}let U=new E("Rebooting nodes...");U.start(),await Y.rebootCacheCluster($,z),U.succeed("Reboot initiated"),W(`
501
- Note: Reboot may take several minutes to complete.`),W("Use `cloud cache:stats` to check status.")}catch(Y){K(`Failed to reboot cluster: ${Y.message}`),process.exit(1)}})}function b5(G){G.command("queue:list","List all SQS queues").option("--region <region>","AWS region").action(async($)=>{R("SQS Queues");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new X0(Y),Q=new E("Fetching queues...");Q.start();let z=(await X.listQueues()).QueueUrls||[];if(Q.succeed(`Found ${z.length} queue(s)`),z.length===0){W("No SQS queues found"),W("Use `cloud queue:create` to create a new queue");return}let F=[];for(let U of z)try{let M=await X.getQueueAttributes(U),H=U.split("/").pop()||U;F.push({url:U,name:H,messages:M.Attributes?.ApproximateNumberOfMessages||"0",type:M.Attributes?.FifoQueue==="true"?"FIFO":"Standard"})}catch{let M=U.split("/").pop()||U;F.push({url:U,name:M,messages:"N/A",type:"Unknown"})}w(["Queue Name","Messages","Type"],F.map((U)=>[U.name,U.messages,U.type]))}catch(J){K(`Failed to list queues: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("Create SQS Queue");try{let Y=new X0(J.region),X=J.fifo&&!$.endsWith(".fifo")?`${$}.fifo`:$;if(W(`Queue name: ${X}`),W(`Type: ${J.fifo?"FIFO":"Standard"}`),W(`Visibility timeout: ${J.visibility} seconds`),W(`Message retention: ${J.retention} days`),J.dlq)W(`Dead letter queue: ${J.dlq}`),W(`Max retries: ${J.maxRetries}`);if(!await k(`
502
- Create this queue?`,!0)){W("Operation cancelled");return}let Z=new E("Creating queue...");Z.start();let z=await Y.createQueue({queueName:X,fifo:J.fifo,visibilityTimeout:Number.parseInt(J.visibility),messageRetentionPeriod:Number.parseInt(J.retention)*24*60*60,contentBasedDeduplication:J.fifo?!0:void 0,deadLetterTargetArn:J.dlq,maxReceiveCount:J.dlq?Number.parseInt(J.maxRetries):void 0});Z.succeed("Queue created"),B(`
503
- Queue URL: ${z.QueueUrl}`),W(`
504
- To send a message:`),W(` cloud queue:send ${X} --message "Hello World"`)}catch(Y){K(`Failed to create queue: ${Y.message}`),process.exit(1)}}),G.command("queue:delete <name>","Delete an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Delete SQS Queue");try{let Y=new X0(J.region);if(N(`This will permanently delete queue: ${$}`),N("All messages in the queue will be lost!"),!await k(`
505
- Delete this queue?`,!1)){W("Operation cancelled");return}let Q=new E("Getting queue URL...");Q.start();let Z=await Y.getQueueUrl($);if(!Z.QueueUrl){Q.fail("Queue not found");return}Q.text="Deleting queue...",await Y.deleteQueue(Z.QueueUrl),Q.succeed("Queue deleted")}catch(Y){K(`Failed to delete queue: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Send SQS Message");try{let Y=new X0(J.region),X;if(J.file)X=await Bun.file(J.file).text();else if(J.message)X=J.message;else X=await p("Message body");if(!X){K("Message body is required");return}let Q=new E("Getting queue URL...");Q.start();let Z=await Y.getQueueUrl($);if(!Z.QueueUrl){Q.fail("Queue not found");return}Q.text="Sending message...";let z=await Y.sendMessage({queueUrl:Z.QueueUrl,messageBody:X,delaySeconds:Number.parseInt(J.delay),messageGroupId:J.group,messageDeduplicationId:J.dedup});Q.succeed("Message sent"),B(`
506
- Message ID: ${z.MessageId}`)}catch(Y){K(`Failed to send message: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Receive SQS Messages");try{let Y=new X0(J.region),X=new E("Getting queue URL...");X.start();let Q=await Y.getQueueUrl($);if(!Q.QueueUrl){X.fail("Queue not found");return}X.text="Receiving messages...";let z=(await Y.receiveMessages({queueUrl:Q.QueueUrl,maxMessages:Number.parseInt(J.max),waitTimeSeconds:Number.parseInt(J.wait)})).Messages||[];if(X.succeed(`Received ${z.length} message(s)`),z.length===0){W("No messages available");return}for(let F of z){if(W(`
507
- --- Message: ${F.MessageId} ---`),W(`Body: ${F.Body}`),F.Attributes)W(`Sent: ${F.Attributes.SentTimestamp?new Date(Number.parseInt(F.Attributes.SentTimestamp)).toISOString():"N/A"}`),W(`Receive Count: ${F.Attributes.ApproximateReceiveCount||"N/A"}`);if(J.delete&&F.ReceiptHandle)await Y.deleteMessage(Q.QueueUrl,F.ReceiptHandle),W("(Deleted)")}}catch(Y){K(`Failed to receive messages: ${Y.message}`),process.exit(1)}}),G.command("queue:purge <name>","Purge all messages from an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Purge SQS Queue");try{let Y=new X0(J.region);if(N(`This will delete ALL messages in queue: ${$}`),N("This action cannot be undone!"),!await k(`
508
- Purge this queue?`,!1)){W("Operation cancelled");return}let Q=new E("Getting queue URL...");Q.start();let Z=await Y.getQueueUrl($);if(!Z.QueueUrl){Q.fail("Queue not found");return}Q.text="Purging queue...",await Y.purgeQueue(Z.QueueUrl),Q.succeed("Queue purged"),W(`
509
- Note: It may take up to 60 seconds for the purge to complete.`)}catch(Y){K(`Failed to purge queue: ${Y.message}`),process.exit(1)}}),G.command("queue:stats <name>","Show SQS queue statistics").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R(`SQS Queue Stats: ${$}`);try{let Y=new X0(J.region),X=new E("Fetching queue stats...");X.start();let Q=await Y.getQueueUrl($);if(!Q.QueueUrl){X.fail("Queue not found");return}let Z=await Y.getQueueAttributes(Q.QueueUrl);X.succeed("Stats loaded");let z=Z.Attributes||{};if(W(`
510
- Queue Information:`),W(` URL: ${Q.QueueUrl}`),W(` ARN: ${z.QueueArn||"N/A"}`),W(` Type: ${z.FifoQueue==="true"?"FIFO":"Standard"}`),W(`
511
- Messages:`),W(` Available: ${z.ApproximateNumberOfMessages||"0"}`),W(` In Flight: ${z.ApproximateNumberOfMessagesNotVisible||"0"}`),W(` Delayed: ${z.ApproximateNumberOfMessagesDelayed||"0"}`),W(`
512
- Configuration:`),W(` Visibility Timeout: ${z.VisibilityTimeout||"30"} seconds`),W(` Message Retention: ${Number.parseInt(z.MessageRetentionPeriod||"345600")/86400} days`),W(` Max Message Size: ${Number.parseInt(z.MaximumMessageSize||"262144")/1024} KB`),W(` Receive Wait Time: ${z.ReceiveMessageWaitTimeSeconds||"0"} seconds`),z.RedrivePolicy){let F=JSON.parse(z.RedrivePolicy);W(`
513
- Dead Letter Queue:`),W(` Target ARN: ${F.deadLetterTargetArn}`),W(` Max Receives: ${F.maxReceiveCount}`)}W(`
514
- Timestamps:`),W(` Created: ${z.CreatedTimestamp?new Date(Number.parseInt(z.CreatedTimestamp)*1000).toISOString():"N/A"}`),W(` Last Modified: ${z.LastModifiedTimestamp?new Date(Number.parseInt(z.LastModifiedTimestamp)*1000).toISOString():"N/A"}`)}catch(Y){K(`Failed to get queue stats: ${Y.message}`),process.exit(1)}})}function I5(G){G.command("network:list","List all VPCs").option("--region <region>","AWS region").action(async($)=>{R("VPCs");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new E$(Y),Q=new E("Fetching VPCs...");Q.start();let z=(await X.describeVpcs()).Vpcs||[];if(Q.succeed(`Found ${z.length} VPC(s)`),z.length===0){W("No VPCs found");return}w(["VPC ID","Name","CIDR","State","Default"],z.map((F)=>[F.VpcId||"N/A",F.Tags?.find((U)=>U.Key==="Name")?.Value||"-",F.CidrBlock||"N/A",F.State||"N/A",F.IsDefault?"Yes":"No"]))}catch(J){K(`Failed to list VPCs: ${J.message}`),process.exit(1)}}),G.command("network:subnets","List all subnets").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async($)=>{R("Subnets");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new E$(Y),Q=new E("Fetching subnets...");Q.start();let Z=$.vpc?[{Name:"vpc-id",Values:[$.vpc]}]:void 0,F=(await X.describeSubnets({Filters:Z})).Subnets||[];if(Q.succeed(`Found ${F.length} subnet(s)`),F.length===0){W("No subnets found");return}w(["Subnet ID","Name","VPC","CIDR","AZ","IPs Available","Public"],F.map((U)=>[U.SubnetId||"N/A",U.Tags?.find((M)=>M.Key==="Name")?.Value||"-",U.VpcId||"N/A",U.CidrBlock||"N/A",U.AvailabilityZone||"N/A",(U.AvailableIpAddressCount||0).toString(),U.MapPublicIpOnLaunch?"Yes":"No"]))}catch(J){K(`Failed to list subnets: ${J.message}`),process.exit(1)}}),G.command("network:security-groups","List all security groups").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async($)=>{R("Security Groups");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new E$(Y),Q=new E("Fetching security groups...");Q.start();let Z=$.vpc?[{Name:"vpc-id",Values:[$.vpc]}]:void 0,F=(await X.describeSecurityGroups({Filters:Z})).SecurityGroups||[];if(Q.succeed(`Found ${F.length} security group(s)`),F.length===0){W("No security groups found");return}w(["Group ID","Name","VPC","Description","Inbound Rules","Outbound Rules"],F.map((U)=>[U.GroupId||"N/A",U.GroupName||"N/A",U.VpcId||"N/A",(U.Description||"").substring(0,30),(U.IpPermissions?.length||0).toString(),(U.IpPermissionsEgress?.length||0).toString()]))}catch(J){K(`Failed to list security groups: ${J.message}`),process.exit(1)}}),G.command("network:security-group <groupId>","Show security group details").option("--region <region>","AWS region").action(async($,J)=>{R(`Security Group: ${$}`);try{let Y=await x(),X=J.region||Y.project.region||"us-east-1",Q=new E$(X),Z=new E("Fetching security group...");Z.start();let F=(await Q.describeSecurityGroups({GroupIds:[$]})).SecurityGroups?.[0];if(!F){Z.fail("Security group not found");return}if(Z.succeed("Security group loaded"),W(`
515
- General Information:`),W(` Group ID: ${F.GroupId}`),W(` Name: ${F.GroupName}`),W(` VPC: ${F.VpcId}`),W(` Description: ${F.Description}`),F.IpPermissions&&F.IpPermissions.length>0){W(`
516
- Inbound Rules:`);for(let U of F.IpPermissions){let M=U.IpProtocol==="-1"?"All":U.IpProtocol?.toUpperCase(),H=U.FromPort===U.ToPort?U.FromPort?.toString()||"All":`${U.FromPort}-${U.ToPort}`;for(let j of U.IpRanges||[])W(` - ${M} ${H} from ${j.CidrIp}${j.Description?` (${j.Description})`:""}`);for(let j of U.UserIdGroupPairs||[])W(` - ${M} ${H} from ${j.GroupId}${j.Description?` (${j.Description})`:""}`)}}else W(`
517
- No inbound rules configured.`);if(F.IpPermissionsEgress&&F.IpPermissionsEgress.length>0){W(`
518
- Outbound Rules:`);for(let U of F.IpPermissionsEgress){let M=U.IpProtocol==="-1"?"All":U.IpProtocol?.toUpperCase(),H=U.FromPort===U.ToPort?U.FromPort?.toString()||"All":`${U.FromPort}-${U.ToPort}`;for(let j of U.IpRanges||[])W(` - ${M} ${H} to ${j.CidrIp}${j.Description?` (${j.Description})`:""}`);for(let j of U.UserIdGroupPairs||[])W(` - ${M} ${H} to ${j.GroupId}${j.Description?` (${j.Description})`:""}`)}}else W(`
519
- No outbound rules configured.`);if(F.Tags&&F.Tags.length>0){W(`
520
- Tags:`);for(let U of F.Tags)W(` ${U.Key}: ${U.Value}`)}}catch(Y){K(`Failed to get security group: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Create VPC");try{let Y=new E$(J.region);if(W(`CIDR Block: ${$}`),W(`Region: ${J.region}`),W(`Tenancy: ${J.tenancy}`),J.name)W(`Name: ${J.name}`);if(!await k(`
521
- Create this VPC?`,!0)){W("Operation cancelled");return}let Q=new E("Creating VPC...");Q.start();let Z=await Y.createVpc({CidrBlock:$,InstanceTenancy:J.tenancy,TagSpecifications:J.name?[{ResourceType:"vpc",Tags:[{Key:"Name",Value:J.name}]}]:void 0});Q.succeed("VPC created"),B(`
522
- VPC ID: ${Z.Vpc?.VpcId}`),W(`CIDR: ${Z.Vpc?.CidrBlock}`),W(`State: ${Z.Vpc?.State}`)}catch(Y){K(`Failed to create VPC: ${Y.message}`),process.exit(1)}}),G.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($,J,Y)=>{R("Create Subnet");try{let X=new E$(Y.region);if(W(`VPC: ${$}`),W(`CIDR Block: ${J}`),Y.az)W(`Availability Zone: ${Y.az}`);if(Y.name)W(`Name: ${Y.name}`);if(W(`Auto-assign Public IP: ${Y.public?"Yes":"No"}`),!await k(`
523
- Create this subnet?`,!0)){W("Operation cancelled");return}let Z=new E("Creating subnet...");Z.start();let z=await X.createSubnet({VpcId:$,CidrBlock:J,AvailabilityZone:Y.az,TagSpecifications:Y.name?[{ResourceType:"subnet",Tags:[{Key:"Name",Value:Y.name}]}]:void 0});if(Y.public&&z.Subnet?.SubnetId)Z.text="Enabling auto-assign public IP...",await X.modifySubnetAttribute({SubnetId:z.Subnet.SubnetId,MapPublicIpOnLaunch:{Value:!0}});Z.succeed("Subnet created"),B(`
524
- Subnet ID: ${z.Subnet?.SubnetId}`),W(`CIDR: ${z.Subnet?.CidrBlock}`),W(`Availability Zone: ${z.Subnet?.AvailabilityZone}`)}catch(X){K(`Failed to create subnet: ${X.message}`),process.exit(1)}}),G.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($,J,Y)=>{R("Create Security Group");try{let X=new E$(Y.region),Q=Y.description||`Security group for ${J}`;if(W(`VPC: ${$}`),W(`Name: ${J}`),W(`Description: ${Q}`),!await k(`
525
- Create this security group?`,!0)){W("Operation cancelled");return}let z=new E("Creating security group...");z.start();let F=await X.createSecurityGroup({GroupName:J,Description:Q,VpcId:$,TagSpecifications:[{ResourceType:"security-group",Tags:[{Key:"Name",Value:J}]}]});z.succeed("Security group created"),B(`
526
- Group ID: ${F.GroupId}`),W(`
527
- To add rules:`),W(` cloud network:sg-rule ${F.GroupId} --inbound --port 443 --cidr 0.0.0.0/0`)}catch(X){K(`Failed to create security group: ${X.message}`),process.exit(1)}}),G.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($,J)=>{R("Add Security Group Rule");try{let Y=new E$(J.region);if(!J.inbound&&!J.outbound){K("Specify --inbound or --outbound");return}if(!J.cidr&&!J.sourceGroup){K("Specify --cidr or --source-group");return}let X,Q;if(J.port)if(J.port.includes("-")){let[H,j]=J.port.split("-");X=Number.parseInt(H),Q=Number.parseInt(j)}else X=Number.parseInt(J.port),Q=X;else if(J.protocol!=="-1"){K("Port is required for TCP/UDP protocols");return}let Z=J.inbound?"Inbound":"Outbound",z=J.protocol==="-1"?"All":X===Q?X:`${X}-${Q}`;if(W(`Security Group: ${$}`),W(`Direction: ${Z}`),W(`Protocol: ${J.protocol}`),W(`Port(s): ${z}`),W(`Source: ${J.cidr||J.sourceGroup}`),!await k(`
528
- Add this rule?`,!0)){W("Operation cancelled");return}let U=new E("Adding rule...");U.start();let M={IpProtocol:J.protocol,FromPort:X,ToPort:Q};if(J.cidr)M.IpRanges=[{CidrIp:J.cidr,Description:J.description}];if(J.sourceGroup)M.UserIdGroupPairs=[{GroupId:J.sourceGroup,Description:J.description}];if(J.inbound)await Y.authorizeSecurityGroupIngress({GroupId:$,IpPermissions:[M]});else await Y.authorizeSecurityGroupEgress({GroupId:$,IpPermissions:[M]});U.succeed("Rule added")}catch(Y){K(`Failed to add rule: ${Y.message}`),process.exit(1)}}),G.command("network:elastic-ips","List Elastic IP addresses").option("--region <region>","AWS region").action(async($)=>{R("Elastic IP Addresses");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new E$(Y),Q=new E("Fetching Elastic IPs...");Q.start();let z=(await X.describeAddresses()).Addresses||[];if(Q.succeed(`Found ${z.length} Elastic IP(s)`),z.length===0){W("No Elastic IPs found");return}w(["Public IP","Allocation ID","Association ID","Instance","Name"],z.map((F)=>[F.PublicIp||"N/A",F.AllocationId||"N/A",F.AssociationId||"-",F.InstanceId||"-",F.Tags?.find((U)=>U.Key==="Name")?.Value||"-"]))}catch(J){K(`Failed to list Elastic IPs: ${J.message}`),process.exit(1)}}),G.command("network:route-tables","List route tables").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async($)=>{R("Route Tables");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new E$(Y),Q=new E("Fetching route tables...");Q.start();let Z=$.vpc?[{Name:"vpc-id",Values:[$.vpc]}]:void 0,F=(await X.describeRouteTables({Filters:Z})).RouteTables||[];if(Q.succeed(`Found ${F.length} route table(s)`),F.length===0){W("No route tables found");return}w(["Route Table ID","VPC","Name","Main","Associations","Routes"],F.map((U)=>[U.RouteTableId||"N/A",U.VpcId||"N/A",U.Tags?.find((M)=>M.Key==="Name")?.Value||"-",U.Associations?.some((M)=>M.Main)?"Yes":"No",(U.Associations?.length||0).toString(),(U.Routes?.length||0).toString()]))}catch(J){K(`Failed to list route tables: ${J.message}`),process.exit(1)}})}function k5(G){G.command("scheduler:list","List all EventBridge schedules").option("--region <region>","AWS region").option("--group <name>","Schedule group name",{default:"default"}).action(async($)=>{R("EventBridge Schedules");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new Q0(Y),Q=new E("Fetching schedules...");Q.start();let z=(await X.listSchedules({GroupName:$.group})).Schedules||[];if(Q.succeed(`Found ${z.length} schedule(s)`),z.length===0){W("No schedules found"),W("Use `cloud scheduler:create` to create a new schedule");return}w(["Name","State","Schedule Expression","Group"],z.map((F)=>[F.Name||"N/A",F.State||"N/A",F.ScheduleExpression||"N/A",F.GroupName||"default"]))}catch(J){K(`Failed to list schedules: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("Create EventBridge Schedule");try{if(!J.schedule){K("--schedule is required"),W("Examples:"),W(" Rate: rate(5 minutes), rate(1 hour), rate(1 day)"),W(" Cron: cron(0 12 * * ? *) - every day at 12:00 UTC");return}if(!J.targetArn){K("--target-arn is required");return}if(!J.roleArn){K("--role-arn is required"),W("The role must have permissions to invoke the target.");return}let Y=new Q0(J.region);if(W(`Name: ${$}`),W(`Schedule: ${J.schedule}`),W(`Target: ${J.targetArn}`),W(`Group: ${J.group}`),W(`Timezone: ${J.timezone}`),!await k(`
529
- Create this schedule?`,!0)){W("Operation cancelled");return}let Q=new E("Creating schedule...");Q.start(),await Y.createSchedule({Name:$,GroupName:J.group,ScheduleExpression:J.schedule,ScheduleExpressionTimezone:J.timezone,Description:J.description,State:"ENABLED",FlexibleTimeWindow:{Mode:"OFF"},Target:{Arn:J.targetArn,RoleArn:J.roleArn,Input:J.input},StartDate:J.start?new Date(J.start):void 0,EndDate:J.end?new Date(J.end):void 0}),Q.succeed("Schedule created"),B(`
530
- Schedule: ${$}`),W(`Expression: ${J.schedule}`)}catch(Y){K(`Failed to create schedule: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Delete EventBridge Schedule");try{let Y=new Q0(J.region);if(N(`This will delete schedule: ${$}`),!await k(`
531
- Delete this schedule?`,!1)){W("Operation cancelled");return}let Q=new E("Deleting schedule...");Q.start(),await Y.deleteSchedule({Name:$,GroupName:J.group}),Q.succeed("Schedule deleted")}catch(Y){K(`Failed to delete schedule: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Enable EventBridge Schedule");try{let Y=new Q0(J.region),X=new E("Getting schedule...");X.start();let Q=await Y.getSchedule({Name:$,GroupName:J.group});if(!Q){X.fail("Schedule not found");return}X.text="Enabling schedule...",await Y.updateSchedule({Name:$,GroupName:J.group,ScheduleExpression:Q.ScheduleExpression,ScheduleExpressionTimezone:Q.ScheduleExpressionTimezone,FlexibleTimeWindow:Q.FlexibleTimeWindow,Target:Q.Target,State:"ENABLED"}),X.succeed("Schedule enabled")}catch(Y){K(`Failed to enable schedule: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Disable EventBridge Schedule");try{let Y=new Q0(J.region),X=new E("Getting schedule...");X.start();let Q=await Y.getSchedule({Name:$,GroupName:J.group});if(!Q){X.fail("Schedule not found");return}X.text="Disabling schedule...",await Y.updateSchedule({Name:$,GroupName:J.group,ScheduleExpression:Q.ScheduleExpression,ScheduleExpressionTimezone:Q.ScheduleExpressionTimezone,FlexibleTimeWindow:Q.FlexibleTimeWindow,Target:Q.Target,State:"DISABLED"}),X.succeed("Schedule disabled")}catch(Y){K(`Failed to disable schedule: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R(`Schedule: ${$}`);try{let Y=new Q0(J.region),X=new E("Fetching schedule...");X.start();let Q=await Y.getSchedule({Name:$,GroupName:J.group});if(!Q){X.fail("Schedule not found");return}if(X.succeed("Schedule loaded"),W(`
532
- Schedule Information:`),W(` Name: ${Q.Name}`),W(` ARN: ${Q.Arn}`),W(` Group: ${Q.GroupName}`),W(` State: ${Q.State}`),W(` Expression: ${Q.ScheduleExpression}`),W(` Timezone: ${Q.ScheduleExpressionTimezone}`),Q.Description)W(` Description: ${Q.Description}`);if(Q.Target){if(W(`
533
- Target:`),W(` ARN: ${Q.Target.Arn}`),W(` Role ARN: ${Q.Target.RoleArn}`),Q.Target.Input)W(` Input: ${Q.Target.Input}`)}if(W(`
534
- Flexible Time Window:`),W(` Mode: ${Q.FlexibleTimeWindow?.Mode}`),Q.FlexibleTimeWindow?.MaximumWindowInMinutes)W(` Max Window: ${Q.FlexibleTimeWindow.MaximumWindowInMinutes} minutes`);if(Q.StartDate)W(`
535
- Start Date: ${Q.StartDate}`);if(Q.EndDate)W(`End Date: ${Q.EndDate}`);W(`
536
- Created: ${Q.CreationDate||"N/A"}`),W(`Last Modified: ${Q.LastModificationDate||"N/A"}`)}catch(Y){K(`Failed to get schedule: ${Y.message}`),process.exit(1)}}),G.command("scheduler:groups","List schedule groups").option("--region <region>","AWS region").action(async($)=>{R("Schedule Groups");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new Q0(Y),Q=new E("Fetching groups...");Q.start();let z=(await X.listScheduleGroups()).ScheduleGroups||[];if(Q.succeed(`Found ${z.length} group(s)`),z.length===0){W("No schedule groups found");return}w(["Name","State","Created"],z.map((F)=>[F.Name||"N/A",F.State||"N/A",F.CreationDate?new Date(F.CreationDate).toLocaleString():"N/A"]))}catch(J){K(`Failed to list groups: ${J.message}`),process.exit(1)}})}function h5(G){G.command("events:list","List all EventBridge rules").option("--region <region>","AWS region").option("--bus <name>","Event bus name",{default:"default"}).action(async($)=>{R("EventBridge Rules");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new l$(Y),Q=new E("Fetching rules...");Q.start();let z=(await X.listRules({EventBusName:$.bus})).Rules||[];if(Q.succeed(`Found ${z.length} rule(s)`),z.length===0){W("No EventBridge rules found"),W("Use `cloud events:create` to create a new rule");return}w(["Name","State","Schedule/Pattern","Description"],z.map((F)=>[F.Name||"N/A",F.State||"N/A",F.ScheduleExpression||(F.EventPattern?"Event Pattern":"N/A"),(F.Description||"").substring(0,40)]))}catch(J){K(`Failed to list rules: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("Create EventBridge Rule");try{if(!J.schedule&&!J.pattern&&!J.patternFile){K("Either --schedule or --pattern/--pattern-file is required"),W(`
537
- Examples:`),W(' Schedule: --schedule "rate(5 minutes)"'),W(` Pattern: --pattern '{"source": ["aws.ec2"]}'`);return}let Y=new l$(J.region),X;if(J.patternFile)X=await Bun.file(J.patternFile).text();else if(J.pattern)X=J.pattern;if(W(`Name: ${$}`),W(`Event Bus: ${J.bus}`),J.schedule)W(`Schedule: ${J.schedule}`);if(X)W(`Event Pattern: ${X}`);if(W(`State: ${J.disabled?"DISABLED":"ENABLED"}`),!await k(`
538
- Create this rule?`,!0)){W("Operation cancelled");return}let Z=new E("Creating rule...");Z.start();let z=await Y.putRule({Name:$,EventBusName:J.bus,ScheduleExpression:J.schedule,EventPattern:X,Description:J.description,State:J.disabled?"DISABLED":"ENABLED"});Z.succeed("Rule created"),B(`
539
- Rule ARN: ${z.RuleArn}`),W("\nNote: Add targets to the rule with `cloud events:target`")}catch(Y){K(`Failed to create rule: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Delete EventBridge Rule");try{let Y=new l$(J.region);if(N(`This will delete rule: ${$}`),!await k(`
540
- Delete this rule?`,!1)){W("Operation cancelled");return}let Q=new E("Checking targets...");Q.start();let Z=await Y.listTargetsByRule({Rule:$,EventBusName:J.bus});if(Z.Targets&&Z.Targets.length>0){if(!J.force){Q.fail("Rule has targets"),W(`
541
- The rule has ${Z.Targets.length} target(s).`),W("Use --force to remove targets and delete the rule.");return}Q.text="Removing targets...",await Y.removeTargets({Rule:$,EventBusName:J.bus,Ids:Z.Targets.map((z)=>z.Id)})}Q.text="Deleting rule...",await Y.deleteRule({Name:$,EventBusName:J.bus}),Q.succeed("Rule deleted")}catch(Y){K(`Failed to delete rule: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R(`Rule: ${$}`);try{let Y=new l$(J.region),X=new E("Fetching rule...");X.start();let Q=await Y.describeRule({Name:$,EventBusName:J.bus});if(!Q){X.fail("Rule not found");return}let Z=await Y.listTargetsByRule({Rule:$,EventBusName:J.bus});if(X.succeed("Rule loaded"),W(`
542
- Rule Information:`),W(` Name: ${Q.Name}`),W(` ARN: ${Q.Arn}`),W(` State: ${Q.State}`),W(` Event Bus: ${Q.EventBusName}`),Q.Description)W(` Description: ${Q.Description}`);if(Q.ScheduleExpression)W(`
543
- Schedule Expression: ${Q.ScheduleExpression}`);if(Q.EventPattern)W(`
544
- Event Pattern:`),console.log(JSON.stringify(JSON.parse(Q.EventPattern),null,2));if(Z.Targets&&Z.Targets.length>0){W(`
545
- Targets (${Z.Targets.length}):`);for(let z of Z.Targets)if(W(` - ${z.Id}: ${z.Arn}`),z.Input)W(` Input: ${z.Input}`)}else W(`
546
- No targets configured.`)}catch(Y){K(`Failed to get rule: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Add EventBridge Target");try{if(!J.arn){K("--arn is required");return}let Y=new l$(J.region),X=J.id||`target-${Date.now()}`;if(W(`Rule: ${$}`),W(`Target ID: ${X}`),W(`Target ARN: ${J.arn}`),!await k(`
547
- Add this target?`,!0)){W("Operation cancelled");return}let Z=new E("Adding target...");Z.start();let z={Id:X,Arn:J.arn};if(J.role)z.RoleArn=J.role;if(J.input)z.Input=J.input;if(J.inputPath)z.InputPath=J.inputPath;await Y.putTargets({Rule:$,EventBusName:J.bus,Targets:[z]}),Z.succeed("Target added"),B(`
548
- Target ${X} added to rule ${$}`)}catch(Y){K(`Failed to add target: ${Y.message}`),process.exit(1)}}),G.command("events:buses","List event buses").option("--region <region>","AWS region").action(async($)=>{R("Event Buses");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new l$(Y),Q=new E("Fetching event buses...");Q.start();let z=(await X.listEventBuses()).EventBuses||[];if(Q.succeed(`Found ${z.length} event bus(es)`),z.length===0){W("No event buses found");return}w(["Name","ARN","Policy"],z.map((F)=>[F.Name||"N/A",F.Arn||"N/A",F.Policy?"Custom":"Default"]))}catch(J){K(`Failed to list event buses: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("Enable EventBridge Rule");try{let Y=new l$(J.region),X=new E("Enabling rule...");X.start(),await Y.enableRule({Name:$,EventBusName:J.bus}),X.succeed("Rule enabled")}catch(Y){K(`Failed to enable rule: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Disable EventBridge Rule");try{let Y=new l$(J.region),X=new E("Disabling rule...");X.start(),await Y.disableRule({Name:$,EventBusName:J.bus}),X.succeed("Rule disabled")}catch(Y){K(`Failed to disable rule: ${Y.message}`),process.exit(1)}})}function y5(G){G.command("email:identities","List verified email identities").option("--region <region>","AWS region").action(async($)=>{R("Email Identities");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new u$(Y),Q=new E("Fetching identities...");Q.start();let z=(await X.listEmailIdentities()).EmailIdentities||[];if(Q.succeed(`Found ${z.length} identity(s)`),z.length===0){W("No email identities found"),W("Use `cloud email:verify` to verify an email or domain");return}let F=[];for(let U of z){let M=U.IdentityName||"Unknown",H="Unknown";try{H=(await X.getEmailIdentity(M)).VerificationStatus||"Unknown"}catch{}let j=U.IdentityType==="EMAIL_ADDRESS"?"Email":U.IdentityType==="DOMAIN"?"Domain":U.IdentityType||"Unknown";F.push([M,j,H])}w(["Identity","Type","Verification Status"],F)}catch(J){let Y=J instanceof Error?J.message:String(J);K(`Failed to list identities: ${Y}`),process.exit(1)}}),G.command("email:verify <identity>","Verify an email address or domain").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Verify Email Identity");try{let Y=new u$(J.region),X=$.includes("@");if(W(`Identity: ${$}`),W(`Type: ${X?"Email Address":"Domain"}`),!await k(`
549
- Send verification?`,!0)){W("Operation cancelled");return}let Z=new E("Initiating verification...");if(Z.start(),X)await Y.createEmailIdentity({EmailIdentity:$}),Z.succeed("Verification email sent"),W(`
550
- A verification email has been sent to ${$}`),W("Click the link in the email to complete verification.");else{let z=await Y.verifyDomain($);Z.succeed("Domain verification initiated");let F=z.dkimTokens||[];if(F.length>0){W(`
551
- DKIM Records (for email authentication):`);for(let U of F)W(`
552
- Name: ${U}._domainkey.${$}`),W(" Type: CNAME"),W(` Value: ${U}.dkim.amazonses.com`)}W(`
553
- Verification Status: ${z.verificationStatus||"PENDING"}`)}}catch(Y){let X=Y instanceof Error?Y.message:String(Y);K(`Failed to verify identity: ${X}`),process.exit(1)}}),G.command("email:delete <identity>","Delete a verified email identity").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Delete Email Identity");try{let Y=new u$(J.region);if(N(`This will remove identity: ${$}`),!await k(`
554
- Delete this identity?`,!1)){W("Operation cancelled");return}let Q=new E("Deleting identity...");Q.start(),await Y.deleteEmailIdentity($),Q.succeed("Identity deleted")}catch(Y){let X=Y instanceof Error?Y.message:String(Y);K(`Failed to delete identity: ${X}`),process.exit(1)}}),G.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($)=>{R("Send Email");try{let J=new u$($.region),Y=$.from||await p("From (verified email)"),X=$.to||await p("To"),Q=$.subject||await p("Subject","Test Email from ts-cloud"),Z=$.body||await p("Body","This is a test email sent from ts-cloud CLI.");if(W(`
555
- From: ${Y}`),W(`To: ${X}`),W(`Subject: ${Q}`),!await k(`
556
- Send this email?`,!0)){W("Operation cancelled");return}let F=new E("Sending email...");F.start();let U=await J.sendEmail({FromEmailAddress:Y,Destination:{ToAddresses:[X]},Content:{Simple:{Subject:{Data:Q},Body:{Text:{Data:Z},...$.html&&{Html:{Data:$.html}}}}}});F.succeed("Email sent"),B(`
557
- Message ID: ${U.MessageId}`)}catch(J){let Y=J instanceof Error?J.message:String(J);K(`Failed to send email: ${Y}`),process.exit(1)}}),G.command("email:templates","List email templates").option("--region <region>","AWS region").action(async($)=>{R("Email Templates");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new u$(Y),Q=new E("Fetching templates...");Q.start();let z=(await X.listEmailTemplates()).TemplatesMetadata||[];if(Q.succeed(`Found ${z.length} template(s)`),z.length===0){W("No email templates found"),W("Use `cloud email:template:create` to create a template");return}w(["Name","Created"],z.map((F)=>[F.TemplateName||"N/A",F.CreatedTimestamp?new Date(F.CreatedTimestamp).toLocaleString():"N/A"]))}catch(J){let Y=J instanceof Error?J.message:String(J);K(`Failed to list templates: ${Y}`),process.exit(1)}}),G.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($,J)=>{R("Create Email Template");try{let Y=new u$(J.region),X=J.subject||await p("Subject template","Hello {{name}}"),Q=J.text||await p("Text body","Hello {{name}}, this is a test."),Z=J.html;if(J.htmlFile)Z=await Bun.file(J.htmlFile).text();if(W(`
558
- Template Name: ${$}`),W(`Subject: ${X}`),!await k(`
559
- Create this template?`,!0)){W("Operation cancelled");return}let F=new E("Creating template...");F.start(),await Y.createEmailTemplate({TemplateName:$,TemplateContent:{Subject:X,Text:Q,Html:Z}}),F.succeed("Template created"),W(`
560
- To send using this template:`),W(` cloud email:send:template --template ${$} --to user@example.com --data '{"name":"John"}'`)}catch(Y){let X=Y instanceof Error?Y.message:String(Y);K(`Failed to create template: ${X}`),process.exit(1)}}),G.command("email:template:delete <name>","Delete an email template").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Delete Email Template");try{let Y=new u$(J.region);if(N(`This will delete template: ${$}`),!await k(`
561
- Delete this template?`,!1)){W("Operation cancelled");return}let Q=new E("Deleting template...");Q.start(),await Y.deleteEmailTemplate($),Q.succeed("Template deleted")}catch(Y){let X=Y instanceof Error?Y.message:String(Y);K(`Failed to delete template: ${X}`),process.exit(1)}}),G.command("email:stats","Show SES sending statistics").option("--region <region>","AWS region").action(async($)=>{R("Email Statistics");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new u$(Y),Q=new E("Fetching statistics...");Q.start();let[Z,z]=await Promise.all([X.getSendQuota(),X.getSendStatistics()]);Q.succeed("Statistics loaded"),W(`
562
- Sending Quota:`),W(` Max 24-hour Send: ${Z.Max24HourSend||0}`),W(` Max Send Rate: ${Z.MaxSendRate||0} emails/second`),W(` Sent Last 24h: ${Z.SentLast24Hours||0}`);let F=(Z.Max24HourSend||0)-(Z.SentLast24Hours||0);if(W(` Remaining: ${F}`),z.SendDataPoints&&z.SendDataPoints.length>0){W(`
563
- Recent Statistics:`);let U=0,M=0,H=0,j=0;for(let V of z.SendDataPoints)U+=V.DeliveryAttempts||0,M+=V.Bounces||0,H+=V.Complaints||0,j+=V.Rejects||0;if(W(` Total Attempts: ${U}`),W(` Bounces: ${M}`),W(` Complaints: ${H}`),W(` Rejects: ${j}`),U>0){let V=(M/U*100).toFixed(2),T=(H/U*100).toFixed(2);if(W(`
564
- Bounce Rate: ${V}%`),W(` Complaint Rate: ${T}%`),Number.parseFloat(V)>5)N(`
565
- Warning: Bounce rate is high (>5%). This may affect your sender reputation.`);if(Number.parseFloat(T)>0.1)N(`
566
- Warning: Complaint rate is high (>0.1%). This may affect your sender reputation.`)}}}catch(J){let Y=J instanceof Error?J.message:String(J);K(`Failed to get statistics: ${Y}`),process.exit(1)}})}function C5(G){G.command("notify:topics","List all SNS topics").option("--region <region>","AWS region").action(async($)=>{R("SNS Topics");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=new b$(Y),Q=new E("Fetching topics...");Q.start();let z=(await X.listTopics()).Topics||[];if(Q.succeed(`Found ${z.length} topic(s)`),z.length===0){W("No SNS topics found"),W("Use `cloud notify:create` to create a new topic");return}w(["Topic ARN","Name"],z.map((F)=>{let U=F.TopicArn?.split(":").pop()||"N/A";return[F.TopicArn||"N/A",U]}))}catch(J){K(`Failed to list topics: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("Create SNS Topic");try{let Y=new b$(J.region),X=J.fifo&&!$.endsWith(".fifo")?`${$}.fifo`:$;if(W(`Topic name: ${X}`),W(`Type: ${J.fifo?"FIFO":"Standard"}`),!await k(`
567
- Create this topic?`,!0)){W("Operation cancelled");return}let Z=new E("Creating topic...");Z.start();let z={};if(J.fifo)z.FifoTopic="true",z.ContentBasedDeduplication="true";if(J.displayName)z.DisplayName=J.displayName;let F=await Y.createTopic({Name:X,Attributes:Object.keys(z).length>0?z:void 0});Z.succeed("Topic created"),B(`
568
- Topic ARN: ${F.TopicArn}`),W(`
569
- To subscribe:`),W(` cloud notify:subscribe ${F.TopicArn} --email user@example.com`)}catch(Y){K(`Failed to create topic: ${Y.message}`),process.exit(1)}}),G.command("notify:delete <topicArn>","Delete an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Delete SNS Topic");try{let Y=new b$(J.region);if(N(`This will delete topic: ${$}`),N("All subscriptions will be removed."),!await k(`
570
- Delete this topic?`,!1)){W("Operation cancelled");return}let Q=new E("Deleting topic...");Q.start(),await Y.deleteTopic($),Q.succeed("Topic deleted")}catch(Y){K(`Failed to delete topic: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Subscribe to SNS Topic");try{let Y=new b$(J.region),X,Q;if(J.email)X="email",Q=J.email;else if(J.sms)X="sms",Q=J.sms;else if(J.sqs)X="sqs",Q=J.sqs;else if(J.lambda)X="lambda",Q=J.lambda;else if(J.https)X="https",Q=J.https;else{K("Specify a subscription type: --email, --sms, --sqs, --lambda, or --https");return}if(W(`Topic: ${$}`),W(`Protocol: ${X}`),W(`Endpoint: ${Q}`),!await k(`
571
- Create this subscription?`,!0)){W("Operation cancelled");return}let z=new E("Creating subscription...");z.start();let F={TopicArn:$,Protocol:X,Endpoint:Q};if(J.filter)F.Attributes={FilterPolicy:J.filter};let U=await Y.subscribe(F);if(z.succeed("Subscription created"),U.SubscriptionArn==="pending confirmation")W(`
572
- Subscription is pending confirmation.`),W("The subscriber will receive a confirmation message.");else B(`
573
- Subscription ARN: ${U.SubscriptionArn}`)}catch(Y){K(`Failed to subscribe: ${Y.message}`),process.exit(1)}}),G.command("notify:unsubscribe <subscriptionArn>","Unsubscribe from an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Unsubscribe from SNS Topic");try{let Y=new b$(J.region);if(N(`This will remove subscription: ${$}`),!await k(`
574
- Unsubscribe?`,!1)){W("Operation cancelled");return}let Q=new E("Unsubscribing...");Q.start(),await Y.unsubscribe($),Q.succeed("Unsubscribed")}catch(Y){K(`Failed to unsubscribe: ${Y.message}`),process.exit(1)}}),G.command("notify:subscriptions <topicArn>","List subscriptions for a topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Topic Subscriptions");try{let Y=new b$(J.region),X=new E("Fetching subscriptions...");X.start();let Z=(await Y.listSubscriptionsByTopic($)).Subscriptions||[];if(X.succeed(`Found ${Z.length} subscription(s)`),Z.length===0){W("No subscriptions found");return}w(["Protocol","Endpoint","Status","Subscription ARN"],Z.map((z)=>[z.Protocol||"N/A",z.Endpoint||"N/A",z.SubscriptionArn==="PendingConfirmation"?"Pending":"Confirmed",(z.SubscriptionArn||"N/A").substring(0,50)]))}catch(Y){K(`Failed to list subscriptions: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Publish to SNS Topic");try{let Y=new b$(J.region),X;if(J.file)X=await Bun.file(J.file).text();else if(J.message)X=J.message;else X=await p("Message");if(!X){K("Message is required");return}if(W(`Topic: ${$}`),W(`Message length: ${X.length} characters`),!await k(`
575
- Publish this message?`,!0)){W("Operation cancelled");return}let Z=new E("Publishing message...");Z.start();let z={TopicArn:$,Message:X};if(J.subject)z.Subject=J.subject;if(J.json)z.MessageStructure="json";if(J.group)z.MessageGroupId=J.group;if(J.dedup)z.MessageDeduplicationId=J.dedup;let F=await Y.publish(z);Z.succeed("Message published"),B(`
576
- Message ID: ${F.MessageId}`)}catch(Y){K(`Failed to publish message: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Send SMS");try{let Y=new b$(J.region),X=J.message||await p("Message");if(!X){K("Message is required");return}if(W(`To: ${$}`),W(`Message: ${X}`),W(`Type: ${J.type}`),!await k(`
577
- Send this SMS?`,!0)){W("Operation cancelled");return}let Z=new E("Sending SMS...");Z.start();let z={PhoneNumber:$,Message:X,MessageAttributes:{"AWS.SNS.SMS.SMSType":{DataType:"String",StringValue:J.type}}};if(J.sender)z.MessageAttributes["AWS.SNS.SMS.SenderID"]={DataType:"String",StringValue:J.sender};let F=await Y.publish(z);Z.succeed("SMS sent"),B(`
578
- Message ID: ${F.MessageId}`)}catch(Y){K(`Failed to send SMS: ${Y.message}`),process.exit(1)}}),G.command("notify:topic:attributes <topicArn>","Show topic attributes").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Topic Attributes");try{let Y=new b$(J.region),X=new E("Fetching attributes...");X.start();let Q=await Y.getTopicAttributes($);if(X.succeed("Attributes loaded"),W(`
579
- Topic Information:`),W(` ARN: ${$}`),W(` Display Name: ${Q.DisplayName||"Not set"}`),W(` Owner: ${Q.Owner||"N/A"}`),W(`
580
- Subscriptions:`),W(` Confirmed: ${Q.SubscriptionsConfirmed||0}`),W(` Pending: ${Q.SubscriptionsPending||0}`),W(` Deleted: ${Q.SubscriptionsDeleted||0}`),W(`
581
- Settings:`),W(` FIFO: ${Q.FifoTopic==="true"?"Yes":"No"}`),W(` Content Deduplication: ${Q.ContentBasedDeduplication==="true"?"Yes":"No"}`),Q.EffectiveDeliveryPolicy)W(`
582
- Delivery Policy: Configured`);if(Q.Policy)W(`
583
- Access Policy: Configured`)}catch(Y){K(`Failed to get attributes: ${Y.message}`),process.exit(1)}})}function f5(G){G.command("iam:roles","List IAM roles").option("--prefix <prefix>","Filter by role name prefix").option("--path <path>","Filter by path prefix",{default:"/"}).action(async($)=>{R("IAM Roles");try{let J=new K$,Y=new E("Fetching roles...");Y.start();let Q=(await J.listRoles({PathPrefix:$.path})).Roles||[];if($.prefix)Q=Q.filter((Z)=>Z.RoleName?.startsWith($.prefix));if(Y.succeed(`Found ${Q.length} role(s)`),Q.length===0){W("No IAM roles found");return}w(["Role Name","Path","Created","Description"],Q.map((Z)=>[Z.RoleName||"N/A",Z.Path||"/",Z.CreateDate?new Date(Z.CreateDate).toLocaleDateString():"N/A",(Z.Description||"").substring(0,40)]))}catch(J){K(`Failed to list roles: ${J.message}`),process.exit(1)}}),G.command("iam:role <roleName>","Show IAM role details").action(async($)=>{R(`IAM Role: ${$}`);try{let J=new K$,Y=new E("Fetching role details...");Y.start();let X=await J.getRole({RoleName:$}),Q=await J.listAttachedRolePolicies({RoleName:$}),Z=await J.listRolePolicies({RoleName:$});if(Y.succeed("Role details loaded"),W(`
584
- Role Information:`),W(` Name: ${X.RoleName}`),W(` ARN: ${X.Arn}`),W(` Path: ${X.Path}`),W(` Created: ${X.CreateDate?new Date(X.CreateDate).toLocaleString():"N/A"}`),X.Description)W(` Description: ${X.Description}`);if(X.MaxSessionDuration)W(` Max Session: ${X.MaxSessionDuration/3600} hours`);if(X.AssumeRolePolicyDocument){W(`
585
- Trust Policy:`);let z=JSON.parse(decodeURIComponent(X.AssumeRolePolicyDocument));console.log(JSON.stringify(z,null,2))}if(Q.AttachedPolicies&&Q.AttachedPolicies.length>0){W(`
586
- Attached Managed Policies:`);for(let z of Q.AttachedPolicies)W(` - ${z.PolicyName}`)}if(Z.PolicyNames&&Z.PolicyNames.length>0){W(`
587
- Inline Policies:`);for(let z of Z.PolicyNames)W(` - ${z}`)}if(X.Tags&&X.Tags.length>0){W(`
588
- Tags:`);for(let z of X.Tags)W(` ${z.Key}: ${z.Value}`)}}catch(J){K(`Failed to get role: ${J.message}`),process.exit(1)}}),G.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($)=>{R("IAM Policies");try{let J=new K$,Y=new E("Fetching policies...");Y.start();let Q=(await J.listPolicies({Scope:$.scope,PathPrefix:$.path,OnlyAttached:!1})).Policies||[];if($.prefix)Q=Q.filter((Z)=>Z.PolicyName?.startsWith($.prefix));if(Y.succeed(`Found ${Q.length} policy(s)`),Q.length===0){W("No IAM policies found");return}if(w(["Policy Name","ARN","Attachments","Created"],Q.slice(0,50).map((Z)=>[Z.PolicyName||"N/A",(Z.Arn||"N/A").substring(0,50),(Z.AttachmentCount||0).toString(),Z.CreateDate?new Date(Z.CreateDate).toLocaleDateString():"N/A"])),Q.length>50)W(`
589
- ... and ${Q.length-50} more policies`)}catch(J){K(`Failed to list policies: ${J.message}`),process.exit(1)}}),G.command("iam:policy <policyArn>","Show IAM policy details").option("--version <version>","Policy version ID").action(async($,J)=>{R("IAM Policy Details");try{let Y=new K$,X=new E("Fetching policy...");X.start();let Q=await Y.getPolicy({PolicyArn:$}),Z=J.version||Q.DefaultVersionId||"v1",z=await Y.getPolicyVersion({PolicyArn:$,VersionId:Z});if(X.succeed("Policy loaded"),W(`
590
- Policy Information:`),W(` Name: ${Q.PolicyName}`),W(` ARN: ${Q.Arn}`),W(` Path: ${Q.Path}`),W(` Created: ${Q.CreateDate?new Date(Q.CreateDate).toLocaleString():"N/A"}`),W(` Updated: ${Q.UpdateDate?new Date(Q.UpdateDate).toLocaleString():"N/A"}`),W(` Attachment Count: ${Q.AttachmentCount||0}`),W(` Version: ${Z}`),Q.Description)W(` Description: ${Q.Description}`);if(z.Document){W(`
591
- Policy Document:`);let F=JSON.parse(decodeURIComponent(z.Document));console.log(JSON.stringify(F,null,2))}}catch(Y){K(`Failed to get policy: ${Y.message}`),process.exit(1)}}),G.command("iam:users","List IAM users").option("--path <path>","Filter by path prefix",{default:"/"}).action(async($)=>{R("IAM Users");try{let J=new K$,Y=new E("Fetching users...");Y.start();let Q=(await J.listUsers({PathPrefix:$.path})).Users||[];if(Y.succeed(`Found ${Q.length} user(s)`),Q.length===0){W("No IAM users found");return}w(["User Name","User ID","Path","Created","Password Last Used"],Q.map((Z)=>[Z.UserName||"N/A",Z.UserId||"N/A",Z.Path||"/",Z.CreateDate?new Date(Z.CreateDate).toLocaleDateString():"N/A",Z.PasswordLastUsed?new Date(Z.PasswordLastUsed).toLocaleDateString():"Never"]))}catch(J){K(`Failed to list users: ${J.message}`),process.exit(1)}}),G.command("iam:user <userName>","Show IAM user details").action(async($)=>{R(`IAM User: ${$}`);try{let J=new K$,Y=new E("Fetching user details...");Y.start();let X=await J.getUser({UserName:$}),[Q,Z,z]=await Promise.all([J.listGroupsForUser({UserName:$}),J.listAttachedUserPolicies({UserName:$}),J.listAccessKeys({UserName:$})]);if(Y.succeed("User details loaded"),W(`
592
- User Information:`),W(` Name: ${X.UserName}`),W(` ARN: ${X.Arn}`),W(` User ID: ${X.UserId}`),W(` Path: ${X.Path}`),W(` Created: ${X.CreateDate?new Date(X.CreateDate).toLocaleString():"N/A"}`),X.PasswordLastUsed)W(` Password Last Used: ${new Date(X.PasswordLastUsed).toLocaleString()}`);if(Q.Groups&&Q.Groups.length>0){W(`
593
- Groups:`);for(let F of Q.Groups)W(` - ${F.GroupName}`)}if(Z.AttachedPolicies&&Z.AttachedPolicies.length>0){W(`
594
- Attached Policies:`);for(let F of Z.AttachedPolicies)W(` - ${F.PolicyName}`)}if(z.AccessKeyMetadata&&z.AccessKeyMetadata.length>0){W(`
595
- Access Keys:`);for(let F of z.AccessKeyMetadata)if(W(` - ${F.AccessKeyId}: ${F.Status}`),F.CreateDate)W(` Created: ${new Date(F.CreateDate).toLocaleString()}`)}if(X.Tags&&X.Tags.length>0){W(`
596
- Tags:`);for(let F of X.Tags)W(` ${F.Key}: ${F.Value}`)}}catch(J){K(`Failed to get user: ${J.message}`),process.exit(1)}}),G.command("iam:groups","List IAM groups").option("--path <path>","Filter by path prefix",{default:"/"}).action(async($)=>{R("IAM Groups");try{let J=new K$,Y=new E("Fetching groups...");Y.start();let Q=(await J.listGroups({PathPrefix:$.path})).Groups||[];if(Y.succeed(`Found ${Q.length} group(s)`),Q.length===0){W("No IAM groups found");return}w(["Group Name","Group ID","Path","Created"],Q.map((Z)=>[Z.GroupName||"N/A",Z.GroupId||"N/A",Z.Path||"/",Z.CreateDate?new Date(Z.CreateDate).toLocaleDateString():"N/A"]))}catch(J){K(`Failed to list groups: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("Simulate IAM Policy");try{let Y=new K$,X=J.action||await p("Action to simulate","s3:GetObject"),Q=J.resource||await p("Resource ARN","*");W(`
597
- Policy: ${$}`),W(`Action: ${X}`),W(`Resource: ${Q}`);let Z=new E("Running simulation...");Z.start();let z=await Y.simulatePrincipalPolicy({PolicySourceArn:$,ActionNames:[X],ResourceArns:[Q]});if(Z.succeed("Simulation complete"),z.EvaluationResults&&z.EvaluationResults.length>0)for(let F of z.EvaluationResults){let U=F.EvalDecision;if(W(`
598
- Result for ${F.EvalActionName}:`),U==="allowed")B(" Decision: ALLOWED");else if(U==="implicitDeny")N(" Decision: IMPLICIT DENY (no matching allow statement)");else K(" Decision: EXPLICIT DENY");if(F.MatchedStatements&&F.MatchedStatements.length>0){W(" Matched Statements:");for(let M of F.MatchedStatements)W(` - ${M.SourcePolicyId}: ${M.SourcePolicyType}`)}}}catch(Y){K(`Failed to simulate policy: ${Y.message}`),process.exit(1)}}),G.command("iam:whoami","Show current IAM identity").action(async()=>{R("Current IAM Identity");try{let $=new K$,J=new E("Fetching identity...");J.start();let{STSClient:Y}=await import("./chunk-gpe2prkm.js"),Q=await new Y("us-east-1").getCallerIdentity();J.succeed("Identity loaded"),W(`
599
- Caller Identity:`),W(` Account: ${Q.Account}`),W(` ARN: ${Q.Arn}`),W(` User ID: ${Q.UserId}`);let Z=Q.Arn||"";if(Z.includes(":user/"))W(" Type: IAM User");else if(Z.includes(":assumed-role/"))W(" Type: Assumed Role");else if(Z.includes(":root"))N(" Type: Root Account"),N(`
600
- Warning: Using root credentials is not recommended!`)}catch($){K(`Failed to get identity: ${$.message}`),process.exit(1)}})}async function q0(G){let{AWSClient:$}=await import("./chunk-dbgffky8.js");class J{client;region;constructor(Y){this.region=Y,this.client=new $}async jsonRpcRequest(Y,X){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.${Y}`},body:JSON.stringify(X)})}async describeTrails(){return this.jsonRpcRequest("DescribeTrails",{})}async getTrailStatus(Y){return this.jsonRpcRequest("GetTrailStatus",{Name:Y})}async lookupEvents(Y){return this.jsonRpcRequest("LookupEvents",{...Y,StartTime:Y.StartTime?.toISOString(),EndTime:Y.EndTime?.toISOString()})}async getEventSelectors(Y){return this.jsonRpcRequest("GetEventSelectors",{TrailName:Y})}async createTrail(Y){return this.jsonRpcRequest("CreateTrail",Y)}async startLogging(Y){return this.jsonRpcRequest("StartLogging",{Name:Y})}async stopLogging(Y){return this.jsonRpcRequest("StopLogging",{Name:Y})}async deleteTrail(Y){return this.jsonRpcRequest("DeleteTrail",{Name:Y})}}return new J(G)}function g5(G){G.command("audit:trails","List CloudTrail trails").option("--region <region>","AWS region").action(async($)=>{R("CloudTrail Trails");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=await q0(Y),Q=new E("Fetching trails...");Q.start();let z=(await X.describeTrails()).trailList||[];if(Q.succeed(`Found ${z.length} trail(s)`),z.length===0){W("No CloudTrail trails found"),W("Use `cloud audit:create` to create a new trail");return}w(["Name","Multi-Region","S3 Bucket","Log Validation","Home Region"],z.map((F)=>[F.Name||"N/A",F.IsMultiRegionTrail?"Yes":"No",F.S3BucketName||"N/A",F.LogFileValidationEnabled?"Yes":"No",F.HomeRegion||"N/A"]))}catch(J){K(`Failed to list trails: ${J.message}`),process.exit(1)}}),G.command("audit:trail <trailName>","Show CloudTrail trail details").option("--region <region>","AWS region").action(async($,J)=>{R(`CloudTrail: ${$}`);try{let Y=await x(),X=J.region||Y.project.region||"us-east-1",Q=await q0(X),Z=new E("Fetching trail details...");Z.start();let[z,F]=await Promise.all([Q.describeTrails(),Q.getTrailStatus($)]),U=(z.trailList||[]).find((M)=>M.Name===$);if(!U){Z.fail("Trail not found");return}if(Z.succeed("Trail details loaded"),W(`
601
- Trail Configuration:`),W(` Name: ${U.Name}`),W(` ARN: ${U.TrailARN}`),W(` Home Region: ${U.HomeRegion}`),W(` Multi-Region: ${U.IsMultiRegionTrail?"Yes":"No"}`),W(` Organization Trail: ${U.IsOrganizationTrail?"Yes":"No"}`),W(`
602
- Storage:`),W(` S3 Bucket: ${U.S3BucketName}`),U.S3KeyPrefix)W(` S3 Prefix: ${U.S3KeyPrefix}`);if(W(` Log Validation: ${U.LogFileValidationEnabled?"Enabled":"Disabled"}`),U.CloudWatchLogsLogGroupArn)W(`
603
- CloudWatch Logs:`),W(` Log Group: ${U.CloudWatchLogsLogGroupArn}`);if(U.KMSKeyId)W(`
604
- Encryption:`),W(` KMS Key: ${U.KMSKeyId}`);if(W(`
605
- Status:`),W(` Logging: ${F.IsLogging?"Active":"Stopped"}`),F.LatestDeliveryTime)W(` Latest Delivery: ${new Date(F.LatestDeliveryTime).toLocaleString()}`);if(F.LatestDeliveryError)N(` Latest Error: ${F.LatestDeliveryError}`);try{let M=await Q.getEventSelectors($);if(M.EventSelectors&&M.EventSelectors.length>0){W(`
606
- Event Selectors:`);for(let H of M.EventSelectors)if(W(` - Read/Write: ${H.ReadWriteType}`),W(` Management Events: ${H.IncludeManagementEvents?"Yes":"No"}`),H.DataResources&&H.DataResources.length>0)W(` Data Resources: ${H.DataResources.length} configured`)}}catch{}}catch(Y){K(`Failed to get trail: ${Y.message}`),process.exit(1)}}),G.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($)=>{R("CloudTrail Events");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=await q0(Y),Q=new E("Looking up events...");Q.start();let Z=new Date,F={StartTime:new Date(Z.getTime()-Number.parseInt($.hours)*60*60*1000),EndTime:Z,MaxResults:Number.parseInt($.limit)};if($.user)F.LookupAttributes=[{AttributeKey:"Username",AttributeValue:$.user}];else if($.event)F.LookupAttributes=[{AttributeKey:"EventName",AttributeValue:$.event}];else if($.resource)F.LookupAttributes=[{AttributeKey:"ResourceName",AttributeValue:$.resource}];let M=(await X.lookupEvents(F)).Events||[];if(Q.succeed(`Found ${M.length} event(s)`),M.length===0){W("No events found matching criteria");return}w(["Time","Event","User","Source IP","Resources"],M.map((H)=>[H.EventTime?new Date(H.EventTime).toLocaleString():"N/A",H.EventName||"N/A",H.Username||"N/A",H.SourceIPAddress||"N/A",(H.Resources||[]).map((j)=>j.ResourceName).join(", ").substring(0,30)||"-"]))}catch(J){K(`Failed to lookup events: ${J.message}`),process.exit(1)}}),G.command("audit:event <eventId>","Show CloudTrail event details").option("--region <region>","AWS region").action(async($,J)=>{R(`Event: ${$}`);try{let Y=await x(),X=J.region||Y.project.region||"us-east-1",Q=await q0(X),Z=new E("Fetching event...");Z.start();let F=(await Q.lookupEvents({LookupAttributes:[{AttributeKey:"EventId",AttributeValue:$}],MaxResults:1})).Events?.[0];if(!F){Z.fail("Event not found");return}if(Z.succeed("Event loaded"),W(`
607
- Event Information:`),W(` Event ID: ${F.EventId}`),W(` Event Name: ${F.EventName}`),W(` Event Time: ${F.EventTime?new Date(F.EventTime).toLocaleString():"N/A"}`),W(` Event Source: ${F.EventSource}`),W(` Username: ${F.Username}`),W(` Source IP: ${F.SourceIPAddress}`),W(` Access Key: ${F.AccessKeyId||"N/A"}`),F.Resources&&F.Resources.length>0){W(`
608
- Resources:`);for(let U of F.Resources)W(` - ${U.ResourceType}: ${U.ResourceName}`)}if(F.CloudTrailEvent){W(`
609
- Full Event Record:`);let U=JSON.parse(F.CloudTrailEvent);console.log(JSON.stringify(U,null,2))}}catch(Y){K(`Failed to get event: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Create CloudTrail Trail");try{if(!J.bucket){K("--bucket is required");return}let Y=await q0(J.region);if(W(`Trail Name: ${$}`),W(`S3 Bucket: ${J.bucket}`),W(`Multi-Region: ${J.multiRegion?"Yes":"No"}`),W(`Log Validation: ${J.validation?"Yes":"No"}`),W(`Global Events: ${J.globalEvents?"Yes":"No"}`),!await k(`
610
- Create this trail?`,!0)){W("Operation cancelled");return}let Q=new E("Creating trail...");Q.start();let Z=await Y.createTrail({Name:$,S3BucketName:J.bucket,S3KeyPrefix:J.prefix,IsMultiRegionTrail:J.multiRegion,EnableLogFileValidation:J.validation,IncludeGlobalServiceEvents:J.globalEvents??!0});Q.text="Starting logging...",await Y.startLogging($),Q.succeed("Trail created and logging started"),B(`
611
- Trail ARN: ${Z.TrailARN}`),W(`S3 Bucket: ${Z.S3BucketName}`)}catch(Y){K(`Failed to create trail: ${Y.message}`),process.exit(1)}}),G.command("audit:start <trailName>","Start CloudTrail logging").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Start CloudTrail Logging");try{let Y=await q0(J.region),X=new E("Starting logging...");X.start(),await Y.startLogging($),X.succeed("Logging started")}catch(Y){K(`Failed to start logging: ${Y.message}`),process.exit(1)}}),G.command("audit:stop <trailName>","Stop CloudTrail logging").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Stop CloudTrail Logging");try{let Y=await q0(J.region);if(N(`This will stop logging for trail: ${$}`),!await k(`
612
- Stop logging?`,!1)){W("Operation cancelled");return}let Q=new E("Stopping logging...");Q.start(),await Y.stopLogging($),Q.succeed("Logging stopped")}catch(Y){K(`Failed to stop logging: ${Y.message}`),process.exit(1)}}),G.command("audit:delete <trailName>","Delete a CloudTrail trail").option("--region <region>","AWS region",{default:"us-east-1"}).action(async($,J)=>{R("Delete CloudTrail Trail");try{let Y=await q0(J.region);if(N(`This will permanently delete trail: ${$}`),!await k(`
613
- Delete this trail?`,!1)){W("Operation cancelled");return}let Q=new E("Deleting trail...");Q.start(),await Y.deleteTrail($),Q.succeed("Trail deleted")}catch(Y){K(`Failed to delete trail: ${Y.message}`),process.exit(1)}})}function v5(G){G.command("status","Show overall infrastructure health dashboard").option("--region <region>","AWS region").action(async($)=>{R("Infrastructure Status Dashboard");try{let J=await x(),Y=$.region||J.project.region||"us-east-1";W(`Region: ${Y}`),W(`Project: ${J.project.name||"Unknown"}`),W("");let X=[],Q=new E("Checking EC2 instances...");Q.start();try{let{EC2Client:A}=await import("./chunk-dwr30vbv.js"),O=await new A(Y).describeInstances({Filters:[{Name:"instance-state-name",Values:["running","pending","stopping","stopped"]}]}),L=0,D=0,P=0;for(let b of O.Reservations||[])for(let _ of b.Instances||[])if(P++,_.State?.Name==="running")L++;else if(_.State?.Name==="stopped")D++;Q.succeed("EC2 instances checked"),X.push({name:"EC2 Instances",status:L>0?"OK":P>0?"WARN":"INFO",details:`${L} running, ${D} stopped, ${P} total`})}catch(A){Q.fail("EC2 check failed"),X.push({name:"EC2 Instances",status:"ERROR",details:A.message})}let Z=new E("Checking RDS instances...");Z.start();try{let{RDSClient:A}=await import("./chunk-yv9sth30.js"),L=(await new A(Y).describeDBInstances()).DBInstances||[],D=L.filter((P)=>P.DBInstanceStatus==="available").length;Z.succeed("RDS instances checked"),X.push({name:"RDS Databases",status:L.length>0&&D===L.length?"OK":L.length>0?"WARN":"INFO",details:`${D} available, ${L.length} total`})}catch(A){Z.fail("RDS check failed"),X.push({name:"RDS Databases",status:"ERROR",details:A.message})}let z=new E("Checking Lambda functions...");z.start();try{let{LambdaClient:A}=await import("./chunk-9pvsff8g.js"),L=(await new A(Y).listFunctions()).Functions||[];z.succeed("Lambda functions checked"),X.push({name:"Lambda Functions",status:"OK",details:`${L.length} function(s)`})}catch(A){z.fail("Lambda check failed"),X.push({name:"Lambda Functions",status:"ERROR",details:A.message})}let F=new E("Checking S3 buckets...");F.start();try{let{S3Client:A}=await import("./chunk-598yf4bs.js"),L=(await new A(Y).listBuckets()).Buckets||[];F.succeed("S3 buckets checked"),X.push({name:"S3 Buckets",status:"OK",details:`${L.length} bucket(s)`})}catch(A){F.fail("S3 check failed"),X.push({name:"S3 Buckets",status:"ERROR",details:A.message})}let U=new E("Checking CloudFront distributions...");U.start();try{let{CloudFrontClient:A}=await import("./chunk-e3xrwh9z.js"),O=await new A().listDistributions(),L=O.filter((D)=>D.Status==="Deployed").length;U.succeed("CloudFront checked"),X.push({name:"CloudFront",status:O.length>0&&L===O.length?"OK":O.length>0?"WARN":"INFO",details:`${L} deployed, ${O.length} total`})}catch(A){U.fail("CloudFront check failed"),X.push({name:"CloudFront",status:"ERROR",details:A.message})}let M=new E("Checking SQS queues...");M.start();try{let{SQSClient:A}=await import("./chunk-fvbxvq6b.js"),L=(await new A(Y).listQueues()).QueueUrls||[];M.succeed("SQS queues checked"),X.push({name:"SQS Queues",status:"OK",details:`${L.length} queue(s)`})}catch(A){M.fail("SQS check failed"),X.push({name:"SQS Queues",status:"ERROR",details:A.message})}let H=new E("Checking CloudFormation stacks...");H.start();try{let{CloudFormationClient:A}=await import("./chunk-m669wtgr.js"),L=(await new A(Y).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE"])).StackSummaries||[],D=L.filter((_)=>_.StackStatus==="CREATE_COMPLETE"||_.StackStatus==="UPDATE_COMPLETE").length,P=L.filter((_)=>_.StackStatus?.includes("IN_PROGRESS")).length,b=L.filter((_)=>_.StackStatus?.includes("ROLLBACK")).length;H.succeed("CloudFormation checked"),X.push({name:"CloudFormation",status:b>0?"WARN":P>0?"INFO":"OK",details:`${D} healthy, ${P} in progress, ${b} rolled back`})}catch(A){H.fail("CloudFormation check failed"),X.push({name:"CloudFormation",status:"ERROR",details:A.message})}let j=new E("Checking SSL certificates...");j.start();try{let{ACMClient:A}=await import("./chunk-vek8a0w6.js"),q=new A("us-east-1"),L=(await q.listCertificates()).CertificateSummaryList||[],D=await Promise.all(L.map((S)=>q.describeCertificate({CertificateArn:S.CertificateArn}))),P=D.filter((S)=>S.Status==="ISSUED").length,b=D.filter((S)=>S.Status==="PENDING_VALIDATION").length,_=D.filter((S)=>{if(S.NotAfter)return(new Date(S.NotAfter).getTime()-Date.now())/86400000<30;return!1}).length;j.succeed("SSL certificates checked"),X.push({name:"SSL Certificates",status:_>0?"WARN":b>0?"INFO":"OK",details:`${P} issued, ${b} pending${_>0?`, ${_} expiring soon`:""}`})}catch(A){j.fail("ACM check failed"),X.push({name:"SSL Certificates",status:"ERROR",details:A.message})}W(`
614
- `+"=".repeat(60)),W("HEALTH SUMMARY"),W("=".repeat(60)+`
615
- `);for(let A of X){let q="";if(A.status==="OK")q=`${Z0.green}[OK]${Z0.reset}`;else if(A.status==="WARN")q=`${Z0.yellow}[WARN]${Z0.reset}`;else if(A.status==="ERROR")q=`${Z0.red}[ERROR]${Z0.reset}`;else q=`${Z0.blue}[INFO]${Z0.reset}`;console.log(`${q} ${A.name.padEnd(20)} ${A.details}`)}let V=X.some((A)=>A.status==="ERROR"),T=X.some((A)=>A.status==="WARN");if(W(""),V)K("Some services have errors. Check the details above.");else if(T)N("Some services need attention. Check the warnings above.");else B("All services are healthy!")}catch(J){K(`Failed to get status: ${J.message}`),process.exit(1)}}),G.command("status:costs","Show current month cost summary").option("--region <region>","AWS region").action(async($)=>{R("Cost Summary");try{W("Fetching cost data from AWS Cost Explorer..."),W("");let J=new Date,Y=new Date(J.getFullYear(),J.getMonth(),1),X=new Date(J.getFullYear(),J.getMonth()+1,0);W(`Period: ${Y.toISOString().split("T")[0]} to ${X.toISOString().split("T")[0]}`),W(""),W("Note: Cost data requires AWS Cost Explorer API access."),W("Run `cloud cost` for detailed cost analysis.")}catch(J){K(`Failed to get cost summary: ${J.message}`),process.exit(1)}}),G.command("status:alarms","Show CloudWatch alarm status").option("--region <region>","AWS region").action(async($)=>{R("CloudWatch Alarms");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",{AWSClient:X}=await import("./chunk-dbgffky8.js");class Q{client;region;constructor(V){this.region=V,this.client=new X}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 Z=new Q(Y),z=new E("Fetching alarms...");z.start();let U=(await Z.describeAlarms()).MetricAlarms||[];if(z.succeed(`Found ${U.length} alarm(s)`),U.length===0){W("No CloudWatch alarms configured");return}let M=U.filter((V)=>V.StateValue==="ALARM"),H=U.filter((V)=>V.StateValue==="OK"),j=U.filter((V)=>V.StateValue==="INSUFFICIENT_DATA");if(W(""),W(`Alarming: ${M.length}`),W(`OK: ${H.length}`),W(`Insufficient Data: ${j.length}`),M.length>0){W(`
616
- Alarms in ALARM state:`);for(let V of M)K(` - ${V.AlarmName}: ${V.AlarmDescription||"No description"}`)}}catch(J){K(`Failed to get alarms: ${J.message}`),process.exit(1)}})}async function I$(G){let{AWSClient:$}=await import("./chunk-dbgffky8.js");class J{client;region;constructor(Y){this.region=Y,this.client=new $}async jsonRpcRequest(Y,X){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.${Y}`},body:JSON.stringify(X)})}async listBackupVaults(){return this.jsonRpcRequest("ListBackupVaults",{})}async listBackupPlans(){return this.jsonRpcRequest("ListBackupPlans",{})}async listRecoveryPointsByBackupVault(Y,X){return this.jsonRpcRequest("ListRecoveryPointsByBackupVault",{BackupVaultName:Y,...X})}async describeBackupVault(Y){return this.jsonRpcRequest("DescribeBackupVault",{BackupVaultName:Y})}async getBackupPlan(Y){return this.jsonRpcRequest("GetBackupPlan",{BackupPlanId:Y})}async createBackupVault(Y,X){return this.jsonRpcRequest("CreateBackupVault",{BackupVaultName:Y,...X})}async deleteBackupVault(Y){return this.jsonRpcRequest("DeleteBackupVault",{BackupVaultName:Y})}async createBackupPlan(Y){return this.jsonRpcRequest("CreateBackupPlan",{BackupPlan:Y})}async deleteBackupPlan(Y){return this.jsonRpcRequest("DeleteBackupPlan",{BackupPlanId:Y})}async startBackupJob(Y){return this.jsonRpcRequest("StartBackupJob",Y)}async startRestoreJob(Y){return this.jsonRpcRequest("StartRestoreJob",Y)}async describeBackupJob(Y){return this.jsonRpcRequest("DescribeBackupJob",{BackupJobId:Y})}async describeRestoreJob(Y){return this.jsonRpcRequest("DescribeRestoreJob",{RestoreJobId:Y})}async listBackupJobs(Y){return this.jsonRpcRequest("ListBackupJobs",Y||{})}async listRestoreJobs(Y){return this.jsonRpcRequest("ListRestoreJobs",Y||{})}async createBackupSelection(Y,X){return this.jsonRpcRequest("CreateBackupSelection",{BackupPlanId:Y,BackupSelection:X})}async listBackupSelections(Y){return this.jsonRpcRequest("ListBackupSelections",{BackupPlanId:Y})}}return new J(G)}function m5(G){G.command("backup:vaults","List backup vaults").option("--region <region>","AWS region").action(async($)=>{R("Backup Vaults");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=await I$(Y),Q=new E("Fetching vaults...");Q.start();let z=(await X.listBackupVaults()).BackupVaultList||[];if(Q.succeed(`Found ${z.length} vault(s)`),z.length===0){W("No backup vaults found"),W("Use `cloud backup:create-vault` to create a new vault");return}w(["Vault Name","Recovery Points","Created","Encrypted"],z.map((F)=>[F.BackupVaultName||"N/A",(F.NumberOfRecoveryPoints||0).toString(),F.CreationDate?new Date(F.CreationDate).toLocaleDateString():"N/A",F.EncryptionKeyArn?"Yes":"Default"]))}catch(J){K(`Failed to list vaults: ${J.message}`),process.exit(1)}}),G.command("backup:list","List backup plans").option("--region <region>","AWS region").action(async($)=>{R("Backup Plans");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=await I$(Y),Q=new E("Fetching backup plans...");Q.start();let z=(await X.listBackupPlans()).BackupPlansList||[];if(Q.succeed(`Found ${z.length} backup plan(s)`),z.length===0){W("No backup plans found"),W("Use `cloud backup:create` to create a backup plan");return}w(["Plan Name","Plan ID","Version","Created"],z.map((F)=>[F.BackupPlanName||"N/A",F.BackupPlanId||"N/A",F.VersionId?.substring(0,8)||"N/A",F.CreationDate?new Date(F.CreationDate).toLocaleDateString():"N/A"]))}catch(J){K(`Failed to list backup plans: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R(`Recovery Points: ${$}`);try{let Y=await x(),X=J.region||Y.project.region||"us-east-1",Q=await I$(X),Z=new E("Fetching recovery points...");Z.start();let F=(await Q.listRecoveryPointsByBackupVault($,{MaxResults:Number.parseInt(J.limit)})).RecoveryPoints||[];if(Z.succeed(`Found ${F.length} recovery point(s)`),F.length===0){W("No recovery points found");return}w(["Resource","Status","Created","Size","Lifecycle"],F.map((U)=>[U.ResourceArn?.split(":").pop()||"N/A",U.Status||"N/A",U.CreationDate?new Date(U.CreationDate).toLocaleString():"N/A",U.BackupSizeInBytes?qY(U.BackupSizeInBytes):"N/A",U.Lifecycle?.DeleteAfterDays?`${U.Lifecycle.DeleteAfterDays} days`:"Indefinite"]))}catch(Y){K(`Failed to list recovery points: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Create Backup Vault");try{let Y=await I$(J.region);if(W(`Vault Name: ${$}`),W(`Region: ${J.region}`),W(`Encryption: ${J.kmsKey?"Custom KMS":"AWS Managed"}`),!await k(`
617
- Create this vault?`,!0)){W("Operation cancelled");return}let Q=new E("Creating vault...");Q.start(),await Y.createBackupVault($,{EncryptionKeyArn:J.kmsKey}),Q.succeed("Vault created"),B(`
618
- Vault: ${$}`)}catch(Y){K(`Failed to create vault: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Create Backup Plan");try{let Y=await I$(J.region);if(W(`Plan Name: ${$}`),W(`Vault: ${J.vault}`),W(`Schedule: ${J.schedule}`),W(`Retention: ${J.retention} days`),!await k(`
619
- Create this backup plan?`,!0)){W("Operation cancelled");return}let Q=new E("Creating backup plan...");Q.start();let Z={DeleteAfterDays:Number.parseInt(J.retention)};if(J.lifecycleCold)Z.MoveToColdStorageAfterDays=Number.parseInt(J.lifecycleCold);let z=await Y.createBackupPlan({BackupPlanName:$,Rules:[{RuleName:`${$}-daily`,TargetBackupVaultName:J.vault,ScheduleExpression:J.schedule,StartWindowMinutes:60,CompletionWindowMinutes:180,Lifecycle:Z}]});Q.succeed("Backup plan created"),B(`
620
- Plan ID: ${z.BackupPlanId}`),W("\nNote: Add resource selections with `cloud backup:add-selection`")}catch(Y){K(`Failed to create backup plan: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Add Backup Selection");try{if(!J.role){K("--role is required (IAM role ARN for AWS Backup)");return}if(!J.resource&&!J.tagKey){K("Specify --resource or --tag-key/--tag-value");return}let Y=await I$(J.region),Q={SelectionName:J.name||`selection-${Date.now()}`,IamRoleArn:J.role};if(J.resource)Q.Resources=[J.resource],W(`Resource: ${J.resource}`);if(J.tagKey&&J.tagValue)Q.ListOfTags=[{ConditionType:"STRINGEQUALS",ConditionKey:J.tagKey,ConditionValue:J.tagValue}],W(`Tag: ${J.tagKey}=${J.tagValue}`);if(!await k(`
621
- Add this selection?`,!0)){W("Operation cancelled");return}let z=new E("Adding selection...");z.start();let F=await Y.createBackupSelection($,Q);z.succeed("Selection added"),B(`
622
- Selection ID: ${F.SelectionId}`)}catch(Y){K(`Failed to add selection: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Start Backup Job");try{if(!J.role){K("--role is required (IAM role ARN for AWS Backup)");return}let Y=await I$(J.region);if(W(`Resource: ${$}`),W(`Vault: ${J.vault}`),!await k(`
623
- Start backup?`,!0)){W("Operation cancelled");return}let Q=new E("Starting backup job...");Q.start();let Z=await Y.startBackupJob({BackupVaultName:J.vault,ResourceArn:$,IamRoleArn:J.role,IdempotencyToken:`cli-${Date.now()}`});Q.succeed("Backup job started"),B(`
624
- Job ID: ${Z.BackupJobId}`),W("Use `cloud backup:jobs` to check status")}catch(Y){K(`Failed to start backup: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Start Restore Job");try{if(!J.role){K("--role is required (IAM role ARN for AWS Backup)");return}let Y=await I$(J.region);W(`Recovery Point: ${$}`);let X={};if(J.metadata)X=JSON.parse(J.metadata);if(N(`
625
- Restore will create new resources. Review carefully.`),!await k(`
626
- Start restore?`,!1)){W("Operation cancelled");return}let Z=new E("Starting restore job...");Z.start();let z=await Y.startRestoreJob({RecoveryPointArn:$,IamRoleArn:J.role,Metadata:X,IdempotencyToken:`cli-${Date.now()}`});Z.succeed("Restore job started"),B(`
627
- Job ID: ${z.RestoreJobId}`),W("Use `cloud backup:restore-jobs` to check status")}catch(Y){K(`Failed to start restore: ${Y.message}`),process.exit(1)}}),G.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($)=>{R("Backup Jobs");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=await I$(Y),Q=new E("Fetching backup jobs...");Q.start();let z=(await X.listBackupJobs({ByState:$.state,MaxResults:50})).BackupJobs||[];if(Q.succeed(`Found ${z.length} job(s)`),z.length===0){W("No backup jobs found");return}w(["Job ID","Resource","State","Started","Size"],z.map((F)=>[F.BackupJobId?.substring(0,16)||"N/A",F.ResourceArn?.split(":").pop()||"N/A",F.State||"N/A",F.CreationDate?new Date(F.CreationDate).toLocaleString():"N/A",F.BackupSizeInBytes?qY(F.BackupSizeInBytes):"N/A"]))}catch(J){K(`Failed to list backup jobs: ${J.message}`),process.exit(1)}}),G.command("backup:restore-jobs","List recent restore jobs").option("--region <region>","AWS region").option("--status <status>","Filter by status").action(async($)=>{R("Restore Jobs");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=await I$(Y),Q=new E("Fetching restore jobs...");Q.start();let z=(await X.listRestoreJobs({ByStatus:$.status,MaxResults:50})).RestoreJobs||[];if(Q.succeed(`Found ${z.length} job(s)`),z.length===0){W("No restore jobs found");return}w(["Job ID","Resource","Status","Started","Completed"],z.map((F)=>[F.RestoreJobId?.substring(0,16)||"N/A",F.CreatedResourceArn?.split(":").pop()||"Pending",F.Status||"N/A",F.CreationDate?new Date(F.CreationDate).toLocaleString():"N/A",F.CompletionDate?new Date(F.CompletionDate).toLocaleString():"-"]))}catch(J){K(`Failed to list restore jobs: ${J.message}`),process.exit(1)}}),G.command("backup:schedule","Show backup schedule overview").option("--region <region>","AWS region").action(async($)=>{R("Backup Schedule Overview");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",X=await I$(Y),Q=new E("Fetching backup plans...");Q.start();let z=(await X.listBackupPlans()).BackupPlansList||[];if(Q.succeed(`Found ${z.length} backup plan(s)`),z.length===0){W("No backup plans configured");return}for(let F of z){W(`
628
- ${F.BackupPlanName}`),W("=".repeat(40));try{let M=(await X.getBackupPlan(F.BackupPlanId)).BackupPlan?.Rules||[];for(let j of M)if(W(` Rule: ${j.RuleName}`),W(` Schedule: ${j.ScheduleExpression}`),W(` Vault: ${j.TargetBackupVaultName}`),j.Lifecycle?.DeleteAfterDays)W(` Retention: ${j.Lifecycle.DeleteAfterDays} days`);let H=await X.listBackupSelections(F.BackupPlanId);if(H.BackupSelectionsList&&H.BackupSelectionsList.length>0)W(` Selections: ${H.BackupSelectionsList.length}`)}catch{W(" (Unable to load plan details)")}}}catch(J){K(`Failed to get schedule: ${J.message}`),process.exit(1)}})}function qY(G){if(G===0)return"0 B";let $=1024,J=["B","KB","MB","GB","TB"],Y=Math.floor(Math.log(G)/Math.log($));return`${Number.parseFloat((G/$**Y).toFixed(2))} ${J[Y]}`}async function wQ(G){let $=await x(),J=u(process.cwd(),$),Y=G??Object.keys($.environments??{})[0]??"production",X=J.environments.get(Y);if(!X)throw J.store.close(),Error(`Environment ${Y} was not found.`);let Q=new v$(J.store,{workerId:`cli:${process.pid}`}),Z=new LY(J.store),z=new TY(Z,Q),F=new y1(J.store),U=new C1(J.store,Q$(process.cwd())),M=J.store.getActorByExternalId("system","cli")??J.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:$,controlPlane:J,env:Y,environmentRecord:X,queue:Q,store:Z,coordinator:z,dataServices:F,secrets:U,actor:M}}async function r$(G,$){let J=await wQ(G);try{return await $(J)}finally{J.controlPlane.store.close()}}async function a$(G){try{await G()}catch($){K($ instanceof Error?$.message:String($))}}function W0(G,$,J,Y){G.controlPlane.store.appendEvent({organizationId:G.controlPlane.organization.id,projectId:G.controlPlane.project.id,resourceId:Y,actorId:G.actor.id,type:`backup.${$}`,payload:J})}function DQ(G,$){let J=G.store.getPolicy($)??G.store.listPolicies(G.controlPlane.project.id,G.environmentRecord.id).find((Y)=>Y.name===$);if(!J||J.environmentId!==G.environmentRecord.id)throw Error(`Backup policy ${$} was not found in ${G.env}.`);return J}function Z6(G,$){let J=G.store.getRecoveryPoint($),Y=G.store.listRecoveryPoints(G.controlPlane.project.id).filter((Q)=>Q.id.startsWith($)),X=J??(Y.length===1?Y[0]:void 0);if(!X)throw Error(Y.length>1?`Recovery point prefix ${$} is ambiguous.`:`Recovery point ${$} was not found.`);return X}function RY(G,$){let J=G.store.getDestination($)??G.store.listDestinations(G.controlPlane.project.id).find((Y)=>Y.name===$);if(!J)throw Error(`Backup destination ${$} was not found.`);return J}function xQ(G,$){return G.kind==="volume"?{volumeName:$}:G.kind==="logical_database"?{targetId:$,dataServiceId:$}:{targetId:$}}function NQ(G,$=[]){let J=new Map($.map((Y)=>[Y.id,Y.name]));return G.map((Y)=>[Y.id,Y.status,Y.verificationState,Y.kind,J.get(Y.policyId??"")??"manual",Y.pointInTime,Y.sizeBytes?`${Y.sizeBytes} B`:"provider",Y.held?"hold":Y.pinned?"pinned":"retention"])}function d5(G){G.command("recovery:list","Show backup coverage, destination health, and recent recovery points").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async($)=>a$(async()=>r$($.env,async(J)=>{let Y=J.store.listPolicies(J.controlPlane.project.id,J.environmentRecord.id),X=new Set(Y.map((z)=>z.id)),Q=J.store.listRecoveryPoints(J.controlPlane.project.id).filter((z)=>!z.policyId||X.has(z.policyId)),Z={coverage:J.store.coverage(J.controlPlane.project.id).filter((z)=>X.has(z.policy.id)),destinations:J.store.listDestinations(J.controlPlane.project.id).map((z)=>({...z,credentialRef:void 0,encryptionKeyRef:void 0,credentialsConfigured:!!z.credentialRef,clientEncryptionConfigured:!!z.encryptionKeyRef})),policies:Y,recoveryPoints:Q,jobs:J.store.listJobs(J.controlPlane.project.id).filter((z)=>!z.policyId||X.has(z.policyId))};if($.json)W(JSON.stringify(Z,null,2));else w(["Policy","RPO","Destination","Latest point","Verification"],Z.coverage.map((z)=>[z.policy.name,z.missedRpo?"MISSED":"protected",z.destinationHealthy?"healthy":"attention",z.lastRecoveryPoint?.pointInTime??"none",z.lastRecoveryPoint?.verificationState??"none"])),w(["ID","State","Verification","Kind","Policy","Point in time","Size","Protection"],NQ(Q,Y))}))),G.command("recovery:jobs","List durable backup, verification, restore, drill, and cleanup jobs").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async($)=>a$(async()=>r$($.env,async(J)=>{let Y=new Set(J.store.listPolicies(J.controlPlane.project.id,J.environmentRecord.id).map((Q)=>Q.id)),X=J.store.listJobs(J.controlPlane.project.id).filter((Q)=>!Q.policyId||Y.has(Q.policyId));if($.json)W(JSON.stringify(X,null,2));else w(["ID","Kind","State","Phase","Operation","Started","Finished","Error"],X.map((Q)=>[Q.id,Q.kind,Q.status,String(Q.progress.phase??"queued"),Q.operationId??"—",Q.startedAt??"—",Q.finishedAt??"—",Q.error??"—"]))}))),G.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($,J)=>a$(async()=>r$(J.env,async(Y)=>{if(!["aws_s3","s3_compatible","aws_backup"].includes(J.provider??""))throw Error("--provider must be aws_s3, s3_compatible, or aws_backup.");if(!["provider","client_side","both"].includes(J.encryption??""))throw Error("--encryption must be provider, client_side, or both.");let X=`secret://data-services/backups/${Y.controlPlane.project.id}/${$}`,Q=[];try{let Z,z;if(J.credentialsEnv){let U=process.env[J.credentialsEnv];if(!U)throw Error(`${J.credentialsEnv} is empty.`);Z=`${X}/credentials`,await Y.secrets.put(Z,U),Q.push(Z)}if(J.encryption!=="provider"){if(!J.encryptionKeyEnv)throw Error("Client encryption requires --encryption-key-env.");let U=process.env[J.encryptionKeyEnv];if(!U||U.length<32)throw Error(`${J.encryptionKeyEnv} must contain at least 32 characters.`);z=`${X}/encryption-key`,await Y.secrets.put(z,U),Q.push(z)}let F=Y.store.createDestination({organizationId:Y.controlPlane.organization.id,projectId:Y.controlPlane.project.id,name:$,provider:J.provider,endpoint:J.endpoint,endpointPolicy:J.allowPrivate?"allow_private":"public_https",bucket:J.bucket,prefix:J.prefix??"",region:J.region??Y.config.project.region,forcePathStyle:!!J.pathStyle,credentialRef:Z,encryption:J.encryption,encryptionKeyRef:z,immutability:{objectLock:!!J.lockDays,defaultRetentionDays:J.lockDays?Number(J.lockDays):void 0},status:"untested"});W0(Y,"destination_created",{destinationId:F.id,provider:F.provider}),B(`Created destination ${F.name} (${F.id}); test it with cloud recovery:destination:test ${F.name}`)}catch(Z){for(let z of Q)await Y.secrets.remove(z).catch(()=>{});throw Z}}))),G.command("recovery:destination:test <destination>","Write, read, verify, and clean a destination health object").option("--env <environment>","Target environment").action(async($,J)=>a$(async()=>r$(J.env,async(Y)=>{let X=RY(Y,$);if(X.provider==="aws_backup")throw Error("AWS Backup is verified by creating a provider recovery point.");try{await new EY(Y.secrets).test(X),Y.store.recordDestinationTest(X.id,{ok:!0}),W0(Y,"destination_tested",{destinationId:X.id,ok:!0}),B(`${X.name} passed write, read, checksum, and cleanup checks.`)}catch(Q){let Z=Q instanceof Error?Q.message:String(Q);throw Y.store.recordDestinationTest(X.id,{ok:!1,error:Z}),W0(Y,"destination_tested",{destinationId:X.id,ok:!1}),Q}}))),G.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($,J)=>a$(async()=>r$(J.env,async(Y)=>{if(!J.destination)throw Error("--destination is required.");if(!["managed_database","logical_database","volume","files","control_plane","infrastructure"].includes(J.kind??""))throw Error("--kind must be managed_database, logical_database, volume, files, control_plane, or infrastructure.");let X=RY(Y,J.destination),Q=Y.dataServices.list(Y.controlPlane.project.id,Y.environmentRecord.id),Z=J.dataService?Y.dataServices.get(J.dataService)??Q.find((j)=>j.name===J.dataService):void 0,z=Y.controlPlane.store.listResources(Y.controlPlane.project.id,Y.environmentRecord.id),F=J.resource?z.find((j)=>j.id===J.resource||j.slug===J.resource):void 0,U=J.include?Array.isArray(J.include)?J.include:[J.include]:[],M=J.exclude?Array.isArray(J.exclude)?J.exclude:[J.exclude]:[];if(["managed_database","logical_database"].includes(J.kind??"")&&!Z)throw Error("--data-service must identify a service in this environment.");if(J.kind==="volume"&&(!F||!J.volume))throw Error("Volume policies require --resource and --volume.");if(J.kind==="files"&&(!F||!U.length))throw Error("File policies require --resource and at least one --include path.");if(J.kind==="infrastructure"&&(!U.some((j)=>j.startsWith("resource:arn:"))||!U.some((j)=>j.startsWith("role:arn:"))))throw Error("Infrastructure policies require --include resource:<arn> and --include role:<arn>.");let H=Y.store.createPolicy({organizationId:Y.controlPlane.organization.id,projectId:Y.controlPlane.project.id,environmentId:Y.environmentRecord.id,resourceId:F?.id,dataServiceId:Z?.id,destinationId:X.id,name:$,resourceKind:J.kind,schedule:J.schedule??"daily",timezone:J.timezone??"UTC",retention:{keepLast:Number(J.keep)||7,expireAfterDays:Number(J.expire)||30},compression:J.compression,encryption:"destination",includePatterns:J.volume?[J.volume,...U]:U,excludePatterns:M,expectedRpoMinutes:Number(J.rpo)||1440,expectedRtoMinutes:Number(J.rto)||120,enabled:!J.disabled});W0(Y,"policy_created",{policyId:H.id,resourceKind:H.resourceKind},H.resourceId),B(`Created policy ${H.name} (${H.id}); next run ${H.nextRunAt}`)}))),G.command("recovery:run <policy>","Queue an on-demand run of a recovery policy").option("--env <environment>","Target environment").action(async($,J)=>a$(async()=>r$(J.env,async(Y)=>{let X=DQ(Y,$),Q=Y.coordinator.enqueueBackup(X,new Date().toISOString(),Y.actor.id);W0(Y,"run_queued",{policyId:X.id,backupJobId:Q.id},X.resourceId),B(`Queued backup job ${Q.id}; inspect it with cloud recovery:jobs`)}))),G.command("recovery:verify <point>","Queue independent verification of a recovery point").option("--env <environment>","Target environment").action(async($,J)=>a$(async()=>r$(J.env,async(Y)=>{let X=Z6(Y,$),Q=Y.coordinator.enqueueVerification(X);W0(Y,"verification_queued",{recoveryPointId:X.id,backupJobId:Q.id},X.resourceId),B(`Queued verification job ${Q.id}`)}))),G.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($,J,Y)=>a$(async()=>r$(Y.env,async(X)=>{if(!["isolated","in_place"].includes(Y.mode??""))throw Error("--mode must be isolated or in_place.");if(Y.drill&&Y.mode==="in_place")throw Error("Recovery drills require --mode isolated.");let Q=Z6(X,$),Z={mode:Y.mode,target:{...xQ(Q,J),...Y.metadata?{metadata:JSON.parse(Y.metadata)}:{},inPlace:Y.mode==="in_place"},targetName:J,confirm:Y.confirm,recentAuth:!0,downtimeAcknowledged:!!Y.ackDowntime,safetyBackupId:Y.safety?Z6(X,Y.safety).id:void 0},z=X.coordinator.planRestore(Q,Z);if(!Y.execute){W(JSON.stringify({...z,productionExecutionCreated:!1},null,2));return}let F=X.coordinator.enqueueRestore(Q,{...Z,drill:!!Y.drill,actorId:X.actor.id});W0(X,Y.drill?"drill_queued":"restore_queued",{recoveryPointId:Q.id,backupJobId:F.id,mode:Z.mode,target:J},Q.resourceId),B(`Queued ${Y.drill?"drill":"restore"} job ${F.id}`)}))),G.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($,J)=>a$(async()=>r$(J.env,async(Y)=>{let X=Z6(Y,$);if(![J.pin,J.unpin,J.hold,J.releaseHold].filter(Boolean).length)throw Error("Choose --pin, --unpin, --hold, or --release-hold.");let Q=Y.store.updateRecoveryPoint(X.id,{pinned:J.pin?!0:J.unpin?!1:X.pinned,held:J.hold?!0:J.releaseHold?!1:X.held});W0(Y,"protection_updated",{recoveryPointId:X.id,pinned:Q.pinned,held:Q.held},X.resourceId),B(`${Q.id}: pinned=${Q.pinned} held=${Q.held}`)}))),G.command("recovery:retention","Queue deletion of expired, unlocked, unheld recovery points").option("--env <environment>","Target environment").action(async($)=>a$(async()=>r$($.env,async(J)=>{let Y=J.coordinator.enqueueRetention();W0(J,"retention_queued",{jobs:Y.length}),B(`Queued ${Y.length} retention cleanup job${Y.length===1?"":"s"}.`)})))}function c5(G){G.command("api:list","List all API Gateway APIs").option("--region <region>","AWS region").action(async($)=>{R("API Gateway APIs");try{let J=await x(),Y=$.region||J.project.region||"us-east-1",{LambdaClient:X}=await import("./chunk-9pvsff8g.js"),Q=new X(Y),Z=new E("Fetching APIs...");Z.start();let{CloudFormationClient:z}=await import("./chunk-m669wtgr.js"),M=(await new z(Y).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE"])).StackSummaries.filter((H)=>H.StackName?.includes("api")||H.StackName?.includes("Api")||H.StackName?.includes("API"));if(Z.succeed("APIs listed"),W(`
629
- API-related CloudFormation stacks:`),M.length===0)W("No API Gateway stacks found"),W(`
630
- To create an API, you can:`),W(" 1. Use cloud.config.ts to define your API"),W(" 2. Deploy with `cloud deploy`");else w(["Stack Name","Status","Created"],M.map((H)=>[H.StackName||"N/A",H.StackStatus||"N/A",H.CreationTime?new Date(H.CreationTime).toLocaleDateString():"N/A"]));W("\nTip: Use AWS Console or `aws apigateway get-rest-apis` for detailed API listing")}catch(J){K(`Failed to list APIs: ${J.message}`),process.exit(1)}}),G.command("api:describe <apiId>","Show API Gateway API details").option("--region <region>","AWS region").action(async($,J)=>{R(`API: ${$}`);try{let Y=await x(),X=J.region||Y.project.region||"us-east-1";W(`API ID: ${$}`),W(`Region: ${X}`),W(""),W("For detailed API information, use AWS CLI:"),W(` aws apigateway get-rest-api --rest-api-id ${$} --region ${X}`),W(` aws apigateway get-resources --rest-api-id ${$} --region ${X}`),W(` aws apigateway get-stages --rest-api-id ${$} --region ${X}`)}catch(Y){K(`Failed to describe API: ${Y.message}`),process.exit(1)}}),G.command("api:stages <apiId>","List API stages").option("--region <region>","AWS region").action(async($,J)=>{R(`API Stages: ${$}`);try{let Y=await x(),X=J.region||Y.project.region||"us-east-1";W(`API ID: ${$}`),W(`Region: ${X}`),W(""),W("Common stages:"),W(" - prod (production)"),W(" - staging"),W(" - dev (development)"),W(""),W("For detailed stage information, use AWS CLI:"),W(` aws apigateway get-stages --rest-api-id ${$} --region ${X}`)}catch(Y){K(`Failed to list stages: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Deploy API");try{if(W(`API ID: ${$}`),W(`Stage: ${J.stage}`),!await k(`
631
- Deploy to this stage?`,!0)){W("Operation cancelled");return}if(W(""),W("To deploy an API Gateway API:"),W(" aws apigateway create-deployment \\"),W(` --rest-api-id ${$} \\`),W(` --stage-name ${J.stage} \\`),W(` --region ${J.region}`),J.description)W(` --description "${J.description}"`)}catch(Y){K(`Failed to deploy API: ${Y.message}`),process.exit(1)}}),G.command("api:domains","List custom domain names").option("--region <region>","AWS region").action(async($)=>{R("API Gateway Custom Domains");try{let J=await x(),Y=$.region||J.project.region||"us-east-1";W(`Region: ${Y}`),W(""),W("To list custom domains, use AWS CLI:"),W(` aws apigateway get-domain-names --region ${Y}`),W(""),W("To create a custom domain:"),W(" 1. Request or import an SSL certificate in ACM"),W(" 2. Create a custom domain in API Gateway"),W(" 3. Create a base path mapping to your API"),W(" 4. Add a DNS record pointing to the distribution")}catch(J){K(`Failed to list domains: ${J.message}`),process.exit(1)}}),G.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($,J)=>{R("API Usage Statistics");try{let Y=await x(),X=J.region||Y.project.region||"us-east-1";W(`API ID: ${$}`),W(`Stage: ${J.stage}`),W(`Period: Last ${J.days} days`),W("");let Q=new Date,Z=new Date;Z.setDate(Z.getDate()-Number.parseInt(J.days)),W("To view API metrics in CloudWatch:"),W(" aws cloudwatch get-metric-statistics \\"),W(" --namespace AWS/ApiGateway \\"),W(" --metric-name Count \\"),W(` --dimensions Name=ApiName,Value=${$} Name=Stage,Value=${J.stage} \\`),W(` --start-time ${Z.toISOString()} \\`),W(` --end-time ${Q.toISOString()} \\`),W(" --period 86400 \\"),W(" --statistics Sum \\"),W(` --region ${X}`),W(""),W("Available metrics:"),W(" - Count: Total API calls"),W(" - Latency: Response latency"),W(" - 4XXError: Client errors"),W(" - 5XXError: Server errors"),W(" - IntegrationLatency: Backend latency")}catch(Y){K(`Failed to get usage: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("Export API Specification");try{W(`API ID: ${$}`),W(`Stage: ${J.stage}`),W(`Format: ${J.format==="oas30"?"OpenAPI 3.0":"Swagger 2.0"}`),W("");let Y=J.format==="oas30"?"oas30":"swagger";W("To export the API specification:"),W(" aws apigateway get-export \\"),W(` --rest-api-id ${$} \\`),W(` --stage-name ${J.stage} \\`),W(` --export-type ${Y} \\`),W(" --accepts application/json \\"),W(` --region ${J.region} \\`),W(` ${J.output||"api-spec.json"}`)}catch(Y){K(`Failed to export API: ${Y.message}`),process.exit(1)}}),G.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($,J)=>{R("API Gateway Logs");try{let Y=await x(),X=J.region||Y.project.region||"us-east-1",Q=`API-Gateway-Execution-Logs_${$}/${J.stage}`;if(W(`Log Group: ${Q}`),W(""),W("To view logs:"),W(" aws logs filter-log-events \\"),W(` --log-group-name "${Q}" \\`),W(` --region ${X}`),J.tail)W(""),W("For real-time log tailing, use:"),W(` aws logs tail "${Q}" --follow --region ${X}`);W(""),W("Note: Ensure logging is enabled for the API stage."),W("You can enable it in the stage settings.")}catch(Y){K(`Failed to get logs: ${Y.message}`),process.exit(1)}}),G.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($,J,Y)=>{R("Test API Endpoint");try{let X=await x(),Q=Y.region||X.project.region||"us-east-1",Z=`https://${$}.execute-api.${Q}.amazonaws.com/${Y.stage}${J.startsWith("/")?J:`/${J}`}`;W(`URL: ${Z}`),W(`Method: ${Y.method}`);let z={"Content-Type":"application/json"};if(Y.header){let T=Array.isArray(Y.header)?Y.header:[Y.header];for(let A of T){let[q,...O]=A.split(":");z[q.trim()]=O.join(":").trim()}}W("Headers:");for(let[T,A]of Object.entries(z))W(` ${T}: ${A}`);if(Y.body)W(`Body: ${Y.body}`);if(!await k(`
632
- Send request?`,!0)){W("Operation cancelled");return}let U=new E("Sending request...");U.start();let M=Date.now(),H=await fetch(Z,{method:Y.method,headers:z,body:Y.body}),j=Date.now()-M,V=await H.text();U.succeed(`Response received (${j}ms)`),W(`
633
- Status: ${H.status} ${H.statusText}`),W(`
634
- Response Headers:`),H.headers.forEach((T,A)=>{W(` ${A}: ${T}`)}),W(`
635
- Response Body:`);try{let T=JSON.parse(V);console.log(JSON.stringify(T,null,2))}catch{console.log(V)}}catch(X){K(`Failed to test API: ${X.message}`),process.exit(1)}})}async function _0(G){let $=await x(),J=u(process.cwd(),$),Y=new v$(J.store,{workerId:`cli:${process.pid}`});try{return await G(Y,J.project.id,J.organization.id)}finally{J.store.close()}}function PQ(G){if(!G)return;if(!["queued","running","succeeded","failed","cancelled","timed_out"].includes(G))throw Error("State must be queued, running, succeeded, failed, cancelled, or timed_out");return G}function SQ(G,$){if(!G)return"—";let J=Math.max(0,new Date($??Date.now()).getTime()-new Date(G).getTime());return J<1000?`${J}ms`:J<60000?`${Math.round(J/1000)}s`:`${Math.round(J/60000)}m`}function u5(G){G.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($)=>{try{await _0(async(J,Y)=>{let X=J.list({projectId:$.allProjects?void 0:Y,state:PQ($.state),limit:500});w(["ID","State","Operation","Target","Attempt","Step / blocked","Queued","Duration"],X.map(({operation:Q,job:Z,approximatePosition:z})=>[Q.id,Q.state,Q.kind,Q.resourceId??Q.environmentId??Q.projectId??"global",`${Q.attempt}/${Z.maxAttempts}`,Z.blockedReason??Z.currentStep??(z?`${z.ahead} ahead`:"—"),Q.createdAt,SQ(Q.startedAt,Q.finishedAt)]))})}catch(J){K(J instanceof Error?J.message:String(J))}}),G.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($,J)=>{try{await _0(async(Y)=>{let X=Y.view($);if(!X)throw Error(`Operation ${$} was not found`);W(JSON.stringify(X,null,2));for(let Q of Y.logs($,{after:Number(J.after)||0,limit:500}))W(`${Q.sequence} ${Q.stream.toUpperCase()}${Q.step?` [${Q.step}]`:""} ${Q.message}`)})}catch(Y){K(Y instanceof Error?Y.message:String(Y))}}),G.command("ops:cancel <id>","Cancel queued work or request running cancellation").option("--yes","Skip confirmation").action(async($,J)=>{try{await _0(async(Y)=>{let X=Y.view($);if(!X)throw Error(`Operation ${$} was not found`);if(!J.yes&&!await k(`Cancel ${X.operation.kind} (${$})?`,!1))return;let Q=Y.requestCancellation($);B(`${Q.state==="cancelled"?"Cancelled":"Cancellation requested for"} ${$}`)})}catch(Y){K(Y instanceof Error?Y.message:String(Y))}}),G.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($,J)=>{try{if(!J.class)throw Error("Pass --class with one of the retry classes shown by ops:show");await _0(async(Y)=>{let X=Y.retry($,J.class,{delayMs:Number(J.delay)||0});B(`Retry queued: ${X.id} · attempt ${X.attempt+1}`)})}catch(Y){K(Y instanceof Error?Y.message:String(Y))}}),G.command("ops:concurrency","Show effective durable queue concurrency limits").action(async()=>{try{await _0(async($)=>W(JSON.stringify($.limits,null,2)))}catch($){K($ instanceof Error?$.message:String($))}}),G.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($)=>{try{if($.confirm!=="update queue limits")throw Error('Pass --confirm "update queue limits" to change production concurrency');let J={};for(let Y of["project","environment","provider","builds"])if($[Y]!==void 0)J[Y]=Number($[Y]);await _0(async(Y,X,Q)=>B(`Queue concurrency updated: ${JSON.stringify(Y.configureConcurrency(J,{organizationId:Q}))}`))}catch(J){K(J instanceof Error?J.message:String(J))}}),G.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($)=>{try{await _0(async(J,Y)=>{if(!$.yes&&!await k("Clear completed queue history whose retention has elapsed?",!1))return;let X=J.clearCompleted({projectId:Y,before:$.before});B(`Cleared ${X} retained operation${X===1?"":"s"}.`)})}catch(J){K(J instanceof Error?J.message:String(J))}})}import{readFile as _Q,writeFile as bQ}from"node:fs/promises";function IQ(G){if(G==null)return"—";if(G<60)return`${G}s`;if(G<3600)return`${Math.floor(G/60)}m`;if(G<86400)return`${Math.floor(G/3600)}h`;return`${Math.floor(G/86400)}d`}function kQ(G){return G==null?"—":q1(G)}function hQ(G){return G.map(($)=>[$.id,$.name,$.provider,$.kind,$.status,$.desiredReplicas==null?"—":`${$.runningReplicas??0}/${$.desiredReplicas}`,$.image??$.runtime??"—",kQ($.resources?.memoryBytes??$.resources?.memoryLimitBytes),IQ($.ageSeconds)])}function yQ(G,$){let J=$.search?.toLowerCase();return{...G,workloads:G.workloads.filter((Y)=>(!$.provider||Y.provider===$.provider)&&(!$.status||Y.status===$.status)&&(!J||`${Y.name} ${Y.id} ${Y.links.service??""} ${Object.values(Y.tags).join(" ")}`.toLowerCase().includes(J)))}}async function b0(G){let $=await x(),J=G??Object.keys($.environments??{})[0]??"production";if(!Object.hasOwn($.environments??{},J))throw Error(`Environment ${J} was not found`);let Y=new l5($,J);return{config:$,env:J,service:Y}}async function W6(G,$){if($!==void 0)return $;return p(`Type ${G.name} to confirm`,"")}async function CQ(G,$,J,Y,X,Q){return z6(G,`runtime.${$}`,{workloadId:G.id,provider:G.provider,action:$,replicas:J??null},X,()=>Q.run({workloadId:G.id,action:$,replicas:J,confirm:Y,recentAuth:!0}))}async function z6(G,$,J,Y,X){let Q=await x(),Z=u(process.cwd(),Q),z=new v$(Z.store,{workerId:`cli:${process.pid}`});try{let F=Z.environments.get(Y),U=Z.store.listResources(Z.project.id,F?.id).find((j)=>j.slug===G.links.service),M=Z.store.getActorByExternalId("system","cli")??Z.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI",metadata:{source:"cli"}}),H=z.enqueue({projectId:Z.project.id,environmentId:F?.id,resourceId:U?.id,actorId:M.id,kind:$,input:OY(J),lockKey:U?`resource:${U.id}`:`runtime:${G.id}`,providerKey:G.provider,maxAttempts:1,timeoutSeconds:3600,resumePolicy:"fail",cancellationMode:"provider_non_cancellable",retentionDays:90}).operation;if(!z.claim(H.id))throw Error(`Workload is locked by another operation. Inspect ${H.id} with cloud ops:show.`);try{let j=await X();if(j.stdout)z.appendLog(H.id,j.stdout,{stream:"stdout"});if(j.stderr)z.appendLog(H.id,j.stderr,{stream:"stderr"});if(j.ok)z.complete(H.id,{ok:!0,command:j.command??null});else z.fail(H.id,j.error??j.stderr??"Runtime operation failed",{ok:!1});return{result:j,operationId:H.id}}catch(j){throw z.fail(H.id,j instanceof Error?j.message:String(j),{ok:!1,threw:!0}),j}}finally{Z.store.close()}}function r5(G){G.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($)=>{try{let{service:J}=await b0($.env),Y=yQ(await J.inventory(),$);if($.json)W(JSON.stringify(Y,null,2));else{w(["ID","Name","Provider","Kind","Status","Replicas","Image / runtime","Memory","Age"],hQ(Y.workloads));for(let X of Y.sources.filter((Q)=>Q.status!=="fresh"))N(`${X.id}: ${X.status}${X.message?` — ${X.message}`:""}`)}}catch(J){K(J instanceof Error?J.message:String(J)),process.exitCode=1}}),G.command("runtime:inspect <workload>","Inspect normalized config, replicas, networks, mounts, links, and capabilities").option("--env <environment>","Target environment").action(async($,J)=>{try{let{service:Y}=await b0(J.env),X=await Y.workload($);if(!X)throw Error("Workload was not found in this project and environment");W(JSON.stringify(X,null,2))}catch(Y){K(Y instanceof Error?Y.message:String(Y)),process.exitCode=1}}),G.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($,J)=>{try{let{service:Y}=await b0(J.env),X=await Y.logs($,{limit:Number(J.lines)||200});if(J.json)W(JSON.stringify(X,null,2));else for(let Q of X.lines)console.log(`${Q.timestamp??"—"} ${Q.message}`)}catch(Y){K(Y instanceof Error?Y.message:String(Y)),process.exitCode=1}}),G.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($,J,Y)=>{try{let X=J;if(!["start","stop","restart","redeploy","scale"].includes(X))throw Error("Action must be start, stop, restart, redeploy, or scale");let Q=Y.replicas==null?void 0:Number(Y.replicas);if(X==="scale"&&(!Number.isInteger(Q)||Q<0))throw Error("Scale requires a non-negative integer --replicas value");let{env:Z,service:z}=await b0(Y.env),F=await z.inventory(),U=F.workloads.find((j)=>j.id===$);if(!U)throw Error("Workload was not found in this project and environment");let M=new l5(await x(),Z,{inventory:async()=>F}),H=await CQ(U,X,Q,await W6(U,Y.confirm),Z,M);if(!H.result.ok)throw Error(H.result.error??H.result.stderr??"Runtime operation failed");if(B(`${X} completed for ${U.name} · operation ${H.operationId}`),H.result.stdout)W(H.result.stdout)}catch(X){K(X instanceof Error?X.message:String(X)),process.exitCode=1}}),G.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($,J)=>{try{let{env:Y,service:X}=await b0(J.env),Z=(await X.inventory()).workloads.find((M)=>M.id===$);if(!Z)throw Error("Workload was not found in this project and environment");let z=["process","sockets","filesystem"].includes(String(J.preset))?J.preset:void 0;if(J.preset&&!z)throw Error("Preset must be process, sockets, or filesystem");if(!z&&!J.command)throw Error("Pass --preset or --command");let F=await W6(Z,J.confirm),U=await z6(Z,"runtime.exec",{workloadId:$,provider:Z.provider,preset:z??null,freeForm:!!J.command},Y,()=>X.exec({workloadId:$,preset:z,command:J.command,confirm:F,recentAuth:!0}));if(!U.result.ok)throw Error(U.result.error??U.result.stderr??"Exec failed");if(U.result.stdout)W(U.result.stdout);if(U.result.stderr)N(U.result.stderr);B(`Exec completed for ${Z.name} · operation ${U.operationId}`)}catch(Y){K(Y instanceof Error?Y.message:String(Y)),process.exitCode=1}}),G.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($,J,Y,X)=>{try{let{env:Q,service:Z}=await b0(X.env),F=(await Z.inventory()).workloads.find((H)=>H.id===$);if(!F)throw Error("Workload was not found in this project and environment");let U=await W6(F,X.confirm),M=await z6(F,"runtime.file.read",{workloadId:$,provider:F.provider,path:J},Q,()=>Z.readFile({workloadId:$,path:J,confirm:U,recentAuth:!0}));if(!M.result.ok||!M.result.contentBase64)throw Error(M.result.error??"File download failed");await bQ(Y,Buffer.from(M.result.contentBase64,"base64")),B(`Downloaded ${M.result.size??0} bytes to ${Y} · operation ${M.operationId}${M.result.truncated?" (truncated)":""}`)}catch(Q){K(Q instanceof Error?Q.message:String(Q)),process.exitCode=1}}),G.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($,J,Y,X)=>{try{let{env:Q,service:Z}=await b0(X.env),F=(await Z.inventory()).workloads.find((j)=>j.id===$);if(!F)throw Error("Workload was not found in this project and environment");let U=await W6(F,X.confirm),M=(await _Q(J)).toString("base64"),H=await z6(F,"runtime.file.write",{workloadId:$,provider:F.provider,path:Y,bytes:Buffer.from(M,"base64").byteLength},Q,()=>Z.writeFile({workloadId:$,path:Y,contentBase64:M,confirm:U,recentAuth:!0}));if(!H.result.ok)throw Error(H.result.error??"File upload failed");B(`Uploaded ${H.result.size??0} bytes to ${Y} · operation ${H.operationId}`)}catch(Q){K(Q instanceof Error?Q.message:String(Q)),process.exitCode=1}})}function fQ(G,$=new Date){let J=G.to?new Date(G.to):$;if(!Number.isFinite(J.getTime()))throw Error("--to must be a valid ISO-8601 instant");if(G.from){let Q=new Date(G.from);if(!Number.isFinite(Q.getTime()))throw Error("--from must be a valid ISO-8601 instant");return{from:Q.toISOString(),to:J.toISOString()}}let Y=String(G.range??"1h").match(/^(\d+)(m|h|d)$/);if(!Y)throw Error("--range must use minutes, hours, or days (for example 30m, 6h, or 7d)");let X=Y[2]==="m"?60000:Y[2]==="h"?3600000:86400000;return{from:new Date(J.getTime()-Number(Y[1])*X).toISOString(),to:J.toISOString()}}async function R1(G){let $=await x(),J=G??Object.keys($.environments??{})[0]??"production";if(!Object.hasOwn($.environments??{},J))throw Error(`Environment ${J} was not found`);let Y=u(process.cwd(),$),X=Y.environments.get(J),Q=new NY(Y.store);return{config:$,env:J,controlPlane:Y,environmentRecord:X,telemetry:Q}}function wY(G,$,J){let Y=(Q)=>Q?.split(",").map((Z)=>Z.trim()).filter(Boolean)||void 0,X=Y(G.kind)?.filter((Q)=>["metric","log","trace","request","event"].includes(Q));return{projectId:$,environmentId:J,...fQ(G),kinds:X,names:Y(G.name),sources:Y(G.source),text:G.text,limit:Math.min(5000,Math.max(1,Number(G.limit)||200))}}function gQ(G){let $=["timestamp","kind","source","name","level","value","unit","message","traceId","requestId","deploymentId","releaseId","workloadId"];return[$.join(","),...G.map((J)=>$.map((Y)=>`"${String(J[Y]??"").replaceAll('"','""')}"`).join(","))].join(`
636
- `)}function p5(G){G.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($)=>{let J=await R1($.env);try{let Y=await PY({controlPlane:J.controlPlane.store,projectId:J.controlPlane.project.id,environmentId:J.environmentRecord?.id,config:J.config,environment:J.env,force:$.force});if($.json)W(JSON.stringify(Y,null,2));else B(`Collected ${Y.collected} normalized records${Y.cached?" (cached provider result)":""}.`),w(["Source","Freshness","Lag","Sampling","Retention"],Y.statuses.map((X)=>[X.source,X.freshness,X.lagSeconds==null?"—":`${X.lagSeconds}s`,`${Math.round(X.samplingRate*100)}%`,`${X.retentionDays}d`]))}finally{J.controlPlane.store.close()}}),G.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($)=>{let J=await R1($.env);try{let Y=J.telemetry.query(wY($,J.controlPlane.project.id,J.environmentRecord?.id));if($.json)W(JSON.stringify(Y,null,2));else w(["Time","Kind","Source","Signal","Value / message","Correlation"],Y.records.map((X)=>[X.timestamp,X.kind,X.source,X.name,X.message??(X.value==null?"—":`${X.value} ${X.unit??""}`),X.requestId??X.traceId??X.releaseId??X.deploymentId??"—"]));if(Y.truncated)N("More records are available; narrow the window or use the dashboard cursor explorer.")}finally{J.controlPlane.store.close()}}),G.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($,J)=>{let Y=await R1(J.env);try{let X=wY(J,Y.controlPlane.project.id,Y.environmentRecord?.id),Q=Y.telemetry.query(X);await Bun.write($,$.toLowerCase().endsWith(".csv")?gQ(Q.records):JSON.stringify({exportedAt:new Date().toISOString(),query:X,...Q},null,2)),B(`Exported ${Q.records.length} records to ${$}${Q.truncated?" (bounded result)":""}.`)}finally{Y.controlPlane.store.close()}}),G.command("telemetry:compact","Downsample old metrics and enforce configured retention").option("--env <environment>","Target environment").action(async($)=>{let J=await R1($.env);try{let Y=a5(J.controlPlane.store,J.controlPlane.project.id);W(JSON.stringify(J.telemetry.enforceRetention(Y,J.controlPlane.project.id),null,2))}finally{J.controlPlane.store.close()}}),G.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($)=>{let J=await R1($.env);try{let Y={};if($.rawDays!=null)Y.rawDays=Number($.rawDays);if($.downsampleDays!=null)Y.downsampleAfterDays=Number($.downsampleDays);if($.sampling!=null)Y.samplingRate=Number($.sampling);if($.storageUsdGbMonth!=null)Y.estimatedStorageUsdPerGbMonth=Number($.storageUsdGbMonth);let X=Object.keys(Y).length?DY(J.controlPlane.store,J.controlPlane.project.id,Y):a5(J.controlPlane.store,J.controlPlane.project.id),Z=J.telemetry.status(J.controlPlane.project.id,J.environmentRecord?.id,X.rawDays,X.samplingRate).reduce((F,U)=>F+U.estimatedDailyBytes*30,0),z={policy:X,estimatedMonthlyBytes:Z,estimatedMonthlyCostUsd:xY(Z,X),costEstimateConfigured:X.estimatedStorageUsdPerGbMonth>0};if($.json)W(JSON.stringify(z,null,2));else W(JSON.stringify(X,null,2)),W(z.costEstimateConfigured?`Estimated local storage: $${z.estimatedMonthlyCostUsd}/month`:"Storage cost remains unavailable until --storage-usd-gb-month is set.")}finally{J.controlPlane.store.close()}})}async function R0(G){let $=await x(),J=G??Object.keys($.environments??{})[0]??"production";if(!Object.hasOwn($.environments??{},J))throw Error(`Environment ${J} was not found`);let Y=u(process.cwd(),$),X=Y.environments.get(J),Q=new F1(Y.store,{encryptionKey:Q$(process.cwd())});return{config:$,env:J,controlPlane:Y,environmentRecord:X,alerts:Q}}function SY(G){return new D5(G,{emailImpl:async($)=>{let{email:J}=await import("./chunk-c1vcdgvr.js");return J.send($)}})}function n5(G){G.command("health:list","List configured health checks and latest results").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async($)=>{let J=await R0($.env);try{let Y=J.alerts.listHealthChecks(J.controlPlane.project.id,J.environmentRecord?.id).map((X)=>({...X,latest:J.alerts.listHealthResults(X.id,1)[0]}));if($.json)W(JSON.stringify(Y,null,2));else w(["ID","Name","Kind","Target","Status","Interval","Enabled"],Y.map((X)=>[X.id,X.name,X.kind,X.target,X.latest?.status??"no data",`${X.intervalSeconds}s`,X.enabled?"yes":"no"]))}finally{J.controlPlane.store.close()}}),G.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($,J)=>{let Y=await R0(J.env);try{let X=Y.alerts.getHealthCheck($)??Y.alerts.listHealthChecks(Y.controlPlane.project.id,Y.environmentRecord?.id).find((z)=>z.name===$);if(!X)throw Error("Health check was not found");let Q=new x5(Y.alerts);if(J.test){let z=await Q.probe(X);W(JSON.stringify({...z,productionIncidentCreated:!1},null,2));return}let Z=await Q.runAndEvaluate(X);if(W(JSON.stringify(Z,null,2)),Z.result.status==="healthy")B(`${X.name} is healthy.`);else N(`${X.name} is ${Z.result.status}.`)}finally{Y.controlPlane.store.close()}}),G.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($)=>{let J=await R0($.env);try{let Y=J.alerts.listAlerts(J.controlPlane.project.id,{environmentId:J.environmentRecord?.id,states:$.state?.split(",").filter(Boolean)});if($.json)W(JSON.stringify(Y,null,2));else w(["ID","State","Severity","Title","Owner","First seen","Last seen"],Y.map((X)=>[X.id,X.state,X.severity,X.title,X.ownerActorId??"unassigned",X.firstSeenAt,X.lastSeenAt]))}finally{J.controlPlane.store.close()}}),G.command("alerts:evaluate","Evaluate persisted telemetry rules now").option("--env <environment>","Target environment").action(async($)=>{let J=await R0($.env);try{let Y=N5(J.alerts,J.controlPlane.project.id,J.environmentRecord?.id);W(JSON.stringify(Y,null,2))}finally{J.controlPlane.store.close()}}),G.command("alerts:ack <alert>","Acknowledge an alert as the CLI actor").option("--env <environment>","Target environment").action(async($,J)=>{let Y=await R0(J.env);try{let X=Y.controlPlane.store.getActorByExternalId("system","cli")??Y.controlPlane.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});B(`Acknowledged ${Y.alerts.acknowledge($,X.id).title}.`)}finally{Y.controlPlane.store.close()}}),G.command("notifications:list","List notification channels, routes, and recent delivery diagnostics").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async($)=>{let J=await R0($.env);try{let Y={channels:J.alerts.listChannels(J.controlPlane.organization.id),routes:J.alerts.listRoutes(J.controlPlane.organization.id),deliveries:J.alerts.listDeliveries({limit:100})};if($.json)W(JSON.stringify(Y,null,2));else w(["Channel","Type","Status","Tested","Error"],Y.channels.map((X)=>[X.name,X.kind,X.status,X.lastTestedAt??"never",X.lastError??"—"])),w(["Event","State","Attempts","Status","Error"],Y.deliveries.map((X)=>[X.eventType,X.state,`${X.attempt}/${X.maxAttempts}`,String(X.responseStatus??"—"),X.error??"—"]))}finally{J.controlPlane.store.close()}}),G.command("notifications:test <channel>","Send a channel test without creating a production incident").option("--env <environment>","Target environment").action(async($,J)=>{let Y=await R0(J.env);try{let X=Y.alerts.getChannel($)??Y.alerts.listChannels(Y.controlPlane.organization.id).find((Z)=>Z.name===$);if(!X)throw Error("Notification channel was not found");let Q=await SY(Y.alerts).testChannel(X.id);if(Q.ok)B("Test delivered; no production incident was created.");else throw Error(Q.error)}finally{Y.controlPlane.store.close()}}),G.command("notifications:retry <delivery>","Retry one failed notification delivery").option("--env <environment>","Target environment").action(async($,J)=>{let Y=await R0(J.env);try{let X=Y.alerts.getDelivery($);if(!X)throw Error("Notification delivery was not found");Y.alerts.updateDelivery($,{state:"retrying",attempt:Math.max(0,X.attempt-1),nextAttemptAt:new Date().toISOString()}),W(JSON.stringify(await SY(Y.alerts).deliver($),null,2))}finally{Y.controlPlane.store.close()}})}async function vQ(G,$){let J=await x(),Y=u(process.cwd(),J),X=!G&&$?new s5(Y.store).get($):void 0,Q=X?.environmentId?Y.store.listEnvironments(Y.project.id).find((V)=>V.id===X.environmentId)?.slug:void 0,Z=G??Q??Object.keys(J.environments??{})[0]??"production";if(!Object.hasOwn(J.environments??{},Z))throw Y.store.close(),Error(`Environment ${Z} was not found`);let z=Y.environments.get(Z);if(!z)throw Y.store.close(),Error(`Environment ${Z} was not found in the control plane`);let F=new s5(Y.store),U=new v$(Y.store,{workerId:`cli:${process.pid}`}),M=new fY(F,{queue:U}),H=Y.store.listResources(Y.project.id,z.id);i5(F,J,{organization:Y.organization,project:Y.project,environment:z,resources:H});let j=Y.store.getActorByExternalId("system","cli")??Y.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:J,env:Z,controlPlane:Y,environmentRecord:z,resources:H,store:F,queue:U,service:M,actor:j}}async function k$(G,$,J){let Y=await vQ(G,J);try{return await $(Y)}finally{Y.controlPlane.store.close()}}async function T$(G){try{await G()}catch($){K($ instanceof Error?$.message:String($))}}function r0(G,$){let J=G.store.list(G.controlPlane.project.id,{environmentId:G.environmentRecord.id}),Y=G.store.get($)??J.find((X)=>X.name===$);if(!Y||Y.environmentId!==G.environmentRecord.id)throw Error(`Scheduled job ${$} was not found in ${G.env}`);return Y}function I0(G,$,J,Y){G.controlPlane.store.appendEvent({organizationId:G.controlPlane.organization.id,projectId:G.controlPlane.project.id,resourceId:Y,actorId:G.actor.id,type:`job.${$}`,payload:J})}function _Y(G,$,J){if(G.environmentRecord.kind==="production"&&J!==$)throw Error(`Production ${$} requires --confirm ${$}`)}function mQ(G){if(!G)return{};if(!/^(?:secret|ssm|arn|env):\/\//.test(G))throw Error("Payloads must use a secret://, ssm://, arn://, or env:// reference");return{input:G}}function dQ(G){return G.map(($)=>[$.id,$.enabled?"enabled":"disabled",$.provider,$.name,$.expression,$.timezone,$.nextRunAt??"—",$.reconciliationStatus])}function t5(G){G.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$(async()=>k$($.env,async(J)=>{let Y=J.store.list(J.controlPlane.project.id,{environmentId:J.environmentRecord.id});if($.json)W(JSON.stringify(Y,null,2));else w(["ID","State","Provider","Name","Expression","Timezone","Next run","Reconciliation"],dQ(Y))}))),G.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($,J)=>T$(async()=>{let Y=yY($,J.timezone??"UTC",J.from?new Date(J.from):new Date,Number(J.count)||5);W(JSON.stringify({...Y,productionExecutionCreated:!1},null,2))})),G.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($,J,Y)=>T$(async()=>k$(Y.env,async(X)=>{if(!["skip","catch_up"].includes(Y.missed??"skip"))throw Error("--missed must be skip or catch_up");if(!["allow","forbid","replace"].includes(Y.overlap??"forbid"))throw Error("--overlap must be allow, forbid, or replace");let Q=j1(X.config),z={workers:X.store.listWorkers(X.controlPlane.project.id,X.environmentRecord.id).map((V)=>({name:V.name}))},F;if(Q==="server"){let V=bY(X.config,z).filter((A)=>!A.danger&&["scheduler","backup","worker"].includes(A.group)),T=Y.operation?V.find((A)=>A.id===Y.operation):V.length===1?V[0]:void 0;if(!T)throw Error(`Choose a safe target with --operation. Available: ${V.map((A)=>A.id).join(", ")||"none"}`);F={kind:"dashboard_operation",operationId:T.id}}else F={kind:"serverless_scheduler",action:"run"};let U=Q==="server"?"server":"eventbridge",M=CY({provider:U,expression:J,flexibleMinutes:Number(Y.flexibleMinutes)||0,missedRunPolicy:Y.missed??"skip",overlapPolicy:Y.overlap??"forbid"});if(!M.supported)throw Error(M.notes.join(" "));let H=Y.resource?X.resources.find((V)=>V.id===Y.resource||V.slug===Y.resource):void 0;if(Y.resource&&!H)throw Error(`Resource ${Y.resource} was not found in ${X.env}`);let j=X.store.create({organizationId:X.controlPlane.organization.id,projectId:X.controlPlane.project.id,environmentId:X.environmentRecord.id,resourceId:H?.id,name:$,provider:U,expression:J,timezone:Y.timezone??"UTC",startsAt:Y.startsAt,endsAt:Y.endsAt,flexibleMinutes:Number(Y.flexibleMinutes)||0,target:F,payloadRefs:mQ(Y.payloadRef),missedRunPolicy:Y.missed??"skip",overlapPolicy:Y.overlap??"forbid",retryPolicy:{maxAttempts:Number(Y.attempts)||3,backoffSeconds:Number(Y.backoff)||30,deadLetterRef:Y.deadLetter},timeoutSeconds:Number(Y.timeout)||900,enabled:!Y.disabled,origin:"managed",ownerActorId:X.actor.id,observedState:{},reconciliationStatus:"pending"});I0(X,"created",{jobId:j.id,provider:j.provider,expression:j.normalizedExpression},j.resourceId),B(`Created ${j.name} (${j.id}); next run ${j.nextRunAt??"outside the active window"}`)}))),G.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($,J)=>T$(async()=>k$(J.env,async(Y)=>{let X=r0(Y,$);if(X.origin==="config")throw Error("Config-defined schedules must be edited in cloud.config.ts and reconciled");if(J.missed&&!["skip","catch_up"].includes(J.missed))throw Error("--missed must be skip or catch_up");if(J.overlap&&!["allow","forbid","replace"].includes(J.overlap))throw Error("--overlap must be allow, forbid, or replace");let Q=Y.store.updateSchedule(X.id,{expression:J.expression,timezone:J.timezone,missedRunPolicy:J.missed,overlapPolicy:J.overlap,timeoutSeconds:J.timeout==null?void 0:Number(J.timeout)});I0(Y,"updated",{jobId:Q.id,expression:Q.normalizedExpression,version:Q.version},Q.resourceId),B(`Updated ${Q.name}; next run ${Q.nextRunAt??"outside the active window"}`)}))),G.command("jobs:run <job>","Queue a manual run through the durable operation worker").option("--env <environment>","Target environment").action(async($,J)=>T$(async()=>k$(J.env,async(Y)=>{let X=r0(Y,$),Q=Y.service.enqueue(X,{trigger:"manual",actorId:Y.actor.id});I0(Y,"run_queued",{jobId:X.id,executionId:Q.id},X.resourceId),B(`Queued execution ${Q.id}; inspect it with cloud jobs:history "${X.name}"`)}))),G.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($,J)=>T$(async()=>k$(J.env,async(Y)=>{let X=r0(Y,$);if(!X.enabled)throw Error(`${X.name} is disabled`);let Q=J.scheduledFor?new Date(J.scheduledFor):new Date;if(!J.scheduledFor)Q.setUTCSeconds(0,0);let Z=Q.toISOString(),z=Y.service.enqueue(X,{trigger:J.scheduled?"scheduled":"external",scheduledFor:Z,actorId:Y.actor.id}),F=hY(X.normalizedExpression,X.timezone,Q,1)[0];Y.store.markScheduled(X.id,Z,F),B(`Accepted ${X.name} for ${Z} as ${z.id}`)},$)));for(let $ of["enable","disable"])G.command(`jobs:${$} <job>`,`${$==="enable"?"Enable":"Disable"} a scheduled job`).option("--env <environment>","Target environment").option("--confirm <text>",`Production ${$} confirmation`).action(async(J,Y)=>T$(async()=>k$(Y.env,async(X)=>{let Q=r0(X,J);if($==="disable")_Y(X,$,Y.confirm);let Z=X.store.setEnabled(Q.id,$==="enable");I0(X,$==="enable"?"enabled":"disabled",{jobId:Z.id},Z.resourceId),B(`${Z.name} is ${Z.enabled?"enabled":"disabled"}`)})));G.command("jobs:delete <job>","Delete a managed schedule").option("--env <environment>","Target environment").option("--confirm <text>","Must be delete in production").action(async($,J)=>T$(async()=>k$(J.env,async(Y)=>{let X=r0(Y,$);_Y(Y,"delete",J.confirm),Y.store.remove(X.id),I0(Y,"deleted",{jobId:X.id,name:X.name},X.resourceId),B(`Deleted ${X.name}`)}))),G.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($,J)=>T$(async()=>k$(J.env,async(Y)=>{let X=r0(Y,$),Q=Y.store.listExecutions(X.id,Number(J.limit)||100).map((Z)=>({...Z,logs:J.logs&&Z.operationId?Y.queue.logs(Z.operationId,{limit:500}):[]}));if(J.json)W(JSON.stringify(Q,null,2));else if(w(["ID","Status","Trigger","Scheduled for","Attempt","Started","Finished","Error"],Q.map((Z)=>[Z.id,Z.status,Z.trigger,Z.scheduledFor,String(Z.attempt),Z.startedAt??"—",Z.finishedAt??"—",Z.error??"—"])),J.logs)for(let Z of Q)for(let z of Z.logs)W(`${Z.id} ${z.sequence} ${z.stream.toUpperCase()} ${z.message}`)}))),G.command("jobs:reconcile","Import declarative schedules and workers without destructive deletion").option("--env <environment>","Target environment").action(async($)=>T$(async()=>k$($.env,async(J)=>{let Y=i5(J.store,J.config,{organization:J.controlPlane.organization,project:J.controlPlane.project,environment:J.environmentRecord,resources:J.resources});I0(J,"reconciled",Y),B(`Reconciled ${Y.jobs} schedules and ${Y.workers} workers; retained ${Y.drifted} missing definitions as drifted`)}))),G.command("workers:list","List configured systemd, ECS, and Lambda workers").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async($)=>T$(async()=>k$($.env,async(J)=>{let Y=J.store.listWorkers(J.controlPlane.project.id,J.environmentRecord.id);if($.json)W(JSON.stringify(Y,null,2));else w(["ID","Provider","Name","Queue","Processes","Timeout","Restart","Reconciliation"],Y.map((X)=>[X.id,X.provider,X.name,X.queue,String(X.processes),`${X.timeoutSeconds}s`,X.restartPolicy,X.reconciliationStatus]))}))),G.command("workers:restart <worker>","Gracefully restart an allowlisted systemd worker").option("--env <environment>","Target environment").option("--confirm <name>","Exact worker name confirmation").action(async($,J)=>T$(async()=>k$(J.env,async(Y)=>{let X=Y.store.listWorkers(Y.controlPlane.project.id,Y.environmentRecord.id),Q=Y.store.getWorker($)??X.find((F)=>F.name===$);if(!Q||Q.environmentId!==Y.environmentRecord.id)throw Error(`Worker ${$} was not found in ${Y.env}`);if(J.confirm!==Q.name)throw Error(`Restart requires --confirm "${Q.name}"`);if(Q.provider!=="systemd")throw Error(`${Q.provider} workers are provider/config managed and do not expose a safe restart adapter`);let Z=IY(String(Q.target.operationId??""),Y.config,{workers:X.map((F)=>({name:F.name}))});if(!Z||Z.danger||Z.group!=="worker")throw Error("The allowlisted worker restart operation is unavailable");let z=await kY(Y.config,Y.env,Z);if(Y.store.reconcileWorker(Q.id,z.ok?"in_sync":"unavailable",{...Q.observedState,lastRestartAt:new Date().toISOString(),command:z.command??null,error:z.error??z.stderr??null}),I0(Y,"worker_restarted",{workerId:Q.id,ok:z.ok},Q.resourceId),!z.ok)throw Error(z.error??z.stderr??"Worker restart failed");B(`Restarted ${Q.name}`)})))}function e5(G){G.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($)=>{R("Local Tunnel");let J=Number.parseInt($.port);if(W(`Local server: ${$.host}:${J}`),W(`Tunnel server: ${$.server}`),$.subdomain)W(`Requested subdomain: ${$.subdomain}`);let Y=new E("Connecting to tunnel server...");Y.start();try{let X;try{X=(await import("localtunnels")).TunnelClient}catch{Y.fail("localtunnels package not found"),W(`
637
- To use tunnels, install the localtunnels package:`),W(" bun add localtunnels"),W(`
638
- Or use the standalone CLI:`),W(" bunx localtunnels --port 3000");return}let Q=$.server.replace(/^(wss?|https?):\/\//,""),Z=$.server.startsWith("wss://")||$.server.startsWith("https://")||Q==="localtunnel.dev",z=new X({localPort:J,localHost:$.host,subdomain:$.subdomain,host:Q,port:Z?443:80,secure:Z,verbose:$.verbose});z.on("connected",(U)=>{Y.succeed("Connected!"),B(`
639
- Your tunnel URL: ${U.url}`),W(`Subdomain: ${U.subdomain}`),W(`
640
- Forwarding: ${U.url} -> http://${$.host}:${J}`),W(`
641
- Press Ctrl+C to stop the tunnel`)}),z.on("request",(U)=>{if($.verbose)W(`→ ${U.method} ${U.url}`)}),z.on("response",(U)=>{if($.verbose)W(`← ${U.status} (${U.size} bytes${U.duration?`, ${U.duration}ms`:""})`)}),z.on("reconnecting",(U)=>{W(`Reconnecting... (attempt ${U.attempt}/${U.maxAttempts})`)}),z.on("error",(U)=>{K(`Tunnel error: ${U.message}`)}),z.on("close",()=>{W(`
642
- Tunnel closed`)});let F=()=>{W(`
643
- Shutting down tunnel...`),z.disconnect(),process.exit(0)};process.on("SIGINT",F),process.on("SIGTERM",F),await z.connect(),await new Promise(()=>{})}catch(X){Y.fail("Failed to connect"),K(`Error: ${X.message}`),process.exit(1)}}),G.command("tunnel:status","Check tunnel server status").option("--server <url>","Tunnel server URL",{default:"https://localtunnel.dev"}).action(async($)=>{R("Tunnel Server Status");let J=new E("Checking server status...");J.start();try{let Y=$.server.startsWith("http")?$.server:`https://${$.server}`,X=await fetch(`${Y}/status`,{method:"GET",headers:{Accept:"application/json"}});if(X.ok){let Q=await X.json();if(J.succeed("Server is online"),W(`
644
- Server: ${$.server}`),W(`Status: ${Q.status||"operational"}`),Q.version)W(`Version: ${Q.version}`);if(Q.connections!==void 0)W(`Active connections: ${Q.connections}`);if(Q.uptime)W(`Uptime: ${Q.uptime}`);if(Q.activeSubdomains?.length)W(`Active subdomains: ${Q.activeSubdomains.join(", ")}`)}else J.fail(`Server returned status ${X.status}`)}catch(Y){J.fail("Failed to check server status"),K(`Error: ${Y.message}`),W(`
645
- The tunnel server may be offline or unreachable.`)}}),G.command("tunnel:info","Show tunnel configuration and setup info").action(async()=>{R("Local Tunnel Information"),W("ts-cloud uses localtunnels for secure tunnel connections."),W(""),W("Default server: localtunnel.dev"),W(""),W("Usage:"),W(" cloud tunnel --port 3000 # Expose port 3000"),W(" cloud tunnel --port 8080 --subdomain myapp"),W(""),W("Features:"),W(" - Secure WebSocket-based tunnels"),W(" - Custom subdomains (when available)"),W(" - Automatic reconnection"),W(" - Request logging"),W(" - Binary data support"),W(""),W("Self-hosted tunnel server:"),W(" You can run your own tunnel server using localtunnels."),W(" See: https://github.com/stacksjs/localtunnels"),W(""),W("Environment variables:"),W(" TUNNEL_SERVER - Custom tunnel server URL"),W(" TUNNEL_SUBDOMAIN - Default subdomain to request")}),G.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($)=>{if(R("Deploy Tunnel Infrastructure"),W(`Region: ${$.region}`),W(`Prefix: ${$.prefix}`),W(""),W("This will deploy:"),W(" - DynamoDB tables for connection tracking"),W(" - Lambda functions for handling requests"),W(" - Lambda Function URLs for public access"),W(""),!await k("Deploy tunnel infrastructure?",!1)){W("Operation cancelled");return}let Y=new E("Deploying infrastructure...");Y.start();try{let X;try{X=(await import("localtunnels/cloud")).deployTunnelInfrastructure}catch{Y.fail("localtunnels package not found"),W(`
646
- To deploy tunnel infrastructure, install localtunnels:`),W(" bun add localtunnels"),W(`
647
- Or deploy using the localtunnels CLI directly:`),W(" bunx localtunnels deploy --region us-east-1");return}let Q=await X({region:$.region,prefix:$.prefix,verbose:$.verbose});if(Y.succeed("Deployment complete!"),W(""),W("Resources created:"),W(" DynamoDB Tables:"),W(` - ${Q.connectionsTable}`),W(` - ${Q.responsesTable}`),W(""),W(" Lambda Functions:"),W(` - ${Q.functions.http}`),W(` - ${Q.functions.message}`),W(""),Q.httpUrl||Q.wsUrl){if(W("Endpoints:"),Q.httpUrl)W(` HTTP URL: ${Q.httpUrl}`);if(Q.wsUrl)W(` WebSocket URL: ${Q.wsUrl}`)}}catch(X){if(Y.fail("Deployment failed"),K(`Error: ${X.message}`),$.verbose)console.error(X.stack);process.exit(1)}}),G.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($)=>{if(R("Destroy Tunnel Infrastructure"),W(`Region: ${$.region}`),W(`Prefix: ${$.prefix}`),W(""),N("This will permanently delete:"),N(" - All DynamoDB tables and data"),N(" - All Lambda functions"),N(" - All IAM roles and policies"),W(""),!await k("Are you sure you want to destroy this infrastructure?",!1)){W("Operation cancelled");return}let Y=new E("Destroying infrastructure...");Y.start();try{let X;try{X=(await import("localtunnels/cloud")).destroyTunnelInfrastructure}catch{Y.fail("localtunnels package not found"),W(`
648
- To destroy tunnel infrastructure, install localtunnels:`),W(" bun add localtunnels"),W(`
649
- Or destroy using the localtunnels CLI directly:`),W(" bunx localtunnels destroy --region us-east-1");return}await X({region:$.region,prefix:$.prefix,verbose:$.verbose}),Y.succeed("Infrastructure destroyed!")}catch(X){if(Y.fail("Destruction failed"),K(`Error: ${X.message}`),$.verbose)console.error(X.stack);process.exit(1)}}),G.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($)=>{R("Tunnel Server Logs"),W("Tunnel server log groups:"),W(` - /aws/lambda/${$.prefix}-connect`),W(` - /aws/lambda/${$.prefix}-disconnect`),W(` - /aws/lambda/${$.prefix}-message`),W(` - /aws/lambda/${$.prefix}-http`),W(""),W("To view logs:"),W(` aws logs tail /aws/lambda/${$.prefix}-http --region ${$.region}${$.tail?" --follow":""}`),W(""),W("Or use CloudWatch Insights:"),W(" 1. Go to CloudWatch > Logs Insights"),W(" 2. Select the tunnel log groups"),W(" 3. Run queries like:"),W(" fields @timestamp, @message | filter @message like /error/i")}),G.command("tunnel:test <url>","Test a tunnel connection").action(async($)=>{R("Test Tunnel Connection");let J=new E(`Testing ${$}...`);J.start();try{let Y=Date.now(),X=await fetch($,{method:"GET",headers:{"User-Agent":"ts-cloud-tunnel-test"}}),Q=Date.now()-Y;J.succeed(`Connected in ${Q}ms`),W(`
650
- Status: ${X.status} ${X.statusText}`),W(`
651
- Response headers:`),X.headers.forEach((z,F)=>{W(` ${F}: ${z}`)});let Z=await X.text();if(Z.length>0){if(W(`
652
- Response body (first 500 chars):`),console.log(Z.substring(0,500)),Z.length>500)W(`... (${Z.length-500} more characters)`)}}catch(Y){J.fail("Connection failed"),K(`Error: ${Y.message}`)}}),G.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($)=>{R("Local Tunnel Server");let J=Number.parseInt($.port);W(`Listening on: ${$.host}:${J}`),W(`Domain: ${$.domain}`),W("");let Y=new E("Starting tunnel server...");Y.start();try{let X;try{X=(await import("localtunnels")).TunnelServer}catch{Y.fail("localtunnels package not found"),W(`
653
- To run a tunnel server, install localtunnels:`),W(" bun add localtunnels"),W(`
654
- Or use the standalone CLI:`),W(" bunx localtunnels server --port 3000");return}let Q=new X({port:J,host:$.host,verbose:$.verbose});Q.on("connection",(z)=>{W(`+ Client connected: ${z.subdomain} (total: ${z.totalConnections})`)}),Q.on("disconnection",(z)=>{W(`- Client disconnected: ${z.subdomain}`)});let Z=()=>{W(`
655
- Shutting down server...`),Q.stop(),process.exit(0)};process.on("SIGINT",Z),process.on("SIGTERM",Z),await Q.start(),Y.succeed("Server running!"),W(""),W(`WebSocket URL: ws://${$.host==="0.0.0.0"?"localhost":$.host}:${J}`),W(`HTTP URL: http://${$.host==="0.0.0.0"?"localhost":$.host}:${J}`),W(""),W("Clients can connect with:"),W(` cloud tunnel --port 3000 --server ${$.host==="0.0.0.0"?"localhost":$.host}:${J}`),W(""),W("Press Ctrl+C to stop the server"),await new Promise(()=>{})}catch(X){Y.fail("Failed to start server"),K(`Error: ${X.message}`),process.exit(1)}})}var m=new A6("cloud");m.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");JJ(m);T6(m);x6(m);N6(m);k6(m);K5(m);V5(m);A5(m);q5(m);d6(m);c6(m);u6(m);l6(m);a6(m);p6(m);O5(m);w5(m);S5(m);_5(m);b5(m);I5(m);s6(m);XJ(m);$J(m);v5(m);k5(m);h5(m);y5(m);C5(m);YJ(m);GJ(m);f5(m);g5(m);m5(m);d5(m);c5(m);u5(m);r5(m);p5(m);n5(m);t5(m);MJ(m);EJ(m);AJ(m);wJ(m);xJ(m);PJ(m);_J(m);IJ(m);kJ(m);hJ(m);L5(m);e5(m);E5(m);R5(m,R6);m.version(R6);m.help();m.parse();
469
+ `;function Bl(r){let e=r;return String(e?.code||e?.name||e?.statusCode||"")}function Fp(r){return["NoSuchEntity","ResourceNotFoundException","404"].includes(Bl(r))}function Fl(r){return["EntityAlreadyExists","ResourceAlreadyExistsException","ResourceConflictException","409"].includes(Bl(r))}function Ml(r,e){let t=r?.Items;if(!t)return[];if(Array.isArray(t))return t;let n=t[e];if(n===void 0)return[];return Array.isArray(n)?n:[n]}function Fo(r){let e=r?.Aliases?.Items;if(Array.isArray(e))return e.map(String);if(typeof e==="string")return[e];return[]}function Mp(r){if(!/^[A-Z0-9]{8,32}$/.test(r.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(r.expectedAlias))throw Error("Expected CloudFront alias is invalid");if(!/^[A-Za-z0-9-_]{1,64}$/.test(r.functionName))throw Error("Lambda function name is invalid");let e=r.pathPattern?.startsWith("/")?r.pathPattern:`/${r.pathPattern||"api/*"}`;if(e==="/"||e==="/*"||!e.endsWith("*"))throw Error("API path must be a non-default wildcard pattern such as /api/*");let t=r.originId||`${r.functionName}-url`,n=r.roleName||`${r.functionName}-execution`;if(!/^[A-Za-z0-9_.-]{1,64}$/.test(n))throw Error("IAM role name is invalid");let i=r.memorySize??256,o=r.timeout??10,s=r.logRetentionDays??14;if(i<128||i>10240)throw Error("Lambda memory must be between 128 and 10240 MB");if(o<1||o>900)throw Error("Lambda timeout must be between 1 and 900 seconds");return{region:r.region||"us-east-1",pathPattern:e,originId:t,roleName:n,memorySize:i,timeout:o,logRetentionDays:s}}function Bp(r){let e=r.region||"us-east-1";return{cloudfront:new ne(r.profile),iam:new he(e,r.profile),lambda:new tt(e,r.profile),logs:new nt(e,r.profile),sts:new Dt(e,r.profile),sleep:(t)=>new Promise((n)=>setTimeout(n,t))}}async function _l(r,e){let t=Mp(r),n=e||Bp({profile:r.profile,region:t.region}),[i,o,s]=await Promise.all([n.sts.getCallerIdentity(),n.cloudfront.getDistribution(r.distributionId),n.cloudfront.getDistributionConfig(r.distributionId)]);if(!i.Account)throw Error("AWS identity did not return an account ID");if(!o.Enabled)throw Error(`CloudFront distribution ${r.distributionId} is disabled`);if(!(Fo(o).length?Fo(o):Fo(s.DistributionConfig)).includes(r.expectedAlias))throw Error(`Distribution ${r.distributionId} does not contain expected alias ${r.expectedAlias}`);let l,d=!0;try{l=await n.iam.getRole({RoleName:t.roleName})}catch(G){if(!Fp(G))throw G;d=!1}let u=l?.Arn||`arn:aws:iam::${i.Account}:role/${t.roleName}`,m=await n.lambda.functionExists(r.functionName),f=m?await n.lambda.getFunctionUrl(r.functionName):null,h=await n.cloudfront.listOriginAccessControls(),S=`${r.functionName}-lambda-url`,y=h.some((G)=>G.Name===S&&G.OriginAccessControlOriginType==="lambda"),v=Ml(s.DistributionConfig.Origins,"Origin"),R=Ml(s.DistributionConfig.CacheBehaviors,"CacheBehavior"),b=v.some((G)=>String(G.Id)===t.originId),A=R.some((G)=>String(G.PathPattern)===t.pathPattern),k=[...!d?[`create IAM execution role ${t.roleName}`]:[`reconcile IAM execution policy ${t.roleName}`],...m?[`update Lambda ${r.functionName} code and configuration`]:[`create Lambda ${r.functionName}`],...!f?["create private AWS_IAM function URL"]:[],...!y?[`create Lambda origin access control ${S}`]:[],"grant CloudFront function-URL and invocation permissions",`${A?"reconcile":"add"} ${t.pathPattern} behavior without changing the default behavior`],T={command:`cloud cdn:origin:remove ${r.distributionId} FUNCTION_URL_HOST --id ${t.originId} --path '${t.pathPattern}' --apply --confirm 'remove:${r.distributionId}:${t.pathPattern}'`,removeFunction:!m,removeRole:!d},D={mode:r.apply?"apply":"plan",accountId:i.Account,identityArn:i.Arn,distribution:{id:o.Id,arn:o.ARN,alias:r.expectedAlias,domainName:o.DomainName,status:o.Status},function:{name:r.functionName,existed:m,runtime:"nodejs22.x",urlExisted:!!f,url:f?.FunctionUrl},role:{name:t.roleName,existed:d,arn:u},origin:{id:t.originId,pathPattern:t.pathPattern,existed:b,behaviorExisted:A,accessControlExisted:y},changes:k,rollback:T,applied:!1};if(!r.apply)return D;let E=`${r.distributionId}:${t.pathPattern}`;if(r.confirm!==E)throw Error(`Pass exact confirmation token ${E} to mutate the live distribution`);if(!l)l=await n.iam.createRole({RoleName:t.roleName,Description:`Execution role for ${r.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 n.iam.putRolePolicy({RoleName:t.roleName,PolicyName:"lambda-logs",PolicyDocument:JSON.stringify({Version:"2012-10-17",Statement:[{Effect:"Allow",Action:["logs:CreateLogStream","logs:PutLogEvents"],Resource:`arn:aws:logs:${t.region}:${i.Account}:log-group:/aws/lambda/${r.functionName}:*`}]})});try{await n.logs.createLogGroup(`/aws/lambda/${r.functionName}`,{ManagedBy:"ts-cloud"})}catch(G){if(!Fl(G))throw G}await n.logs.putRetentionPolicy(`/aws/lambda/${r.functionName}`,t.logRetentionDays);let N=r.code||jp;if(m)await n.lambda.updateFunctionCodeInline(r.functionName,N,"index.mjs"),await n.lambda.waitForFunctionActive(r.functionName),await n.lambda.updateFunctionConfiguration({FunctionName:r.functionName,Runtime:"nodejs22.x",Role:l.Arn,Handler:"index.handler",Description:`Private API origin for ${r.expectedAlias}`,Timeout:t.timeout,MemorySize:t.memorySize});else{let G;for(let L=0;L<8;L++)try{await n.lambda.createFunctionWithCode({FunctionName:r.functionName,Runtime:"nodejs22.x",Role:l.Arn,Handler:"index.handler",Code:N,Filename:"index.mjs",Description:`Private API origin for ${r.expectedAlias}`,Timeout:t.timeout,MemorySize:t.memorySize}),G=void 0;break}catch(H){if(G=H,!String(H.message).toLowerCase().includes("role"))throw H;await n.sleep(1000)}if(G)throw G}await n.lambda.waitForFunctionActive(r.functionName);let _=f||await n.lambda.createFunctionUrl({FunctionName:r.functionName,AuthType:"AWS_IAM",InvokeMode:"BUFFERED"});if(!_.FunctionUrl)throw Error("Lambda did not return a function URL");let B=new URL(_.FunctionUrl).hostname,V=await n.cloudfront.findOrCreateOriginAccessControl(S,"lambda"),K=o.ARN||`arn:aws:cloudfront::${i.Account}:distribution/${r.distributionId}`,oe=[{FunctionName:r.functionName,StatementId:"AllowCloudFrontFunctionUrl",Action:"lambda:InvokeFunctionUrl",Principal:"cloudfront.amazonaws.com",SourceArn:K,FunctionUrlAuthType:"AWS_IAM"},{FunctionName:r.functionName,StatementId:"AllowCloudFrontInvoke",Action:"lambda:InvokeFunction",Principal:"cloudfront.amazonaws.com",SourceArn:K,InvokedViaFunctionUrl:!0}];for(let G of oe)try{await n.lambda.addPermission(G)}catch(L){if(!Fl(L))throw L}return await n.cloudfront.upsertExistingDistributionOrigin(r.distributionId,{id:t.originId,domainName:B,pathPattern:t.pathPattern,originAccessControlId:V.Id}),{...D,function:{...D.function,url:_.FunctionUrl},applied:!0}}async function zl(r){let e=`https://${r.alias}/`,t=`https://${r.alias}/api/health`,n=await fetch(e,{redirect:"follow",headers:{"cache-control":"no-cache"}}),i=new Uint8Array(await n.arrayBuffer()),o=Lp("sha256").update(i).digest("hex"),s=Date.now(),c=await fetch(t,{redirect:"follow",headers:{"cache-control":"no-cache"}}),l=Date.now()-s,d=await c.text(),u=d;try{u=JSON.parse(d)}catch{}let m;if(r.logs&&r.functionName){let f=await r.logs.filterLogEvents({logGroupName:`/aws/lambda/${r.functionName}`,startTime:s-60000,filterPattern:'"Init Duration"',limit:25});for(let h of f.events||[]){let S=h.message?.match(/Init Duration:\s*([0-9.]+)\s*ms/i);if(S)m=Number(S[1])}}return{frontend:{url:e,status:n.status,sha256:o,unchanged:r.expectedFrontendSha256?o===r.expectedFrontendSha256:void 0,bytes:i.byteLength},api:{url:t,status:c.status,latencyMs:l,healthy:c.ok&&!!u?.ok,body:u},coldStart:{observed:m!==void 0,initDurationMs:m},verifiedAt:(r.now?.()||new Date).toISOString()}}function Ul(r){let e=(r.memoryMb||256)/1024,t=Math.max(0,r.requests-(r.includeFreeTier===!1?0:1e6)),n=Math.max(0,r.requests*(r.averageDurationMs/1000)*e-(r.includeFreeTier===!1?0:400000)),i=t/1e6*0.2+n*0.0000166667,o=9.010025,s=16.425;return{lambdaUsd:Number(i.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 Mo(r){r.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(e,t,n)=>{try{let i=await _l({distributionId:e,expectedAlias:t,functionName:n.function||"ts-cloud-static-api",pathPattern:n.path,originId:n.originId,roleName:n.role,region:n.region,profile:n.profile,memorySize:Number(n.memory),timeout:Number(n.timeout),logRetentionDays:Number(n.retention),apply:!!n.apply,confirm:n.confirm});if(a(JSON.stringify(i,null,2)),i.applied)g("Private API origin submitted to CloudFront. Run cdn:api:verify after propagation.");else a(`Plan only. Re-run with --apply --confirm '${e}:${i.origin.pathPattern}'.`)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}}),r.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(e,t)=>{try{let n=new nt(t.region||"us-east-1",t.profile),i=await zl({alias:e,expectedFrontendSha256:t.frontendSha256,logs:n,functionName:t.function||"ts-cloud-static-api"});if(a(JSON.stringify(i,null,2)),!i.api.healthy||i.frontend.unchanged===!1)throw Error("Static API verification failed");g("Static frontend and Lambda API verification passed.")}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}}),r.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((e,t)=>{let n=Ul({requests:Number(e),averageDurationMs:Number(t.duration),memoryMb:Number(t.memory),includeFreeTier:t.freeTier!==!1});a(JSON.stringify(n,null,2))}),r.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(e,t,n)=>{try{let i=new ne(n.profile),o={id:n.id??"api-backend",domainName:t,pathPattern:n.path??"/api/*",replaceExisting:!!n.replace},s=await i.upsertExistingDistributionOrigin(e,{...o,dryRun:!0});if(a(JSON.stringify(s,null,2)),!n.apply){a("Dry run only. Re-run with --apply and the exact --confirm token.");return}let c=`${e}:${s.pathPattern}`;if(n.confirm!==c)throw Error(`Pass --confirm "${c}" to apply this live distribution change`);let l=await i.upsertExistingDistributionOrigin(e,o);g(l.changed?"Backend origin patch submitted.":"Backend origin already matches."),a(JSON.stringify(l,null,2))}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}}),r.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(e,t,n)=>{try{let i=new ne(n.profile),o={id:n.id??"api-backend",domainName:t,pathPattern:n.path??"/api/*"},s=await i.removeExistingDistributionOrigin(e,{...o,dryRun:!0});if(a(JSON.stringify(s,null,2)),!n.apply){a("Dry run only. Re-run with --apply and the exact --confirm token.");return}let c=`remove:${e}:${s.pathPattern}`;if(n.confirm!==c)throw Error(`Pass --confirm "${c}" to apply this live distribution rollback`);let l=await i.removeExistingDistributionOrigin(e,o);g(l.changed?"Backend behavior rollback submitted.":"Backend behavior was already absent."),a(JSON.stringify(l,null,2))}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}}),r.command("cdn:list","List all CloudFront distributions").action(async()=>{w("CloudFront Distributions");try{let e=await P(),t=new ne,n=new C("Fetching distributions...");n.start();let i=await t.listDistributions();if(n.succeed(`Found ${i.length} distribution(s)`),i.length===0){a("No CloudFront distributions found"),a("Use `cloud cdn:create` to create a new distribution");return}x(["ID","Domain","Status","Enabled","Aliases"],i.map((o)=>[o.Id||"N/A",o.DomainName||"N/A",o.Status||"N/A",o.Enabled?"Yes":"No",(o.Aliases?.Items?.length||0).toString()]))}catch(e){p(`Failed to list distributions: ${e.message}`),process.exit(1)}}),r.command("cdn:status <distributionId>","Get CloudFront distribution status").action(async(e)=>{w(`CloudFront Distribution: ${e}`);try{let t=new ne,n=new C("Fetching distribution details...");n.start();let i=await t.getDistribution(e);if(n.succeed("Distribution details loaded"),a(`
470
+ Distribution Information:`),a(` ID: ${i.Id}`),a(` Domain: ${i.DomainName}`),a(` Status: ${i.Status}`),a(` ARN: ${i.ARN}`),a(` Enabled: ${i.Enabled?"Yes":"No"}`),i.Aliases?.Items?.length)a(` Aliases: ${i.Aliases.Items.join(", ")}`);let o=await t.getDistributionConfig(e);a(`
471
+ Origins:`);let s=o.DistributionConfig?.Origins?.Items||[];if(Array.isArray(s))for(let c of s)a(` - ${c.Id}: ${c.DomainName}`);if(o.DistributionConfig?.DefaultCacheBehavior){let c=o.DistributionConfig.DefaultCacheBehavior;a(`
472
+ Default Cache Behavior:`),a(` Target Origin: ${c.TargetOriginId}`),a(` Viewer Protocol: ${c.ViewerProtocolPolicy}`)}}catch(t){p(`Failed to get distribution: ${t.message}`),process.exit(1)}}),r.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(e)=>{w("Create CloudFront Distribution");try{if(!e.bucket)p("--bucket is required"),a("Example: cloud cdn:create --bucket my-bucket --region us-east-1"),process.exit(1);let t=new ne;if(a(`Bucket: ${e.bucket}`),a(`Region: ${e.region}`),e.alias)a(`Alias: ${e.alias}`);if(!await O(`
473
+ Create this distribution?`,!0)){a("Operation cancelled");return}let i=new C("Setting up Origin Access Control...");i.start();let o=await t.findOrCreateOriginAccessControl(`OAC-${e.bucket}`);if(o.isNew)i.text="Created new Origin Access Control";i.text="Creating distribution...";let s=e.alias?[e.alias]:[],c=await t.createDistributionForS3({bucketName:e.bucket,bucketRegion:e.region,originAccessControlId:o.Id,aliases:s,certificateArn:e.certificate,comment:e.comment||"Created by ts-cloud CLI"});i.succeed("Distribution created"),g(`
474
+ Distribution ID: ${c.Id}`),a(`Domain: ${c.DomainName}`),a(`Status: ${c.Status}`),a(`
475
+ Note: Distribution deployment may take 15-30 minutes.`),a(`
476
+ IMPORTANT: Update your S3 bucket policy to allow CloudFront access:`);let l=ne.getS3BucketPolicyForCloudFront(e.bucket,c.ARN);a(JSON.stringify(l,null,2))}catch(t){p(`Failed to create distribution: ${t.message}`),process.exit(1)}}),r.command("cdn:invalidate <distributionId>","Invalidate CloudFront cache").option("--paths <paths>","Paths to invalidate (comma-separated)",{default:"/*"}).action(async(e,t)=>{w("Invalidate CloudFront Cache");try{let n=new ne,i=t.paths.split(",").map((l)=>l.trim());if(a(`Distribution: ${e}`),a(`Paths: ${i.join(", ")}`),!await O(`
477
+ Create invalidation?`,!0)){a("Operation cancelled");return}let s=new C("Creating invalidation...");s.start();let c=await n.createInvalidation({distributionId:e,paths:i,callerReference:`cli-${Date.now()}`});s.succeed("Invalidation created"),g(`
478
+ Invalidation ID: ${c.Id}`),a(`Status: ${c.Status}`),a(`
479
+ Note: Invalidation typically completes in 5-10 minutes.`)}catch(n){p(`Failed to create invalidation: ${n.message}`),process.exit(1)}}),r.command("cdn:disable <distributionId>","Disable a CloudFront distribution").action(async(e)=>{w("Disable CloudFront Distribution");try{let t=new ne;if(I(`This will disable distribution: ${e}`),a("The distribution will stop serving content."),!await O(`
480
+ Disable this distribution?`,!1)){a("Operation cancelled");return}let i=new C("Disabling distribution...");i.start(),await t.disableDistribution(e),i.succeed("Distribution disabled"),a(`
481
+ Note: Changes may take 15-30 minutes to propagate.`),a("To delete the distribution, wait for it to be fully disabled first.")}catch(t){p(`Failed to disable distribution: ${t.message}`),process.exit(1)}}),r.command("cdn:delete <distributionId>","Delete a CloudFront distribution").action(async(e)=>{w("Delete CloudFront Distribution");try{let t=new ne;if(I(`This will permanently delete distribution: ${e}`),I("The distribution must be disabled first."),!await O(`
482
+ Delete this distribution?`,!1)){a("Operation cancelled");return}let i=new C("Checking distribution status...");i.start();let o=await t.getDistribution(e);if(o.Status!=="Deployed"||o.Enabled){i.fail("Distribution must be disabled and deployed before deletion"),a('\nRun `cloud cdn:disable` first and wait for status to be "Deployed"');return}i.text="Deleting distribution...",await t.deleteDistribution(e),i.succeed("Distribution deleted")}catch(t){p(`Failed to delete distribution: ${t.message}`),process.exit(1)}})}import{readdirSync as _p,statSync as zp}from"node:fs";import{join as Kl,relative as Up}from"node:path";async function Ne(r){let e=await P(),t=q(process.cwd(),e),n=r??Object.keys(e.environments??{})[0]??"production",i=t.environments.get(n);if(!i)throw t.store.close(),Error(`Environment ${n} was not found.`);let o=t.store.getActorByExternalId("system","cli")??t.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"}),s=new Vl(t.store),c=new Wl(s,[new ql,new Gl(Kl(process.cwd(),".ts-cloud","volumes"))]);return{controlPlane:t,environmentRecord:i,actor:o,store:s,service:c}}function Vp(r){return r.map((e)=>[e.name,`${e.provider}/${e.type}`,e.status,e.capacityBytes==null?"—":In(e.capacityBytes),e.usedBytes==null?"—":In(e.usedBytes),String(e.attachments.filter((t)=>t.observedState==="attached").length),e.backupState,e.orphaned?"orphan":"managed"])}function Bo(r){r.command("storage:list","List all S3 buckets").option("--region <region>","AWS region").action(async(t)=>{w("S3 Buckets");try{let n=await P(),i=t.region||n.project.region||"us-east-1",o=new de(i),s=new C("Fetching buckets...");s.start();let l=(await o.listBuckets()).Buckets||[];if(s.succeed(`Found ${l.length} bucket(s)`),l.length===0){a("No S3 buckets found"),a("Use `cloud storage:create` to create a new bucket");return}x(["Name","Created"],l.map((d)=>[d.Name||"N/A",d.CreationDate?new Date(d.CreationDate).toLocaleDateString():"N/A"]))}catch(n){p(`Failed to list buckets: ${n.message}`),process.exit(1)}}),r.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(t,n)=>{w("Create S3 Bucket");try{let i=new de(n.region);if(a(`Bucket name: ${t}`),a(`Region: ${n.region}`),a(`Public access: ${n.public?"Yes":"No"}`),a(`Versioning: ${n.versioning?"Yes":"No"}`),!await O(`
483
+ Create this bucket?`,!0)){a("Operation cancelled");return}let s=new C("Creating bucket...");if(s.start(),await i.createBucket(t),!n.public)s.text="Configuring public access block...",await i.putPublicAccessBlock(t,{BlockPublicAcls:!0,IgnorePublicAcls:!0,BlockPublicPolicy:!0,RestrictPublicBuckets:!0});if(n.versioning)s.text="Enabling versioning...",await i.putBucketVersioning(t,"Enabled");s.succeed("Bucket created"),g(`
484
+ Bucket: ${t}`),a(`Region: ${n.region}`),a(`URL: s3://${t}`)}catch(i){p(`Failed to create bucket: ${i.message}`),process.exit(1)}}),r.command("storage:delete <name>","Delete an S3 bucket").option("--force","Delete all objects first").action(async(t,n)=>{w("Delete S3 Bucket");try{let i=new de("us-east-1");if(I(`This will permanently delete bucket: ${t}`),n.force)I("All objects in the bucket will be deleted!");if(!await O(`
485
+ Delete this bucket?`,!1)){a("Operation cancelled");return}let s=new C("Deleting bucket...");if(s.start(),n.force)s.text="Emptying bucket...",await i.emptyBucket(t);await i.deleteBucket(t),s.succeed("Bucket deleted")}catch(i){p(`Failed to delete bucket: ${i.message}`),process.exit(1)}}),r.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(t,n,i)=>{w("Sync to S3");try{let l=function(f,h){let S=_p(f,{withFileTypes:!0});for(let y of S){let v=Kl(f,y.name);if(y.isDirectory())l(v,h);else{let R=Up(h,v),b=i.prefix?`${i.prefix}/${R}`:R,A=zp(v);c.push({path:v,key:b,size:A.size})}}},o=new de("us-east-1");if(a(`Source: ${t}`),a(`Destination: s3://${n}/${i.prefix||""}`),i.dryRun)a("Dry run mode - no changes will be made");let s=new C("Scanning files...");s.start();let c=[];if(l(t,t),s.succeed(`Found ${c.length} local file(s)`),c.length===0){a("No files to sync");return}a(`
486
+ Files to sync:`);for(let f of c.slice(0,10))a(` ${f.key} (${(f.size/1024).toFixed(2)} KB)`);if(c.length>10)a(` ... and ${c.length-10} more`);if(i.dryRun){a(`
487
+ Dry run complete - no changes made`);return}if(!await O(`
488
+ Sync these files?`,!0)){a("Operation cancelled");return}let u=new C("Uploading files...");u.start();let m=0;for(let f of c){u.text=`Uploading ${f.key}... (${m+1}/${c.length})`;let S=await Bun.file(f.path).arrayBuffer();await o.putObject({bucket:n,key:f.key,body:Buffer.from(S),contentType:Wp(f.key)}),m++}if(u.succeed(`Uploaded ${m} file(s)`),i.delete){let f=new C("Checking for files to delete...");f.start();let h=await o.listObjects({bucket:n,prefix:i.prefix}),S=new Set(c.map((v)=>v.key)),y=h.objects.filter((v)=>v.Key&&!S.has(v.Key)).map((v)=>v.Key);if(y.length>0){f.text=`Deleting ${y.length} remote file(s)...`;for(let v of y)await o.deleteObject(n,v);f.succeed(`Deleted ${y.length} remote file(s)`)}else f.succeed("No files to delete")}g(`
489
+ Sync complete!`)}catch(o){p(`Failed to sync: ${o.message}`),process.exit(1)}}),r.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(t,n)=>{w("S3 Bucket Policy");try{let i=new de("us-east-1");if(n.delete){if(I(`This will delete the policy for bucket: ${t}`),!await O(`
490
+ Delete bucket policy?`,!1)){a("Operation cancelled");return}let c=new C("Deleting policy...");c.start(),await i.deleteBucketPolicy(t),c.succeed("Policy deleted");return}if(n.publicRead){if(I(`This will make bucket ${t} publicly readable!`),!await O(`
491
+ Set public read policy?`,!1)){a("Operation cancelled");return}let c=new C("Setting policy...");c.start();let l={Version:"2012-10-17",Statement:[{Sid:"PublicReadGetObject",Effect:"Allow",Principal:"*",Action:"s3:GetObject",Resource:`arn:aws:s3:::${t}/*`}]};await i.putBucketPolicy(t,l),c.succeed("Public read policy set");return}if(n.set){let s=new C("Setting policy...");s.start();let l=await Bun.file(n.set).text();await i.putBucketPolicy(t,l),s.succeed("Policy set");return}let o=new C("Fetching policy...");o.start();try{let s=await i.getBucketPolicy(t);o.succeed("Policy loaded"),a(`
492
+ Bucket Policy:`),console.log(JSON.stringify(s||{},null,2))}catch(s){if(s.message?.includes("NoSuchBucketPolicy"))o.succeed("No policy set"),a("This bucket has no policy configured.");else throw s}}catch(i){p(`Failed to manage policy: ${i.message}`),process.exit(1)}}),r.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(t,n)=>{w(`Objects in ${t}`);try{let i=new de("us-east-1"),o=new C("Listing objects...");o.start();let s=await i.listObjects({bucket:t,prefix:n.prefix,maxKeys:Number.parseInt(n.limit||"100")}),c=s.objects;if(o.succeed(`Found ${c.length} object(s)${s.nextContinuationToken?" (truncated)":""}`),c.length===0){a("No objects found");return}if(x(["Key","Size","Last Modified"],c.map((l)=>[l.Key||"N/A",In(l.Size||0),l.LastModified?new Date(l.LastModified).toLocaleString():"N/A"])),s.nextContinuationToken)a(`
493
+ More objects available. Use --limit to see more.`)}catch(i){p(`Failed to list objects: ${i.message}`),process.exit(1)}});let e=(t,n)=>r.command(t,n).option("--env <environment>","Dashboard environment");e("volume:list","List persistent volumes, consumers, usage, and protection").option("--json","Print structured JSON").action(async(t)=>{let n=await Ne(t.env);try{let i=n.store.inventory(n.controlPlane.project.id,n.environmentRecord.id);if(t.json)console.log(JSON.stringify(i,null,2));else x(["Name","Driver","Status","Capacity","Used","Consumers","Backup","Ownership"],Vp(i))}catch(i){p(i instanceof Error?i.message:String(i))}finally{n.controlPlane.store.close()}}),e("volume:show <name>","Inspect a persistent volume and its dependency graph").option("--json","Print structured JSON").action(async(t,n)=>{let i=await Ne(n.env);try{let o=i.store.inventory(i.controlPlane.project.id,i.environmentRecord.id).find((s)=>s.id===t||s.name===t);if(!o)throw Error(`Volume ${t} was not found.`);if(n.json)console.log(JSON.stringify(o,null,2));else x(["Property","Value"],[["ID",o.id],["Driver",`${o.provider}/${o.type}`],["Provider ID",o.providerId??"—"],["Status",o.status],["Capacity",o.capacityBytes==null?"—":In(o.capacityBytes)],["Used",o.usedBytes==null?"—":In(o.usedBytes)],["Backup",o.backupState]]),x(["Resource","Target","Mode","State"],o.attachments.map((s)=>[s.resourceId,s.targetPath,s.readOnly?"read only":"read/write",s.observedState]))}catch(o){p(o instanceof Error?o.message:String(o))}finally{i.controlPlane.store.close()}}),e("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(t,n)=>{let i=await Ne(n.env);try{let o=n.driver==="server";if(!o&&n.driver!=="docker")throw Error("--driver must be docker or server.");let s=i.service.create({organizationId:i.controlPlane.organization.id,projectId:i.controlPlane.project.id,environmentId:i.environmentRecord.id,name:t,provider:o?"server":"docker",type:o?"server_path":"docker",capacityBytes:n.capacityBytes?Number(n.capacityBytes):void 0,encrypted:!!n.encrypted,actorId:i.actor.id});g(`Volume create queued: ${s.operation.id}`)}catch(o){p(o instanceof Error?o.message:String(o))}finally{i.controlPlane.store.close()}}),e("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(t,n,i,o)=>{let s=await Ne(o.env);try{let c=s.store.list({projectId:s.controlPlane.project.id,environmentId:s.environmentRecord.id}).find((u)=>u.id===t||u.name===t),l=s.controlPlane.store.listResources(s.controlPlane.project.id,s.environmentRecord.id).find((u)=>u.id===n||u.slug===n);if(!c||!l)throw Error("Volume or resource was not found in the selected environment.");let d=s.service.attach(c.id,{resourceId:l.id,targetPath:i,readOnly:!!o.readOnly,uid:o.uid?Number(o.uid):void 0,gid:o.gid?Number(o.gid):void 0,mode:o.mode,actorId:s.actor.id});g(`Volume attachment queued: ${d.operation.id}`)}catch(c){p(c instanceof Error?c.message:String(c))}finally{s.controlPlane.store.close()}}),e("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(t,n,i)=>{let o=await Ne(i.env);try{let s=o.store.list({projectId:o.controlPlane.project.id,environmentId:o.environmentRecord.id}).find((l)=>l.id===t||l.name===t);if(!s)throw Error("Volume was not found.");let c=o.service.detach(s.id,n,{drained:!0,unmounted:!0,force:!!i.force,confirmation:i.confirm,actorId:o.actor.id});g(`Volume detach queued: ${c.operation.id}`)}catch(s){p(s instanceof Error?s.message:String(s))}finally{o.controlPlane.store.close()}}),e("volume:resize <volume> <bytes>","Grow a capable persistent volume").option("--drained","Confirm offline workload drain").action(async(t,n,i)=>{let o=await Ne(i.env);try{let s=o.store.list({projectId:o.controlPlane.project.id,environmentId:o.environmentRecord.id}).find((l)=>l.id===t||l.name===t);if(!s)throw Error("Volume was not found.");let c=o.service.resize(s.id,Number(n),{drained:!!i.drained,actorId:o.actor.id});g(`Volume resize queued: ${c.operation.id}`)}catch(s){p(s instanceof Error?s.message:String(s))}finally{o.controlPlane.store.close()}}),e("volume:snapshot <volume>","Create a provider snapshot when supported").action(async(t,n)=>{let i=await Ne(n.env);try{let o=i.store.list({projectId:i.controlPlane.project.id,environmentId:i.environmentRecord.id}).find((c)=>c.id===t||c.name===t);if(!o)throw Error("Volume was not found.");let s=i.service.snapshot(o.id,{actorId:i.actor.id});g(`Volume snapshot queued: ${s.operation.id}`)}catch(o){p(o instanceof Error?o.message:String(o))}finally{i.controlPlane.store.close()}}),e("volume:restore <snapshot> <name>","Restore a snapshot into a replacement volume").action(async(t,n,i)=>{let o=await Ne(i.env);try{let s=o.service.restore(t,{name:n,actorId:o.actor.id});g(`Volume restore queued: ${s.operation.id}`)}catch(s){p(s instanceof Error?s.message:String(s))}finally{o.controlPlane.store.close()}}),e("volume:discover","Discover provider volumes without adopting or deleting them").action(async(t)=>{let n=await Ne(t.env);try{let i=await n.service.discover({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,environmentId:n.environmentRecord.id,actorId:n.actor.id});g(`Discovered ${i.filter((o)=>o.status==="orphaned").length} orphan volume(s).`)}catch(i){p(i instanceof Error?i.message:String(i))}finally{n.controlPlane.store.close()}}),e("volume:adopt <volume> <name>","Adopt a discovered orphan under a managed name").action(async(t,n,i)=>{let o=await Ne(i.env);try{o.service.adopt(t,{name:n,actorId:o.actor.id}),g(`Adopted ${n}.`)}catch(s){p(s instanceof Error?s.message:String(s))}finally{o.controlPlane.store.close()}}),e("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(t,n)=>{let i=await Ne(n.env);try{let o=i.store.list({projectId:i.controlPlane.project.id,environmentId:i.environmentRecord.id}).find((c)=>c.id===t||c.name===t);if(!o)throw Error("Volume was not found.");let s=i.service.delete(o.id,{recentAuthAt:new Date().toISOString(),confirmation:n.confirm,backupOverride:!!n.withoutBackup,backupOverrideConfirmation:n.backupConfirm,actorId:i.actor.id});g(`Volume deletion queued: ${s.operation.id}`)}catch(o){p(o instanceof Error?o.message:String(o))}finally{i.controlPlane.store.close()}})}function Wp(r){let e=r.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"}[e||""]||"application/octet-stream"}function In(r){if(r===0)return"0 B";let e=1024,t=["B","KB","MB","GB","TB"],n=Math.floor(Math.log(r)/Math.log(e));return`${Number.parseFloat((r/e**n).toFixed(2))} ${t[n]}`}var Hl=["aws","backblaze","hetzner"];function Jl(r,e){let t=r.indexOf(":");if(t===-1)throw Error(`--${e} must be in the form <provider:bucket> (e.g. aws:my-bucket), got "${r}"`);let n=r.slice(0,t),i=r.slice(t+1);if(!Hl.includes(n))throw Error(`Unknown provider "${n}" for --${e}. Supported: ${Hl.join(", ")}`);if(!i)throw Error(`--${e} is missing a bucket name (expected <provider:bucket>)`);return{provider:n,bucket:i}}function Yl(r){if(!r)return;let e=r.split(",").map((t)=>t.trim()).filter(Boolean);return e.length>0?e:void 0}function Wo(r){r.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(e)=>{w("Migrate Object Storage");try{if(!e.from||!e.to)throw Error("Both --from <provider:bucket> and --to <provider:bucket> are required");let t=Jl(e.from,"from"),n=Jl(e.to,"to"),i=Yl(e.include),o=Yl(e.exclude),s=Number.parseInt(e.concurrency||"8",10),c={provider:t.provider,bucket:t.bucket,region:e.fromRegion,endpoint:e.fromEndpoint,prefix:e.fromPrefix},l={provider:n.provider,bucket:n.bucket,region:e.toRegion,endpoint:e.toEndpoint,prefix:e.toPrefix};if(a(`Source: ${t.provider}:${t.bucket}${e.fromPrefix?`/${e.fromPrefix}`:""}`),a(`Destination: ${n.provider}:${n.bucket}${e.toPrefix?`/${e.toPrefix}`:""}`),i)a(`Include: ${i.join(", ")}`);if(o)a(`Exclude: ${o.join(", ")}`);if(a(`Concurrency: ${s}`),e.dryRun)I("Dry run — no objects will be written");if(e.deleteExtraneous&&!e.dryRun)I("Delete-extraneous ON — destination keys not in the source will be removed");let d=new C("Listing source objects...");d.start();let u=0,m=await Vo({from:c,to:l,include:i,exclude:o,dryRun:e.dryRun,force:e.force,deleteExtraneous:e.deleteExtraneous,concurrency:s,verify:e.verify,onProgress:(f)=>{if(f.index-u>=1||f.index===f.total){u=f.index;let h=f.action==="planned"?"plan":f.action;d.text=`[${f.index}/${f.total}] ${h}: ${f.key}`}}});if(d.succeed(e.dryRun?"Plan ready":"Migration complete"),e.dryRun&&m.plan){a(`
494
+ Would copy ${m.plan.length} object(s):`);for(let f of m.plan.slice(0,50))a(` ${f.key} -> ${f.destKey} (${Pn(f.size)})`);if(m.plan.length>50)a(` ... and ${m.plan.length-50} more`)}if(m.excludedKeys.length>0){a(`
495
+ EXCLUDED ${m.excludedKeys.length} key(s) (deliberately not migrated):`);for(let f of m.excludedKeys.slice(0,50))a(` ${f}`);if(m.excludedKeys.length>50)a(` ... and ${m.excludedKeys.length-50} more`)}if(m.deleted.length>0){I(`
496
+ Deleted ${m.deleted.length} extraneous destination key(s):`);for(let f of m.deleted.slice(0,50))I(` ${f}`)}if(a(`
497
+ Summary:`),e.dryRun)a(` would copy: ${m.plan?.length??0}`);else g(` copied: ${m.copied} (${Pn(m.bytesCopied)})`),a(` skipped: ${m.skipped} (already present)`);if(a(` excluded: ${m.excluded}`),m.deleted.length>0)a(` deleted: ${m.deleted.length}`);if(m.errors.length>0){p(` errors: ${m.errors.length}`);for(let f of m.errors.slice(0,20))p(` ${f.key}: ${f.message}`)}if(m.verification){let f=m.verification;if(f.ok)g(`
498
+ Verification PASSED — ${f.matched} object(s) present at destination with matching sizes`);else{p(`
499
+ Verification FAILED — ${f.missing.length} missing, ${f.sizeMismatches.length} size mismatch(es)`);for(let h of f.missing.slice(0,20))p(` missing: ${h}`);for(let h of f.sizeMismatches.slice(0,20))p(` size: ${h.key} expected ${h.expected} got ${h.actual}`)}}if(m.errors.length>0||m.verification?.ok===!1)process.exit(1)}catch(t){p(`Migration failed: ${t.message}`),process.exit(1)}})}function qo(r){r.command("cache:list","List all ElastiCache clusters").option("--region <region>","AWS region").action(async(e)=>{w("ElastiCache Clusters");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new qe(n),o=new C("Fetching clusters...");o.start();let c=(await i.describeCacheClusters()).CacheClusters||[];if(o.succeed(`Found ${c.length} cluster(s)`),c.length===0){a("No ElastiCache clusters found"),a("Use `cloud cache:create` to create a new cluster");return}x(["Cluster ID","Engine","Node Type","Nodes","Status"],c.map((l)=>[l.CacheClusterId||"N/A",`${l.Engine||"N/A"} ${l.EngineVersion||""}`,l.CacheNodeType||"N/A",(l.NumCacheNodes||0).toString(),l.CacheClusterStatus||"N/A"]))}catch(t){p(`Failed to list clusters: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create ElastiCache Cluster");try{let n=new qe(t.region);if(a(`Cluster ID: ${e}`),a(`Engine: ${t.engine}`),a(`Node Type: ${t.nodeType}`),a(`Number of Nodes: ${t.nodes}`),a(`Region: ${t.region}`),!await O(`
500
+ Create this cluster?`,!0)){a("Operation cancelled");return}let o=new C("Creating cluster...");o.start(),await n.createCacheCluster({cacheClusterId:e,engine:t.engine,cacheNodeType:t.nodeType,numCacheNodes:Number.parseInt(t.nodes),engineVersion:t.version}),o.succeed("Cluster creation initiated"),g(`
501
+ Cluster: ${e}`),a("Status: creating"),a(`
502
+ Note: Cluster creation may take several minutes.`),a("Use `cloud cache:list` to check status.")}catch(n){p(`Failed to create cluster: ${n.message}`),process.exit(1)}}),r.command("cache:delete <clusterId>","Delete an ElastiCache cluster").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Delete ElastiCache Cluster");try{let n=new qe(t.region);if(I(`This will permanently delete cluster: ${e}`),I("All data in the cluster will be lost!"),!await O(`
503
+ Delete this cluster?`,!1)){a("Operation cancelled");return}let o=new C("Deleting cluster...");o.start(),await n.deleteCacheCluster(e),o.succeed("Cluster deletion initiated"),a(`
504
+ Note: Cluster deletion may take several minutes.`)}catch(n){p(`Failed to delete cluster: ${n.message}`),process.exit(1)}}),r.command("cache:stats <clusterId>","Show ElastiCache cluster statistics").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w(`ElastiCache Stats: ${e}`);try{let n=new qe(t.region),i=new C("Fetching cluster details...");i.start();let s=(await n.describeCacheClusters(e)).CacheClusters?.[0];if(!s){i.fail("Cluster not found");return}if(i.succeed("Cluster details loaded"),a(`
505
+ Cluster Information:`),a(` Cluster ID: ${s.CacheClusterId}`),a(` Engine: ${s.Engine} ${s.EngineVersion}`),a(` Node Type: ${s.CacheNodeType}`),a(` Status: ${s.CacheClusterStatus}`),a(` Nodes: ${s.NumCacheNodes}`),a(` Availability Zone: ${s.PreferredAvailabilityZone||"Not set"}`),s.CacheNodes&&s.CacheNodes.length>0){a(`
506
+ Cache Nodes:`);for(let c of s.CacheNodes)if(a(` - ${c.CacheNodeId}: ${c.CacheNodeStatus}`),c.Endpoint)a(` Endpoint: ${c.Endpoint.Address}:${c.Endpoint.Port}`)}}catch(n){p(`Failed to get cluster stats: ${n.message}`),process.exit(1)}}),r.command("cache:flush <clusterId>","Flush all data from a Redis cluster").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Flush ElastiCache Cluster");try{let n=new qe(t.region),i=new C("Getting cluster endpoint...");i.start();let s=(await n.describeCacheClusters(e)).CacheClusters?.[0];if(!s){i.fail("Cluster not found");return}if(s.Engine!=="redis"){i.fail("Flush is only supported for Redis clusters"),a("For Memcached, items expire automatically based on TTL.");return}i.stop();let c=s.CacheNodes?.[0]?.Endpoint;if(!c){p("Could not determine cluster endpoint");return}if(I(`This will delete ALL data in cluster: ${e}`),I(`Endpoint: ${c.Address}:${c.Port}`),I("This action cannot be undone!"),!await O(`
507
+ Flush all data?`,!1)){a("Operation cancelled");return}a(`
508
+ To flush the Redis cluster, connect and run FLUSHALL:`),a(` redis-cli -h ${c.Address} -p ${c.Port} FLUSHALL`),a(`
509
+ Note: Direct FLUSHALL via AWS API is not supported.`),a("You must connect to the cluster directly to execute this command.")}catch(n){p(`Failed to get cluster info: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Reboot ElastiCache Cluster");try{let n=new qe(t.region),i=new C("Getting cluster info...");i.start();let s=(await n.describeCacheClusters(e)).CacheClusters?.[0];if(!s){i.fail("Cluster not found");return}i.stop();let c=t.node?[t.node]:s.CacheNodes?.map((u)=>u.CacheNodeId).filter(Boolean)||[];if(c.length===0){p("No nodes found to reboot");return}if(I(`This will reboot the following nodes: ${c.join(", ")}`),I("The cluster may be temporarily unavailable during reboot."),!await O(`
510
+ Reboot these nodes?`,!1)){a("Operation cancelled");return}let d=new C("Rebooting nodes...");d.start(),await n.rebootCacheCluster(e,c),d.succeed("Reboot initiated"),a(`
511
+ Note: Reboot may take several minutes to complete.`),a("Use `cloud cache:stats` to check status.")}catch(n){p(`Failed to reboot cluster: ${n.message}`),process.exit(1)}})}function Go(r){r.command("queue:list","List all SQS queues").option("--region <region>","AWS region").action(async(e)=>{w("SQS Queues");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new at(n),o=new C("Fetching queues...");o.start();let c=(await i.listQueues()).QueueUrls||[];if(o.succeed(`Found ${c.length} queue(s)`),c.length===0){a("No SQS queues found"),a("Use `cloud queue:create` to create a new queue");return}let l=[];for(let d of c)try{let u=await i.getQueueAttributes(d),m=d.split("/").pop()||d;l.push({url:d,name:m,messages:u.Attributes?.ApproximateNumberOfMessages||"0",type:u.Attributes?.FifoQueue==="true"?"FIFO":"Standard"})}catch{let u=d.split("/").pop()||d;l.push({url:d,name:u,messages:"N/A",type:"Unknown"})}x(["Queue Name","Messages","Type"],l.map((d)=>[d.name,d.messages,d.type]))}catch(t){p(`Failed to list queues: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create SQS Queue");try{let n=new at(t.region),i=t.fifo&&!e.endsWith(".fifo")?`${e}.fifo`:e;if(a(`Queue name: ${i}`),a(`Type: ${t.fifo?"FIFO":"Standard"}`),a(`Visibility timeout: ${t.visibility} seconds`),a(`Message retention: ${t.retention} days`),t.dlq)a(`Dead letter queue: ${t.dlq}`),a(`Max retries: ${t.maxRetries}`);if(!await O(`
512
+ Create this queue?`,!0)){a("Operation cancelled");return}let s=new C("Creating queue...");s.start();let c=await n.createQueue({queueName:i,fifo:t.fifo,visibilityTimeout:Number.parseInt(t.visibility),messageRetentionPeriod:Number.parseInt(t.retention)*24*60*60,contentBasedDeduplication:t.fifo?!0:void 0,deadLetterTargetArn:t.dlq,maxReceiveCount:t.dlq?Number.parseInt(t.maxRetries):void 0});s.succeed("Queue created"),g(`
513
+ Queue URL: ${c.QueueUrl}`),a(`
514
+ To send a message:`),a(` cloud queue:send ${i} --message "Hello World"`)}catch(n){p(`Failed to create queue: ${n.message}`),process.exit(1)}}),r.command("queue:delete <name>","Delete an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Delete SQS Queue");try{let n=new at(t.region);if(I(`This will permanently delete queue: ${e}`),I("All messages in the queue will be lost!"),!await O(`
515
+ Delete this queue?`,!1)){a("Operation cancelled");return}let o=new C("Getting queue URL...");o.start();let s=await n.getQueueUrl(e);if(!s.QueueUrl){o.fail("Queue not found");return}o.text="Deleting queue...",await n.deleteQueue(s.QueueUrl),o.succeed("Queue deleted")}catch(n){p(`Failed to delete queue: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Send SQS Message");try{let n=new at(t.region),i;if(t.file)i=await Bun.file(t.file).text();else if(t.message)i=t.message;else i=await Z("Message body");if(!i){p("Message body is required");return}let o=new C("Getting queue URL...");o.start();let s=await n.getQueueUrl(e);if(!s.QueueUrl){o.fail("Queue not found");return}o.text="Sending message...";let c=await n.sendMessage({queueUrl:s.QueueUrl,messageBody:i,delaySeconds:Number.parseInt(t.delay),messageGroupId:t.group,messageDeduplicationId:t.dedup});o.succeed("Message sent"),g(`
516
+ Message ID: ${c.MessageId}`)}catch(n){p(`Failed to send message: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Receive SQS Messages");try{let n=new at(t.region),i=new C("Getting queue URL...");i.start();let o=await n.getQueueUrl(e);if(!o.QueueUrl){i.fail("Queue not found");return}i.text="Receiving messages...";let c=(await n.receiveMessages({queueUrl:o.QueueUrl,maxMessages:Number.parseInt(t.max),waitTimeSeconds:Number.parseInt(t.wait)})).Messages||[];if(i.succeed(`Received ${c.length} message(s)`),c.length===0){a("No messages available");return}for(let l of c){if(a(`
517
+ --- Message: ${l.MessageId} ---`),a(`Body: ${l.Body}`),l.Attributes)a(`Sent: ${l.Attributes.SentTimestamp?new Date(Number.parseInt(l.Attributes.SentTimestamp)).toISOString():"N/A"}`),a(`Receive Count: ${l.Attributes.ApproximateReceiveCount||"N/A"}`);if(t.delete&&l.ReceiptHandle)await n.deleteMessage(o.QueueUrl,l.ReceiptHandle),a("(Deleted)")}}catch(n){p(`Failed to receive messages: ${n.message}`),process.exit(1)}}),r.command("queue:purge <name>","Purge all messages from an SQS queue").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Purge SQS Queue");try{let n=new at(t.region);if(I(`This will delete ALL messages in queue: ${e}`),I("This action cannot be undone!"),!await O(`
518
+ Purge this queue?`,!1)){a("Operation cancelled");return}let o=new C("Getting queue URL...");o.start();let s=await n.getQueueUrl(e);if(!s.QueueUrl){o.fail("Queue not found");return}o.text="Purging queue...",await n.purgeQueue(s.QueueUrl),o.succeed("Queue purged"),a(`
519
+ Note: It may take up to 60 seconds for the purge to complete.`)}catch(n){p(`Failed to purge queue: ${n.message}`),process.exit(1)}}),r.command("queue:stats <name>","Show SQS queue statistics").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w(`SQS Queue Stats: ${e}`);try{let n=new at(t.region),i=new C("Fetching queue stats...");i.start();let o=await n.getQueueUrl(e);if(!o.QueueUrl){i.fail("Queue not found");return}let s=await n.getQueueAttributes(o.QueueUrl);i.succeed("Stats loaded");let c=s.Attributes||{};if(a(`
520
+ Queue Information:`),a(` URL: ${o.QueueUrl}`),a(` ARN: ${c.QueueArn||"N/A"}`),a(` Type: ${c.FifoQueue==="true"?"FIFO":"Standard"}`),a(`
521
+ Messages:`),a(` Available: ${c.ApproximateNumberOfMessages||"0"}`),a(` In Flight: ${c.ApproximateNumberOfMessagesNotVisible||"0"}`),a(` Delayed: ${c.ApproximateNumberOfMessagesDelayed||"0"}`),a(`
522
+ Configuration:`),a(` Visibility Timeout: ${c.VisibilityTimeout||"30"} seconds`),a(` Message Retention: ${Number.parseInt(c.MessageRetentionPeriod||"345600")/86400} days`),a(` Max Message Size: ${Number.parseInt(c.MaximumMessageSize||"262144")/1024} KB`),a(` Receive Wait Time: ${c.ReceiveMessageWaitTimeSeconds||"0"} seconds`),c.RedrivePolicy){let l=JSON.parse(c.RedrivePolicy);a(`
523
+ Dead Letter Queue:`),a(` Target ARN: ${l.deadLetterTargetArn}`),a(` Max Receives: ${l.maxReceiveCount}`)}a(`
524
+ Timestamps:`),a(` Created: ${c.CreatedTimestamp?new Date(Number.parseInt(c.CreatedTimestamp)*1000).toISOString():"N/A"}`),a(` Last Modified: ${c.LastModifiedTimestamp?new Date(Number.parseInt(c.LastModifiedTimestamp)*1000).toISOString():"N/A"}`)}catch(n){p(`Failed to get queue stats: ${n.message}`),process.exit(1)}})}function Ko(r){r.command("network:list","List all VPCs").option("--region <region>","AWS region").action(async(e)=>{w("VPCs");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new Ce(n),o=new C("Fetching VPCs...");o.start();let c=(await i.describeVpcs()).Vpcs||[];if(o.succeed(`Found ${c.length} VPC(s)`),c.length===0){a("No VPCs found");return}x(["VPC ID","Name","CIDR","State","Default"],c.map((l)=>[l.VpcId||"N/A",l.Tags?.find((d)=>d.Key==="Name")?.Value||"-",l.CidrBlock||"N/A",l.State||"N/A",l.IsDefault?"Yes":"No"]))}catch(t){p(`Failed to list VPCs: ${t.message}`),process.exit(1)}}),r.command("network:subnets","List all subnets").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(e)=>{w("Subnets");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new Ce(n),o=new C("Fetching subnets...");o.start();let s=e.vpc?[{Name:"vpc-id",Values:[e.vpc]}]:void 0,l=(await i.describeSubnets({Filters:s})).Subnets||[];if(o.succeed(`Found ${l.length} subnet(s)`),l.length===0){a("No subnets found");return}x(["Subnet ID","Name","VPC","CIDR","AZ","IPs Available","Public"],l.map((d)=>[d.SubnetId||"N/A",d.Tags?.find((u)=>u.Key==="Name")?.Value||"-",d.VpcId||"N/A",d.CidrBlock||"N/A",d.AvailabilityZone||"N/A",(d.AvailableIpAddressCount||0).toString(),d.MapPublicIpOnLaunch?"Yes":"No"]))}catch(t){p(`Failed to list subnets: ${t.message}`),process.exit(1)}}),r.command("network:security-groups","List all security groups").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(e)=>{w("Security Groups");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new Ce(n),o=new C("Fetching security groups...");o.start();let s=e.vpc?[{Name:"vpc-id",Values:[e.vpc]}]:void 0,l=(await i.describeSecurityGroups({Filters:s})).SecurityGroups||[];if(o.succeed(`Found ${l.length} security group(s)`),l.length===0){a("No security groups found");return}x(["Group ID","Name","VPC","Description","Inbound Rules","Outbound Rules"],l.map((d)=>[d.GroupId||"N/A",d.GroupName||"N/A",d.VpcId||"N/A",(d.Description||"").substring(0,30),(d.IpPermissions?.length||0).toString(),(d.IpPermissionsEgress?.length||0).toString()]))}catch(t){p(`Failed to list security groups: ${t.message}`),process.exit(1)}}),r.command("network:security-group <groupId>","Show security group details").option("--region <region>","AWS region").action(async(e,t)=>{w(`Security Group: ${e}`);try{let n=await P(),i=t.region||n.project.region||"us-east-1",o=new Ce(i),s=new C("Fetching security group...");s.start();let l=(await o.describeSecurityGroups({GroupIds:[e]})).SecurityGroups?.[0];if(!l){s.fail("Security group not found");return}if(s.succeed("Security group loaded"),a(`
525
+ General Information:`),a(` Group ID: ${l.GroupId}`),a(` Name: ${l.GroupName}`),a(` VPC: ${l.VpcId}`),a(` Description: ${l.Description}`),l.IpPermissions&&l.IpPermissions.length>0){a(`
526
+ Inbound Rules:`);for(let d of l.IpPermissions){let u=d.IpProtocol==="-1"?"All":d.IpProtocol?.toUpperCase(),m=d.FromPort===d.ToPort?d.FromPort?.toString()||"All":`${d.FromPort}-${d.ToPort}`;for(let f of d.IpRanges||[])a(` - ${u} ${m} from ${f.CidrIp}${f.Description?` (${f.Description})`:""}`);for(let f of d.UserIdGroupPairs||[])a(` - ${u} ${m} from ${f.GroupId}${f.Description?` (${f.Description})`:""}`)}}else a(`
527
+ No inbound rules configured.`);if(l.IpPermissionsEgress&&l.IpPermissionsEgress.length>0){a(`
528
+ Outbound Rules:`);for(let d of l.IpPermissionsEgress){let u=d.IpProtocol==="-1"?"All":d.IpProtocol?.toUpperCase(),m=d.FromPort===d.ToPort?d.FromPort?.toString()||"All":`${d.FromPort}-${d.ToPort}`;for(let f of d.IpRanges||[])a(` - ${u} ${m} to ${f.CidrIp}${f.Description?` (${f.Description})`:""}`);for(let f of d.UserIdGroupPairs||[])a(` - ${u} ${m} to ${f.GroupId}${f.Description?` (${f.Description})`:""}`)}}else a(`
529
+ No outbound rules configured.`);if(l.Tags&&l.Tags.length>0){a(`
530
+ Tags:`);for(let d of l.Tags)a(` ${d.Key}: ${d.Value}`)}}catch(n){p(`Failed to get security group: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create VPC");try{let n=new Ce(t.region);if(a(`CIDR Block: ${e}`),a(`Region: ${t.region}`),a(`Tenancy: ${t.tenancy}`),t.name)a(`Name: ${t.name}`);if(!await O(`
531
+ Create this VPC?`,!0)){a("Operation cancelled");return}let o=new C("Creating VPC...");o.start();let s=await n.createVpc({CidrBlock:e,InstanceTenancy:t.tenancy,TagSpecifications:t.name?[{ResourceType:"vpc",Tags:[{Key:"Name",Value:t.name}]}]:void 0});o.succeed("VPC created"),g(`
532
+ VPC ID: ${s.Vpc?.VpcId}`),a(`CIDR: ${s.Vpc?.CidrBlock}`),a(`State: ${s.Vpc?.State}`)}catch(n){p(`Failed to create VPC: ${n.message}`),process.exit(1)}}),r.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(e,t,n)=>{w("Create Subnet");try{let i=new Ce(n.region);if(a(`VPC: ${e}`),a(`CIDR Block: ${t}`),n.az)a(`Availability Zone: ${n.az}`);if(n.name)a(`Name: ${n.name}`);if(a(`Auto-assign Public IP: ${n.public?"Yes":"No"}`),!await O(`
533
+ Create this subnet?`,!0)){a("Operation cancelled");return}let s=new C("Creating subnet...");s.start();let c=await i.createSubnet({VpcId:e,CidrBlock:t,AvailabilityZone:n.az,TagSpecifications:n.name?[{ResourceType:"subnet",Tags:[{Key:"Name",Value:n.name}]}]:void 0});if(n.public&&c.Subnet?.SubnetId)s.text="Enabling auto-assign public IP...",await i.modifySubnetAttribute({SubnetId:c.Subnet.SubnetId,MapPublicIpOnLaunch:{Value:!0}});s.succeed("Subnet created"),g(`
534
+ Subnet ID: ${c.Subnet?.SubnetId}`),a(`CIDR: ${c.Subnet?.CidrBlock}`),a(`Availability Zone: ${c.Subnet?.AvailabilityZone}`)}catch(i){p(`Failed to create subnet: ${i.message}`),process.exit(1)}}),r.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(e,t,n)=>{w("Create Security Group");try{let i=new Ce(n.region),o=n.description||`Security group for ${t}`;if(a(`VPC: ${e}`),a(`Name: ${t}`),a(`Description: ${o}`),!await O(`
535
+ Create this security group?`,!0)){a("Operation cancelled");return}let c=new C("Creating security group...");c.start();let l=await i.createSecurityGroup({GroupName:t,Description:o,VpcId:e,TagSpecifications:[{ResourceType:"security-group",Tags:[{Key:"Name",Value:t}]}]});c.succeed("Security group created"),g(`
536
+ Group ID: ${l.GroupId}`),a(`
537
+ To add rules:`),a(` cloud network:sg-rule ${l.GroupId} --inbound --port 443 --cidr 0.0.0.0/0`)}catch(i){p(`Failed to create security group: ${i.message}`),process.exit(1)}}),r.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(e,t)=>{w("Add Security Group Rule");try{let n=new Ce(t.region);if(!t.inbound&&!t.outbound){p("Specify --inbound or --outbound");return}if(!t.cidr&&!t.sourceGroup){p("Specify --cidr or --source-group");return}let i,o;if(t.port)if(t.port.includes("-")){let[m,f]=t.port.split("-");i=Number.parseInt(m),o=Number.parseInt(f)}else i=Number.parseInt(t.port),o=i;else if(t.protocol!=="-1"){p("Port is required for TCP/UDP protocols");return}let s=t.inbound?"Inbound":"Outbound",c=t.protocol==="-1"?"All":i===o?i:`${i}-${o}`;if(a(`Security Group: ${e}`),a(`Direction: ${s}`),a(`Protocol: ${t.protocol}`),a(`Port(s): ${c}`),a(`Source: ${t.cidr||t.sourceGroup}`),!await O(`
538
+ Add this rule?`,!0)){a("Operation cancelled");return}let d=new C("Adding rule...");d.start();let u={IpProtocol:t.protocol,FromPort:i,ToPort:o};if(t.cidr)u.IpRanges=[{CidrIp:t.cidr,Description:t.description}];if(t.sourceGroup)u.UserIdGroupPairs=[{GroupId:t.sourceGroup,Description:t.description}];if(t.inbound)await n.authorizeSecurityGroupIngress({GroupId:e,IpPermissions:[u]});else await n.authorizeSecurityGroupEgress({GroupId:e,IpPermissions:[u]});d.succeed("Rule added")}catch(n){p(`Failed to add rule: ${n.message}`),process.exit(1)}}),r.command("network:elastic-ips","List Elastic IP addresses").option("--region <region>","AWS region").action(async(e)=>{w("Elastic IP Addresses");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new Ce(n),o=new C("Fetching Elastic IPs...");o.start();let c=(await i.describeAddresses()).Addresses||[];if(o.succeed(`Found ${c.length} Elastic IP(s)`),c.length===0){a("No Elastic IPs found");return}x(["Public IP","Allocation ID","Association ID","Instance","Name"],c.map((l)=>[l.PublicIp||"N/A",l.AllocationId||"N/A",l.AssociationId||"-",l.InstanceId||"-",l.Tags?.find((d)=>d.Key==="Name")?.Value||"-"]))}catch(t){p(`Failed to list Elastic IPs: ${t.message}`),process.exit(1)}}),r.command("network:route-tables","List route tables").option("--region <region>","AWS region").option("--vpc <vpcId>","Filter by VPC ID").action(async(e)=>{w("Route Tables");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new Ce(n),o=new C("Fetching route tables...");o.start();let s=e.vpc?[{Name:"vpc-id",Values:[e.vpc]}]:void 0,l=(await i.describeRouteTables({Filters:s})).RouteTables||[];if(o.succeed(`Found ${l.length} route table(s)`),l.length===0){a("No route tables found");return}x(["Route Table ID","VPC","Name","Main","Associations","Routes"],l.map((d)=>[d.RouteTableId||"N/A",d.VpcId||"N/A",d.Tags?.find((u)=>u.Key==="Name")?.Value||"-",d.Associations?.some((u)=>u.Main)?"Yes":"No",(d.Associations?.length||0).toString(),(d.Routes?.length||0).toString()]))}catch(t){p(`Failed to list route tables: ${t.message}`),process.exit(1)}})}function Ho(r){r.command("scheduler:list","List all EventBridge schedules").option("--region <region>","AWS region").option("--group <name>","Schedule group name",{default:"default"}).action(async(e)=>{w("EventBridge Schedules");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new ct(n),o=new C("Fetching schedules...");o.start();let c=(await i.listSchedules({GroupName:e.group})).Schedules||[];if(o.succeed(`Found ${c.length} schedule(s)`),c.length===0){a("No schedules found"),a("Use `cloud scheduler:create` to create a new schedule");return}x(["Name","State","Schedule Expression","Group"],c.map((l)=>[l.Name||"N/A",l.State||"N/A",l.ScheduleExpression||"N/A",l.GroupName||"default"]))}catch(t){p(`Failed to list schedules: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create EventBridge Schedule");try{if(!t.schedule){p("--schedule is required"),a("Examples:"),a(" Rate: rate(5 minutes), rate(1 hour), rate(1 day)"),a(" Cron: cron(0 12 * * ? *) - every day at 12:00 UTC");return}if(!t.targetArn){p("--target-arn is required");return}if(!t.roleArn){p("--role-arn is required"),a("The role must have permissions to invoke the target.");return}let n=new ct(t.region);if(a(`Name: ${e}`),a(`Schedule: ${t.schedule}`),a(`Target: ${t.targetArn}`),a(`Group: ${t.group}`),a(`Timezone: ${t.timezone}`),!await O(`
539
+ Create this schedule?`,!0)){a("Operation cancelled");return}let o=new C("Creating schedule...");o.start(),await n.createSchedule({Name:e,GroupName:t.group,ScheduleExpression:t.schedule,ScheduleExpressionTimezone:t.timezone,Description:t.description,State:"ENABLED",FlexibleTimeWindow:{Mode:"OFF"},Target:{Arn:t.targetArn,RoleArn:t.roleArn,Input:t.input},StartDate:t.start?new Date(t.start):void 0,EndDate:t.end?new Date(t.end):void 0}),o.succeed("Schedule created"),g(`
540
+ Schedule: ${e}`),a(`Expression: ${t.schedule}`)}catch(n){p(`Failed to create schedule: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Delete EventBridge Schedule");try{let n=new ct(t.region);if(I(`This will delete schedule: ${e}`),!await O(`
541
+ Delete this schedule?`,!1)){a("Operation cancelled");return}let o=new C("Deleting schedule...");o.start(),await n.deleteSchedule({Name:e,GroupName:t.group}),o.succeed("Schedule deleted")}catch(n){p(`Failed to delete schedule: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Enable EventBridge Schedule");try{let n=new ct(t.region),i=new C("Getting schedule...");i.start();let o=await n.getSchedule({Name:e,GroupName:t.group});if(!o){i.fail("Schedule not found");return}i.text="Enabling schedule...",await n.updateSchedule({Name:e,GroupName:t.group,ScheduleExpression:o.ScheduleExpression,ScheduleExpressionTimezone:o.ScheduleExpressionTimezone,FlexibleTimeWindow:o.FlexibleTimeWindow,Target:o.Target,State:"ENABLED"}),i.succeed("Schedule enabled")}catch(n){p(`Failed to enable schedule: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Disable EventBridge Schedule");try{let n=new ct(t.region),i=new C("Getting schedule...");i.start();let o=await n.getSchedule({Name:e,GroupName:t.group});if(!o){i.fail("Schedule not found");return}i.text="Disabling schedule...",await n.updateSchedule({Name:e,GroupName:t.group,ScheduleExpression:o.ScheduleExpression,ScheduleExpressionTimezone:o.ScheduleExpressionTimezone,FlexibleTimeWindow:o.FlexibleTimeWindow,Target:o.Target,State:"DISABLED"}),i.succeed("Schedule disabled")}catch(n){p(`Failed to disable schedule: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w(`Schedule: ${e}`);try{let n=new ct(t.region),i=new C("Fetching schedule...");i.start();let o=await n.getSchedule({Name:e,GroupName:t.group});if(!o){i.fail("Schedule not found");return}if(i.succeed("Schedule loaded"),a(`
542
+ Schedule Information:`),a(` Name: ${o.Name}`),a(` ARN: ${o.Arn}`),a(` Group: ${o.GroupName}`),a(` State: ${o.State}`),a(` Expression: ${o.ScheduleExpression}`),a(` Timezone: ${o.ScheduleExpressionTimezone}`),o.Description)a(` Description: ${o.Description}`);if(o.Target){if(a(`
543
+ Target:`),a(` ARN: ${o.Target.Arn}`),a(` Role ARN: ${o.Target.RoleArn}`),o.Target.Input)a(` Input: ${o.Target.Input}`)}if(a(`
544
+ Flexible Time Window:`),a(` Mode: ${o.FlexibleTimeWindow?.Mode}`),o.FlexibleTimeWindow?.MaximumWindowInMinutes)a(` Max Window: ${o.FlexibleTimeWindow.MaximumWindowInMinutes} minutes`);if(o.StartDate)a(`
545
+ Start Date: ${o.StartDate}`);if(o.EndDate)a(`End Date: ${o.EndDate}`);a(`
546
+ Created: ${o.CreationDate||"N/A"}`),a(`Last Modified: ${o.LastModificationDate||"N/A"}`)}catch(n){p(`Failed to get schedule: ${n.message}`),process.exit(1)}}),r.command("scheduler:groups","List schedule groups").option("--region <region>","AWS region").action(async(e)=>{w("Schedule Groups");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new ct(n),o=new C("Fetching groups...");o.start();let c=(await i.listScheduleGroups()).ScheduleGroups||[];if(o.succeed(`Found ${c.length} group(s)`),c.length===0){a("No schedule groups found");return}x(["Name","State","Created"],c.map((l)=>[l.Name||"N/A",l.State||"N/A",l.CreationDate?new Date(l.CreationDate).toLocaleString():"N/A"]))}catch(t){p(`Failed to list groups: ${t.message}`),process.exit(1)}})}function Jo(r){r.command("events:list","List all EventBridge rules").option("--region <region>","AWS region").option("--bus <name>","Event bus name",{default:"default"}).action(async(e)=>{w("EventBridge Rules");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new Je(n),o=new C("Fetching rules...");o.start();let c=(await i.listRules({EventBusName:e.bus})).Rules||[];if(o.succeed(`Found ${c.length} rule(s)`),c.length===0){a("No EventBridge rules found"),a("Use `cloud events:create` to create a new rule");return}x(["Name","State","Schedule/Pattern","Description"],c.map((l)=>[l.Name||"N/A",l.State||"N/A",l.ScheduleExpression||(l.EventPattern?"Event Pattern":"N/A"),(l.Description||"").substring(0,40)]))}catch(t){p(`Failed to list rules: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create EventBridge Rule");try{if(!t.schedule&&!t.pattern&&!t.patternFile){p("Either --schedule or --pattern/--pattern-file is required"),a(`
547
+ Examples:`),a(' Schedule: --schedule "rate(5 minutes)"'),a(` Pattern: --pattern '{"source": ["aws.ec2"]}'`);return}let n=new Je(t.region),i;if(t.patternFile)i=await Bun.file(t.patternFile).text();else if(t.pattern)i=t.pattern;if(a(`Name: ${e}`),a(`Event Bus: ${t.bus}`),t.schedule)a(`Schedule: ${t.schedule}`);if(i)a(`Event Pattern: ${i}`);if(a(`State: ${t.disabled?"DISABLED":"ENABLED"}`),!await O(`
548
+ Create this rule?`,!0)){a("Operation cancelled");return}let s=new C("Creating rule...");s.start();let c=await n.putRule({Name:e,EventBusName:t.bus,ScheduleExpression:t.schedule,EventPattern:i,Description:t.description,State:t.disabled?"DISABLED":"ENABLED"});s.succeed("Rule created"),g(`
549
+ Rule ARN: ${c.RuleArn}`),a("\nNote: Add targets to the rule with `cloud events:target`")}catch(n){p(`Failed to create rule: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Delete EventBridge Rule");try{let n=new Je(t.region);if(I(`This will delete rule: ${e}`),!await O(`
550
+ Delete this rule?`,!1)){a("Operation cancelled");return}let o=new C("Checking targets...");o.start();let s=await n.listTargetsByRule({Rule:e,EventBusName:t.bus});if(s.Targets&&s.Targets.length>0){if(!t.force){o.fail("Rule has targets"),a(`
551
+ The rule has ${s.Targets.length} target(s).`),a("Use --force to remove targets and delete the rule.");return}o.text="Removing targets...",await n.removeTargets({Rule:e,EventBusName:t.bus,Ids:s.Targets.map((c)=>c.Id)})}o.text="Deleting rule...",await n.deleteRule({Name:e,EventBusName:t.bus}),o.succeed("Rule deleted")}catch(n){p(`Failed to delete rule: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w(`Rule: ${e}`);try{let n=new Je(t.region),i=new C("Fetching rule...");i.start();let o=await n.describeRule({Name:e,EventBusName:t.bus});if(!o){i.fail("Rule not found");return}let s=await n.listTargetsByRule({Rule:e,EventBusName:t.bus});if(i.succeed("Rule loaded"),a(`
552
+ Rule Information:`),a(` Name: ${o.Name}`),a(` ARN: ${o.Arn}`),a(` State: ${o.State}`),a(` Event Bus: ${o.EventBusName}`),o.Description)a(` Description: ${o.Description}`);if(o.ScheduleExpression)a(`
553
+ Schedule Expression: ${o.ScheduleExpression}`);if(o.EventPattern)a(`
554
+ Event Pattern:`),console.log(JSON.stringify(JSON.parse(o.EventPattern),null,2));if(s.Targets&&s.Targets.length>0){a(`
555
+ Targets (${s.Targets.length}):`);for(let c of s.Targets)if(a(` - ${c.Id}: ${c.Arn}`),c.Input)a(` Input: ${c.Input}`)}else a(`
556
+ No targets configured.`)}catch(n){p(`Failed to get rule: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Add EventBridge Target");try{if(!t.arn){p("--arn is required");return}let n=new Je(t.region),i=t.id||`target-${Date.now()}`;if(a(`Rule: ${e}`),a(`Target ID: ${i}`),a(`Target ARN: ${t.arn}`),!await O(`
557
+ Add this target?`,!0)){a("Operation cancelled");return}let s=new C("Adding target...");s.start();let c={Id:i,Arn:t.arn};if(t.role)c.RoleArn=t.role;if(t.input)c.Input=t.input;if(t.inputPath)c.InputPath=t.inputPath;await n.putTargets({Rule:e,EventBusName:t.bus,Targets:[c]}),s.succeed("Target added"),g(`
558
+ Target ${i} added to rule ${e}`)}catch(n){p(`Failed to add target: ${n.message}`),process.exit(1)}}),r.command("events:buses","List event buses").option("--region <region>","AWS region").action(async(e)=>{w("Event Buses");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new Je(n),o=new C("Fetching event buses...");o.start();let c=(await i.listEventBuses()).EventBuses||[];if(o.succeed(`Found ${c.length} event bus(es)`),c.length===0){a("No event buses found");return}x(["Name","ARN","Policy"],c.map((l)=>[l.Name||"N/A",l.Arn||"N/A",l.Policy?"Custom":"Default"]))}catch(t){p(`Failed to list event buses: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("Enable EventBridge Rule");try{let n=new Je(t.region),i=new C("Enabling rule...");i.start(),await n.enableRule({Name:e,EventBusName:t.bus}),i.succeed("Rule enabled")}catch(n){p(`Failed to enable rule: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Disable EventBridge Rule");try{let n=new Je(t.region),i=new C("Disabling rule...");i.start(),await n.disableRule({Name:e,EventBusName:t.bus}),i.succeed("Rule disabled")}catch(n){p(`Failed to disable rule: ${n.message}`),process.exit(1)}})}function Yo(r){r.command("email:identities","List verified email identities").option("--region <region>","AWS region").action(async(e)=>{w("Email Identities");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new He(n),o=new C("Fetching identities...");o.start();let c=(await i.listEmailIdentities()).EmailIdentities||[];if(o.succeed(`Found ${c.length} identity(s)`),c.length===0){a("No email identities found"),a("Use `cloud email:verify` to verify an email or domain");return}let l=[];for(let d of c){let u=d.IdentityName||"Unknown",m="Unknown";try{m=(await i.getEmailIdentity(u)).VerificationStatus||"Unknown"}catch{}let f=d.IdentityType==="EMAIL_ADDRESS"?"Email":d.IdentityType==="DOMAIN"?"Domain":d.IdentityType||"Unknown";l.push([u,f,m])}x(["Identity","Type","Verification Status"],l)}catch(t){let n=t instanceof Error?t.message:String(t);p(`Failed to list identities: ${n}`),process.exit(1)}}),r.command("email:verify <identity>","Verify an email address or domain").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Verify Email Identity");try{let n=new He(t.region),i=e.includes("@");if(a(`Identity: ${e}`),a(`Type: ${i?"Email Address":"Domain"}`),!await O(`
559
+ Send verification?`,!0)){a("Operation cancelled");return}let s=new C("Initiating verification...");if(s.start(),i)await n.createEmailIdentity({EmailIdentity:e}),s.succeed("Verification email sent"),a(`
560
+ A verification email has been sent to ${e}`),a("Click the link in the email to complete verification.");else{let c=await n.verifyDomain(e);s.succeed("Domain verification initiated");let l=c.dkimTokens||[];if(l.length>0){a(`
561
+ DKIM Records (for email authentication):`);for(let d of l)a(`
562
+ Name: ${d}._domainkey.${e}`),a(" Type: CNAME"),a(` Value: ${d}.dkim.amazonses.com`)}a(`
563
+ Verification Status: ${c.verificationStatus||"PENDING"}`)}}catch(n){let i=n instanceof Error?n.message:String(n);p(`Failed to verify identity: ${i}`),process.exit(1)}}),r.command("email:delete <identity>","Delete a verified email identity").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Delete Email Identity");try{let n=new He(t.region);if(I(`This will remove identity: ${e}`),!await O(`
564
+ Delete this identity?`,!1)){a("Operation cancelled");return}let o=new C("Deleting identity...");o.start(),await n.deleteEmailIdentity(e),o.succeed("Identity deleted")}catch(n){let i=n instanceof Error?n.message:String(n);p(`Failed to delete identity: ${i}`),process.exit(1)}}),r.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(e)=>{w("Send Email");try{let t=new He(e.region),n=e.from||await Z("From (verified email)"),i=e.to||await Z("To"),o=e.subject||await Z("Subject","Test Email from ts-cloud"),s=e.body||await Z("Body","This is a test email sent from ts-cloud CLI.");if(a(`
565
+ From: ${n}`),a(`To: ${i}`),a(`Subject: ${o}`),!await O(`
566
+ Send this email?`,!0)){a("Operation cancelled");return}let l=new C("Sending email...");l.start();let d=await t.sendEmail({FromEmailAddress:n,Destination:{ToAddresses:[i]},Content:{Simple:{Subject:{Data:o},Body:{Text:{Data:s},...e.html&&{Html:{Data:e.html}}}}}});l.succeed("Email sent"),g(`
567
+ Message ID: ${d.MessageId}`)}catch(t){let n=t instanceof Error?t.message:String(t);p(`Failed to send email: ${n}`),process.exit(1)}}),r.command("email:templates","List email templates").option("--region <region>","AWS region").action(async(e)=>{w("Email Templates");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new He(n),o=new C("Fetching templates...");o.start();let c=(await i.listEmailTemplates()).TemplatesMetadata||[];if(o.succeed(`Found ${c.length} template(s)`),c.length===0){a("No email templates found"),a("Use `cloud email:template:create` to create a template");return}x(["Name","Created"],c.map((l)=>[l.TemplateName||"N/A",l.CreatedTimestamp?new Date(l.CreatedTimestamp).toLocaleString():"N/A"]))}catch(t){let n=t instanceof Error?t.message:String(t);p(`Failed to list templates: ${n}`),process.exit(1)}}),r.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(e,t)=>{w("Create Email Template");try{let n=new He(t.region),i=t.subject||await Z("Subject template","Hello {{name}}"),o=t.text||await Z("Text body","Hello {{name}}, this is a test."),s=t.html;if(t.htmlFile)s=await Bun.file(t.htmlFile).text();if(a(`
568
+ Template Name: ${e}`),a(`Subject: ${i}`),!await O(`
569
+ Create this template?`,!0)){a("Operation cancelled");return}let l=new C("Creating template...");l.start(),await n.createEmailTemplate({TemplateName:e,TemplateContent:{Subject:i,Text:o,Html:s}}),l.succeed("Template created"),a(`
570
+ To send using this template:`),a(` cloud email:send:template --template ${e} --to user@example.com --data '{"name":"John"}'`)}catch(n){let i=n instanceof Error?n.message:String(n);p(`Failed to create template: ${i}`),process.exit(1)}}),r.command("email:template:delete <name>","Delete an email template").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Delete Email Template");try{let n=new He(t.region);if(I(`This will delete template: ${e}`),!await O(`
571
+ Delete this template?`,!1)){a("Operation cancelled");return}let o=new C("Deleting template...");o.start(),await n.deleteEmailTemplate(e),o.succeed("Template deleted")}catch(n){let i=n instanceof Error?n.message:String(n);p(`Failed to delete template: ${i}`),process.exit(1)}}),r.command("email:stats","Show SES sending statistics").option("--region <region>","AWS region").action(async(e)=>{w("Email Statistics");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new He(n),o=new C("Fetching statistics...");o.start();let[s,c]=await Promise.all([i.getSendQuota(),i.getSendStatistics()]);o.succeed("Statistics loaded"),a(`
572
+ Sending Quota:`),a(` Max 24-hour Send: ${s.Max24HourSend||0}`),a(` Max Send Rate: ${s.MaxSendRate||0} emails/second`),a(` Sent Last 24h: ${s.SentLast24Hours||0}`);let l=(s.Max24HourSend||0)-(s.SentLast24Hours||0);if(a(` Remaining: ${l}`),c.SendDataPoints&&c.SendDataPoints.length>0){a(`
573
+ Recent Statistics:`);let d=0,u=0,m=0,f=0;for(let h of c.SendDataPoints)d+=h.DeliveryAttempts||0,u+=h.Bounces||0,m+=h.Complaints||0,f+=h.Rejects||0;if(a(` Total Attempts: ${d}`),a(` Bounces: ${u}`),a(` Complaints: ${m}`),a(` Rejects: ${f}`),d>0){let h=(u/d*100).toFixed(2),S=(m/d*100).toFixed(2);if(a(`
574
+ Bounce Rate: ${h}%`),a(` Complaint Rate: ${S}%`),Number.parseFloat(h)>5)I(`
575
+ Warning: Bounce rate is high (>5%). This may affect your sender reputation.`);if(Number.parseFloat(S)>0.1)I(`
576
+ Warning: Complaint rate is high (>0.1%). This may affect your sender reputation.`)}}}catch(t){let n=t instanceof Error?t.message:String(t);p(`Failed to get statistics: ${n}`),process.exit(1)}})}function Zo(r){r.command("notify:topics","List all SNS topics").option("--region <region>","AWS region").action(async(e)=>{w("SNS Topics");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=new Oe(n),o=new C("Fetching topics...");o.start();let c=(await i.listTopics()).Topics||[];if(o.succeed(`Found ${c.length} topic(s)`),c.length===0){a("No SNS topics found"),a("Use `cloud notify:create` to create a new topic");return}x(["Topic ARN","Name"],c.map((l)=>{let d=l.TopicArn?.split(":").pop()||"N/A";return[l.TopicArn||"N/A",d]}))}catch(t){p(`Failed to list topics: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create SNS Topic");try{let n=new Oe(t.region),i=t.fifo&&!e.endsWith(".fifo")?`${e}.fifo`:e;if(a(`Topic name: ${i}`),a(`Type: ${t.fifo?"FIFO":"Standard"}`),!await O(`
577
+ Create this topic?`,!0)){a("Operation cancelled");return}let s=new C("Creating topic...");s.start();let c={};if(t.fifo)c.FifoTopic="true",c.ContentBasedDeduplication="true";if(t.displayName)c.DisplayName=t.displayName;let l=await n.createTopic({Name:i,Attributes:Object.keys(c).length>0?c:void 0});s.succeed("Topic created"),g(`
578
+ Topic ARN: ${l.TopicArn}`),a(`
579
+ To subscribe:`),a(` cloud notify:subscribe ${l.TopicArn} --email user@example.com`)}catch(n){p(`Failed to create topic: ${n.message}`),process.exit(1)}}),r.command("notify:delete <topicArn>","Delete an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Delete SNS Topic");try{let n=new Oe(t.region);if(I(`This will delete topic: ${e}`),I("All subscriptions will be removed."),!await O(`
580
+ Delete this topic?`,!1)){a("Operation cancelled");return}let o=new C("Deleting topic...");o.start(),await n.deleteTopic(e),o.succeed("Topic deleted")}catch(n){p(`Failed to delete topic: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Subscribe to SNS Topic");try{let n=new Oe(t.region),i,o;if(t.email)i="email",o=t.email;else if(t.sms)i="sms",o=t.sms;else if(t.sqs)i="sqs",o=t.sqs;else if(t.lambda)i="lambda",o=t.lambda;else if(t.https)i="https",o=t.https;else{p("Specify a subscription type: --email, --sms, --sqs, --lambda, or --https");return}if(a(`Topic: ${e}`),a(`Protocol: ${i}`),a(`Endpoint: ${o}`),!await O(`
581
+ Create this subscription?`,!0)){a("Operation cancelled");return}let c=new C("Creating subscription...");c.start();let l={TopicArn:e,Protocol:i,Endpoint:o};if(t.filter)l.Attributes={FilterPolicy:t.filter};let d=await n.subscribe(l);if(c.succeed("Subscription created"),d.SubscriptionArn==="pending confirmation")a(`
582
+ Subscription is pending confirmation.`),a("The subscriber will receive a confirmation message.");else g(`
583
+ Subscription ARN: ${d.SubscriptionArn}`)}catch(n){p(`Failed to subscribe: ${n.message}`),process.exit(1)}}),r.command("notify:unsubscribe <subscriptionArn>","Unsubscribe from an SNS topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Unsubscribe from SNS Topic");try{let n=new Oe(t.region);if(I(`This will remove subscription: ${e}`),!await O(`
584
+ Unsubscribe?`,!1)){a("Operation cancelled");return}let o=new C("Unsubscribing...");o.start(),await n.unsubscribe(e),o.succeed("Unsubscribed")}catch(n){p(`Failed to unsubscribe: ${n.message}`),process.exit(1)}}),r.command("notify:subscriptions <topicArn>","List subscriptions for a topic").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Topic Subscriptions");try{let n=new Oe(t.region),i=new C("Fetching subscriptions...");i.start();let s=(await n.listSubscriptionsByTopic(e)).Subscriptions||[];if(i.succeed(`Found ${s.length} subscription(s)`),s.length===0){a("No subscriptions found");return}x(["Protocol","Endpoint","Status","Subscription ARN"],s.map((c)=>[c.Protocol||"N/A",c.Endpoint||"N/A",c.SubscriptionArn==="PendingConfirmation"?"Pending":"Confirmed",(c.SubscriptionArn||"N/A").substring(0,50)]))}catch(n){p(`Failed to list subscriptions: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Publish to SNS Topic");try{let n=new Oe(t.region),i;if(t.file)i=await Bun.file(t.file).text();else if(t.message)i=t.message;else i=await Z("Message");if(!i){p("Message is required");return}if(a(`Topic: ${e}`),a(`Message length: ${i.length} characters`),!await O(`
585
+ Publish this message?`,!0)){a("Operation cancelled");return}let s=new C("Publishing message...");s.start();let c={TopicArn:e,Message:i};if(t.subject)c.Subject=t.subject;if(t.json)c.MessageStructure="json";if(t.group)c.MessageGroupId=t.group;if(t.dedup)c.MessageDeduplicationId=t.dedup;let l=await n.publish(c);s.succeed("Message published"),g(`
586
+ Message ID: ${l.MessageId}`)}catch(n){p(`Failed to publish message: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Send SMS");try{let n=new Oe(t.region),i=t.message||await Z("Message");if(!i){p("Message is required");return}if(a(`To: ${e}`),a(`Message: ${i}`),a(`Type: ${t.type}`),!await O(`
587
+ Send this SMS?`,!0)){a("Operation cancelled");return}let s=new C("Sending SMS...");s.start();let c={PhoneNumber:e,Message:i,MessageAttributes:{"AWS.SNS.SMS.SMSType":{DataType:"String",StringValue:t.type}}};if(t.sender)c.MessageAttributes["AWS.SNS.SMS.SenderID"]={DataType:"String",StringValue:t.sender};let l=await n.publish(c);s.succeed("SMS sent"),g(`
588
+ Message ID: ${l.MessageId}`)}catch(n){p(`Failed to send SMS: ${n.message}`),process.exit(1)}}),r.command("notify:topic:attributes <topicArn>","Show topic attributes").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Topic Attributes");try{let n=new Oe(t.region),i=new C("Fetching attributes...");i.start();let o=await n.getTopicAttributes(e);if(i.succeed("Attributes loaded"),a(`
589
+ Topic Information:`),a(` ARN: ${e}`),a(` Display Name: ${o.DisplayName||"Not set"}`),a(` Owner: ${o.Owner||"N/A"}`),a(`
590
+ Subscriptions:`),a(` Confirmed: ${o.SubscriptionsConfirmed||0}`),a(` Pending: ${o.SubscriptionsPending||0}`),a(` Deleted: ${o.SubscriptionsDeleted||0}`),a(`
591
+ Settings:`),a(` FIFO: ${o.FifoTopic==="true"?"Yes":"No"}`),a(` Content Deduplication: ${o.ContentBasedDeduplication==="true"?"Yes":"No"}`),o.EffectiveDeliveryPolicy)a(`
592
+ Delivery Policy: Configured`);if(o.Policy)a(`
593
+ Access Policy: Configured`)}catch(n){p(`Failed to get attributes: ${n.message}`),process.exit(1)}})}function Qo(r){r.command("iam:roles","List IAM roles").option("--prefix <prefix>","Filter by role name prefix").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(e)=>{w("IAM Roles");try{let t=new he,n=new C("Fetching roles...");n.start();let o=(await t.listRoles({PathPrefix:e.path})).Roles||[];if(e.prefix)o=o.filter((s)=>s.RoleName?.startsWith(e.prefix));if(n.succeed(`Found ${o.length} role(s)`),o.length===0){a("No IAM roles found");return}x(["Role Name","Path","Created","Description"],o.map((s)=>[s.RoleName||"N/A",s.Path||"/",s.CreateDate?new Date(s.CreateDate).toLocaleDateString():"N/A",(s.Description||"").substring(0,40)]))}catch(t){p(`Failed to list roles: ${t.message}`),process.exit(1)}}),r.command("iam:role <roleName>","Show IAM role details").action(async(e)=>{w(`IAM Role: ${e}`);try{let t=new he,n=new C("Fetching role details...");n.start();let i=await t.getRole({RoleName:e}),o=await t.listAttachedRolePolicies({RoleName:e}),s=await t.listRolePolicies({RoleName:e});if(n.succeed("Role details loaded"),a(`
594
+ Role Information:`),a(` Name: ${i.RoleName}`),a(` ARN: ${i.Arn}`),a(` Path: ${i.Path}`),a(` Created: ${i.CreateDate?new Date(i.CreateDate).toLocaleString():"N/A"}`),i.Description)a(` Description: ${i.Description}`);if(i.MaxSessionDuration)a(` Max Session: ${i.MaxSessionDuration/3600} hours`);if(i.AssumeRolePolicyDocument){a(`
595
+ Trust Policy:`);let c=JSON.parse(decodeURIComponent(i.AssumeRolePolicyDocument));console.log(JSON.stringify(c,null,2))}if(o.AttachedPolicies&&o.AttachedPolicies.length>0){a(`
596
+ Attached Managed Policies:`);for(let c of o.AttachedPolicies)a(` - ${c.PolicyName}`)}if(s.PolicyNames&&s.PolicyNames.length>0){a(`
597
+ Inline Policies:`);for(let c of s.PolicyNames)a(` - ${c}`)}if(i.Tags&&i.Tags.length>0){a(`
598
+ Tags:`);for(let c of i.Tags)a(` ${c.Key}: ${c.Value}`)}}catch(t){p(`Failed to get role: ${t.message}`),process.exit(1)}}),r.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(e)=>{w("IAM Policies");try{let t=new he,n=new C("Fetching policies...");n.start();let o=(await t.listPolicies({Scope:e.scope,PathPrefix:e.path,OnlyAttached:!1})).Policies||[];if(e.prefix)o=o.filter((s)=>s.PolicyName?.startsWith(e.prefix));if(n.succeed(`Found ${o.length} policy(s)`),o.length===0){a("No IAM policies found");return}if(x(["Policy Name","ARN","Attachments","Created"],o.slice(0,50).map((s)=>[s.PolicyName||"N/A",(s.Arn||"N/A").substring(0,50),(s.AttachmentCount||0).toString(),s.CreateDate?new Date(s.CreateDate).toLocaleDateString():"N/A"])),o.length>50)a(`
599
+ ... and ${o.length-50} more policies`)}catch(t){p(`Failed to list policies: ${t.message}`),process.exit(1)}}),r.command("iam:policy <policyArn>","Show IAM policy details").option("--version <version>","Policy version ID").action(async(e,t)=>{w("IAM Policy Details");try{let n=new he,i=new C("Fetching policy...");i.start();let o=await n.getPolicy({PolicyArn:e}),s=t.version||o.DefaultVersionId||"v1",c=await n.getPolicyVersion({PolicyArn:e,VersionId:s});if(i.succeed("Policy loaded"),a(`
600
+ Policy Information:`),a(` Name: ${o.PolicyName}`),a(` ARN: ${o.Arn}`),a(` Path: ${o.Path}`),a(` Created: ${o.CreateDate?new Date(o.CreateDate).toLocaleString():"N/A"}`),a(` Updated: ${o.UpdateDate?new Date(o.UpdateDate).toLocaleString():"N/A"}`),a(` Attachment Count: ${o.AttachmentCount||0}`),a(` Version: ${s}`),o.Description)a(` Description: ${o.Description}`);if(c.Document){a(`
601
+ Policy Document:`);let l=JSON.parse(decodeURIComponent(c.Document));console.log(JSON.stringify(l,null,2))}}catch(n){p(`Failed to get policy: ${n.message}`),process.exit(1)}}),r.command("iam:users","List IAM users").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(e)=>{w("IAM Users");try{let t=new he,n=new C("Fetching users...");n.start();let o=(await t.listUsers({PathPrefix:e.path})).Users||[];if(n.succeed(`Found ${o.length} user(s)`),o.length===0){a("No IAM users found");return}x(["User Name","User ID","Path","Created","Password Last Used"],o.map((s)=>[s.UserName||"N/A",s.UserId||"N/A",s.Path||"/",s.CreateDate?new Date(s.CreateDate).toLocaleDateString():"N/A",s.PasswordLastUsed?new Date(s.PasswordLastUsed).toLocaleDateString():"Never"]))}catch(t){p(`Failed to list users: ${t.message}`),process.exit(1)}}),r.command("iam:user <userName>","Show IAM user details").action(async(e)=>{w(`IAM User: ${e}`);try{let t=new he,n=new C("Fetching user details...");n.start();let i=await t.getUser({UserName:e}),[o,s,c]=await Promise.all([t.listGroupsForUser({UserName:e}),t.listAttachedUserPolicies({UserName:e}),t.listAccessKeys({UserName:e})]);if(n.succeed("User details loaded"),a(`
602
+ User Information:`),a(` Name: ${i.UserName}`),a(` ARN: ${i.Arn}`),a(` User ID: ${i.UserId}`),a(` Path: ${i.Path}`),a(` Created: ${i.CreateDate?new Date(i.CreateDate).toLocaleString():"N/A"}`),i.PasswordLastUsed)a(` Password Last Used: ${new Date(i.PasswordLastUsed).toLocaleString()}`);if(o.Groups&&o.Groups.length>0){a(`
603
+ Groups:`);for(let l of o.Groups)a(` - ${l.GroupName}`)}if(s.AttachedPolicies&&s.AttachedPolicies.length>0){a(`
604
+ Attached Policies:`);for(let l of s.AttachedPolicies)a(` - ${l.PolicyName}`)}if(c.AccessKeyMetadata&&c.AccessKeyMetadata.length>0){a(`
605
+ Access Keys:`);for(let l of c.AccessKeyMetadata)if(a(` - ${l.AccessKeyId}: ${l.Status}`),l.CreateDate)a(` Created: ${new Date(l.CreateDate).toLocaleString()}`)}if(i.Tags&&i.Tags.length>0){a(`
606
+ Tags:`);for(let l of i.Tags)a(` ${l.Key}: ${l.Value}`)}}catch(t){p(`Failed to get user: ${t.message}`),process.exit(1)}}),r.command("iam:groups","List IAM groups").option("--path <path>","Filter by path prefix",{default:"/"}).action(async(e)=>{w("IAM Groups");try{let t=new he,n=new C("Fetching groups...");n.start();let o=(await t.listGroups({PathPrefix:e.path})).Groups||[];if(n.succeed(`Found ${o.length} group(s)`),o.length===0){a("No IAM groups found");return}x(["Group Name","Group ID","Path","Created"],o.map((s)=>[s.GroupName||"N/A",s.GroupId||"N/A",s.Path||"/",s.CreateDate?new Date(s.CreateDate).toLocaleDateString():"N/A"]))}catch(t){p(`Failed to list groups: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("Simulate IAM Policy");try{let n=new he,i=t.action||await Z("Action to simulate","s3:GetObject"),o=t.resource||await Z("Resource ARN","*");a(`
607
+ Policy: ${e}`),a(`Action: ${i}`),a(`Resource: ${o}`);let s=new C("Running simulation...");s.start();let c=await n.simulatePrincipalPolicy({PolicySourceArn:e,ActionNames:[i],ResourceArns:[o]});if(s.succeed("Simulation complete"),c.EvaluationResults&&c.EvaluationResults.length>0)for(let l of c.EvaluationResults){let d=l.EvalDecision;if(a(`
608
+ Result for ${l.EvalActionName}:`),d==="allowed")g(" Decision: ALLOWED");else if(d==="implicitDeny")I(" Decision: IMPLICIT DENY (no matching allow statement)");else p(" Decision: EXPLICIT DENY");if(l.MatchedStatements&&l.MatchedStatements.length>0){a(" Matched Statements:");for(let u of l.MatchedStatements)a(` - ${u.SourcePolicyId}: ${u.SourcePolicyType}`)}}}catch(n){p(`Failed to simulate policy: ${n.message}`),process.exit(1)}}),r.command("iam:whoami","Show current IAM identity").action(async()=>{w("Current IAM Identity");try{let e=new he,t=new C("Fetching identity...");t.start();let{STSClient:n}=await import("./chunk-12210qvj.js"),o=await new n("us-east-1").getCallerIdentity();t.succeed("Identity loaded"),a(`
609
+ Caller Identity:`),a(` Account: ${o.Account}`),a(` ARN: ${o.Arn}`),a(` User ID: ${o.UserId}`);let s=o.Arn||"";if(s.includes(":user/"))a(" Type: IAM User");else if(s.includes(":assumed-role/"))a(" Type: Assumed Role");else if(s.includes(":root"))I(" Type: Root Account"),I(`
610
+ Warning: Using root credentials is not recommended!`)}catch(e){p(`Failed to get identity: ${e.message}`),process.exit(1)}})}async function St(r){let{AWSClient:e}=await import("./chunk-5rv31chh.js");class t{client;region;constructor(n){this.region=n,this.client=new e}async jsonRpcRequest(n,i){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.${n}`},body:JSON.stringify(i)})}async describeTrails(){return this.jsonRpcRequest("DescribeTrails",{})}async getTrailStatus(n){return this.jsonRpcRequest("GetTrailStatus",{Name:n})}async lookupEvents(n){return this.jsonRpcRequest("LookupEvents",{...n,StartTime:n.StartTime?.toISOString(),EndTime:n.EndTime?.toISOString()})}async getEventSelectors(n){return this.jsonRpcRequest("GetEventSelectors",{TrailName:n})}async createTrail(n){return this.jsonRpcRequest("CreateTrail",n)}async startLogging(n){return this.jsonRpcRequest("StartLogging",{Name:n})}async stopLogging(n){return this.jsonRpcRequest("StopLogging",{Name:n})}async deleteTrail(n){return this.jsonRpcRequest("DeleteTrail",{Name:n})}}return new t(r)}function Xo(r){r.command("audit:trails","List CloudTrail trails").option("--region <region>","AWS region").action(async(e)=>{w("CloudTrail Trails");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=await St(n),o=new C("Fetching trails...");o.start();let c=(await i.describeTrails()).trailList||[];if(o.succeed(`Found ${c.length} trail(s)`),c.length===0){a("No CloudTrail trails found"),a("Use `cloud audit:create` to create a new trail");return}x(["Name","Multi-Region","S3 Bucket","Log Validation","Home Region"],c.map((l)=>[l.Name||"N/A",l.IsMultiRegionTrail?"Yes":"No",l.S3BucketName||"N/A",l.LogFileValidationEnabled?"Yes":"No",l.HomeRegion||"N/A"]))}catch(t){p(`Failed to list trails: ${t.message}`),process.exit(1)}}),r.command("audit:trail <trailName>","Show CloudTrail trail details").option("--region <region>","AWS region").action(async(e,t)=>{w(`CloudTrail: ${e}`);try{let n=await P(),i=t.region||n.project.region||"us-east-1",o=await St(i),s=new C("Fetching trail details...");s.start();let[c,l]=await Promise.all([o.describeTrails(),o.getTrailStatus(e)]),d=(c.trailList||[]).find((u)=>u.Name===e);if(!d){s.fail("Trail not found");return}if(s.succeed("Trail details loaded"),a(`
611
+ Trail Configuration:`),a(` Name: ${d.Name}`),a(` ARN: ${d.TrailARN}`),a(` Home Region: ${d.HomeRegion}`),a(` Multi-Region: ${d.IsMultiRegionTrail?"Yes":"No"}`),a(` Organization Trail: ${d.IsOrganizationTrail?"Yes":"No"}`),a(`
612
+ Storage:`),a(` S3 Bucket: ${d.S3BucketName}`),d.S3KeyPrefix)a(` S3 Prefix: ${d.S3KeyPrefix}`);if(a(` Log Validation: ${d.LogFileValidationEnabled?"Enabled":"Disabled"}`),d.CloudWatchLogsLogGroupArn)a(`
613
+ CloudWatch Logs:`),a(` Log Group: ${d.CloudWatchLogsLogGroupArn}`);if(d.KMSKeyId)a(`
614
+ Encryption:`),a(` KMS Key: ${d.KMSKeyId}`);if(a(`
615
+ Status:`),a(` Logging: ${l.IsLogging?"Active":"Stopped"}`),l.LatestDeliveryTime)a(` Latest Delivery: ${new Date(l.LatestDeliveryTime).toLocaleString()}`);if(l.LatestDeliveryError)I(` Latest Error: ${l.LatestDeliveryError}`);try{let u=await o.getEventSelectors(e);if(u.EventSelectors&&u.EventSelectors.length>0){a(`
616
+ Event Selectors:`);for(let m of u.EventSelectors)if(a(` - Read/Write: ${m.ReadWriteType}`),a(` Management Events: ${m.IncludeManagementEvents?"Yes":"No"}`),m.DataResources&&m.DataResources.length>0)a(` Data Resources: ${m.DataResources.length} configured`)}}catch{}}catch(n){p(`Failed to get trail: ${n.message}`),process.exit(1)}}),r.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(e)=>{w("CloudTrail Events");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=await St(n),o=new C("Looking up events...");o.start();let s=new Date,l={StartTime:new Date(s.getTime()-Number.parseInt(e.hours)*60*60*1000),EndTime:s,MaxResults:Number.parseInt(e.limit)};if(e.user)l.LookupAttributes=[{AttributeKey:"Username",AttributeValue:e.user}];else if(e.event)l.LookupAttributes=[{AttributeKey:"EventName",AttributeValue:e.event}];else if(e.resource)l.LookupAttributes=[{AttributeKey:"ResourceName",AttributeValue:e.resource}];let u=(await i.lookupEvents(l)).Events||[];if(o.succeed(`Found ${u.length} event(s)`),u.length===0){a("No events found matching criteria");return}x(["Time","Event","User","Source IP","Resources"],u.map((m)=>[m.EventTime?new Date(m.EventTime).toLocaleString():"N/A",m.EventName||"N/A",m.Username||"N/A",m.SourceIPAddress||"N/A",(m.Resources||[]).map((f)=>f.ResourceName).join(", ").substring(0,30)||"-"]))}catch(t){p(`Failed to lookup events: ${t.message}`),process.exit(1)}}),r.command("audit:event <eventId>","Show CloudTrail event details").option("--region <region>","AWS region").action(async(e,t)=>{w(`Event: ${e}`);try{let n=await P(),i=t.region||n.project.region||"us-east-1",o=await St(i),s=new C("Fetching event...");s.start();let l=(await o.lookupEvents({LookupAttributes:[{AttributeKey:"EventId",AttributeValue:e}],MaxResults:1})).Events?.[0];if(!l){s.fail("Event not found");return}if(s.succeed("Event loaded"),a(`
617
+ Event Information:`),a(` Event ID: ${l.EventId}`),a(` Event Name: ${l.EventName}`),a(` Event Time: ${l.EventTime?new Date(l.EventTime).toLocaleString():"N/A"}`),a(` Event Source: ${l.EventSource}`),a(` Username: ${l.Username}`),a(` Source IP: ${l.SourceIPAddress}`),a(` Access Key: ${l.AccessKeyId||"N/A"}`),l.Resources&&l.Resources.length>0){a(`
618
+ Resources:`);for(let d of l.Resources)a(` - ${d.ResourceType}: ${d.ResourceName}`)}if(l.CloudTrailEvent){a(`
619
+ Full Event Record:`);let d=JSON.parse(l.CloudTrailEvent);console.log(JSON.stringify(d,null,2))}}catch(n){p(`Failed to get event: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create CloudTrail Trail");try{if(!t.bucket){p("--bucket is required");return}let n=await St(t.region);if(a(`Trail Name: ${e}`),a(`S3 Bucket: ${t.bucket}`),a(`Multi-Region: ${t.multiRegion?"Yes":"No"}`),a(`Log Validation: ${t.validation?"Yes":"No"}`),a(`Global Events: ${t.globalEvents?"Yes":"No"}`),!await O(`
620
+ Create this trail?`,!0)){a("Operation cancelled");return}let o=new C("Creating trail...");o.start();let s=await n.createTrail({Name:e,S3BucketName:t.bucket,S3KeyPrefix:t.prefix,IsMultiRegionTrail:t.multiRegion,EnableLogFileValidation:t.validation,IncludeGlobalServiceEvents:t.globalEvents??!0});o.text="Starting logging...",await n.startLogging(e),o.succeed("Trail created and logging started"),g(`
621
+ Trail ARN: ${s.TrailARN}`),a(`S3 Bucket: ${s.S3BucketName}`)}catch(n){p(`Failed to create trail: ${n.message}`),process.exit(1)}}),r.command("audit:start <trailName>","Start CloudTrail logging").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Start CloudTrail Logging");try{let n=await St(t.region),i=new C("Starting logging...");i.start(),await n.startLogging(e),i.succeed("Logging started")}catch(n){p(`Failed to start logging: ${n.message}`),process.exit(1)}}),r.command("audit:stop <trailName>","Stop CloudTrail logging").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Stop CloudTrail Logging");try{let n=await St(t.region);if(I(`This will stop logging for trail: ${e}`),!await O(`
622
+ Stop logging?`,!1)){a("Operation cancelled");return}let o=new C("Stopping logging...");o.start(),await n.stopLogging(e),o.succeed("Logging stopped")}catch(n){p(`Failed to stop logging: ${n.message}`),process.exit(1)}}),r.command("audit:delete <trailName>","Delete a CloudTrail trail").option("--region <region>","AWS region",{default:"us-east-1"}).action(async(e,t)=>{w("Delete CloudTrail Trail");try{let n=await St(t.region);if(I(`This will permanently delete trail: ${e}`),!await O(`
623
+ Delete this trail?`,!1)){a("Operation cancelled");return}let o=new C("Deleting trail...");o.start(),await n.deleteTrail(e),o.succeed("Trail deleted")}catch(n){p(`Failed to delete trail: ${n.message}`),process.exit(1)}})}function es(r){r.command("status","Show overall infrastructure health dashboard").option("--region <region>","AWS region").action(async(e)=>{w("Infrastructure Status Dashboard");try{let t=await P(),n=e.region||t.project.region||"us-east-1";a(`Region: ${n}`),a(`Project: ${t.project.name||"Unknown"}`),a("");let i=[],o=new C("Checking EC2 instances...");o.start();try{let{EC2Client:y}=await import("./chunk-3r3be05z.js"),R=await new y(n).describeInstances({Filters:[{Name:"instance-state-name",Values:["running","pending","stopping","stopped"]}]}),b=0,A=0,k=0;for(let T of R.Reservations||[])for(let D of T.Instances||[])if(k++,D.State?.Name==="running")b++;else if(D.State?.Name==="stopped")A++;o.succeed("EC2 instances checked"),i.push({name:"EC2 Instances",status:b>0?"OK":k>0?"WARN":"INFO",details:`${b} running, ${A} stopped, ${k} total`})}catch(y){o.fail("EC2 check failed"),i.push({name:"EC2 Instances",status:"ERROR",details:y.message})}let s=new C("Checking RDS instances...");s.start();try{let{RDSClient:y}=await import("./chunk-tp4c2mv1.js"),b=(await new y(n).describeDBInstances()).DBInstances||[],A=b.filter((k)=>k.DBInstanceStatus==="available").length;s.succeed("RDS instances checked"),i.push({name:"RDS Databases",status:b.length>0&&A===b.length?"OK":b.length>0?"WARN":"INFO",details:`${A} available, ${b.length} total`})}catch(y){s.fail("RDS check failed"),i.push({name:"RDS Databases",status:"ERROR",details:y.message})}let c=new C("Checking Lambda functions...");c.start();try{let{LambdaClient:y}=await import("./chunk-ds1z880f.js"),b=(await new y(n).listFunctions()).Functions||[];c.succeed("Lambda functions checked"),i.push({name:"Lambda Functions",status:"OK",details:`${b.length} function(s)`})}catch(y){c.fail("Lambda check failed"),i.push({name:"Lambda Functions",status:"ERROR",details:y.message})}let l=new C("Checking S3 buckets...");l.start();try{let{S3Client:y}=await import("./chunk-w5d6tr2x.js"),b=(await new y(n).listBuckets()).Buckets||[];l.succeed("S3 buckets checked"),i.push({name:"S3 Buckets",status:"OK",details:`${b.length} bucket(s)`})}catch(y){l.fail("S3 check failed"),i.push({name:"S3 Buckets",status:"ERROR",details:y.message})}let d=new C("Checking CloudFront distributions...");d.start();try{let{CloudFrontClient:y}=await import("./chunk-xv4bscrm.js"),R=await new y().listDistributions(),b=R.filter((A)=>A.Status==="Deployed").length;d.succeed("CloudFront checked"),i.push({name:"CloudFront",status:R.length>0&&b===R.length?"OK":R.length>0?"WARN":"INFO",details:`${b} deployed, ${R.length} total`})}catch(y){d.fail("CloudFront check failed"),i.push({name:"CloudFront",status:"ERROR",details:y.message})}let u=new C("Checking SQS queues...");u.start();try{let{SQSClient:y}=await import("./chunk-y9eyc5pw.js"),b=(await new y(n).listQueues()).QueueUrls||[];u.succeed("SQS queues checked"),i.push({name:"SQS Queues",status:"OK",details:`${b.length} queue(s)`})}catch(y){u.fail("SQS check failed"),i.push({name:"SQS Queues",status:"ERROR",details:y.message})}let m=new C("Checking CloudFormation stacks...");m.start();try{let{CloudFormationClient:y}=await import("./chunk-dhhz4zyj.js"),b=(await new y(n).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE","CREATE_IN_PROGRESS","UPDATE_IN_PROGRESS","ROLLBACK_COMPLETE","UPDATE_ROLLBACK_COMPLETE"])).StackSummaries||[],A=b.filter((D)=>D.StackStatus==="CREATE_COMPLETE"||D.StackStatus==="UPDATE_COMPLETE").length,k=b.filter((D)=>D.StackStatus?.includes("IN_PROGRESS")).length,T=b.filter((D)=>D.StackStatus?.includes("ROLLBACK")).length;m.succeed("CloudFormation checked"),i.push({name:"CloudFormation",status:T>0?"WARN":k>0?"INFO":"OK",details:`${A} healthy, ${k} in progress, ${T} rolled back`})}catch(y){m.fail("CloudFormation check failed"),i.push({name:"CloudFormation",status:"ERROR",details:y.message})}let f=new C("Checking SSL certificates...");f.start();try{let{ACMClient:y}=await import("./chunk-jjjts1vt.js"),v=new y("us-east-1"),b=(await v.listCertificates()).CertificateSummaryList||[],A=await Promise.all(b.map((E)=>v.describeCertificate({CertificateArn:E.CertificateArn}))),k=A.filter((E)=>E.Status==="ISSUED").length,T=A.filter((E)=>E.Status==="PENDING_VALIDATION").length,D=A.filter((E)=>{if(E.NotAfter)return(new Date(E.NotAfter).getTime()-Date.now())/86400000<30;return!1}).length;f.succeed("SSL certificates checked"),i.push({name:"SSL Certificates",status:D>0?"WARN":T>0?"INFO":"OK",details:`${k} issued, ${T} pending${D>0?`, ${D} expiring soon`:""}`})}catch(y){f.fail("ACM check failed"),i.push({name:"SSL Certificates",status:"ERROR",details:y.message})}a(`
624
+ `+"=".repeat(60)),a("HEALTH SUMMARY"),a("=".repeat(60)+`
625
+ `);for(let y of i){let v="";if(y.status==="OK")v=`${lt.green}[OK]${lt.reset}`;else if(y.status==="WARN")v=`${lt.yellow}[WARN]${lt.reset}`;else if(y.status==="ERROR")v=`${lt.red}[ERROR]${lt.reset}`;else v=`${lt.blue}[INFO]${lt.reset}`;console.log(`${v} ${y.name.padEnd(20)} ${y.details}`)}let h=i.some((y)=>y.status==="ERROR"),S=i.some((y)=>y.status==="WARN");if(a(""),h)p("Some services have errors. Check the details above.");else if(S)I("Some services need attention. Check the warnings above.");else g("All services are healthy!")}catch(t){p(`Failed to get status: ${t.message}`),process.exit(1)}}),r.command("status:costs","Show current month cost summary").option("--region <region>","AWS region").action(async(e)=>{w("Cost Summary");try{a("Fetching cost data from AWS Cost Explorer..."),a("");let t=new Date,n=new Date(t.getFullYear(),t.getMonth(),1),i=new Date(t.getFullYear(),t.getMonth()+1,0);a(`Period: ${n.toISOString().split("T")[0]} to ${i.toISOString().split("T")[0]}`),a(""),a("Note: Cost data requires AWS Cost Explorer API access."),a("Run `cloud cost` for detailed cost analysis.")}catch(t){p(`Failed to get cost summary: ${t.message}`),process.exit(1)}}),r.command("status:alarms","Show CloudWatch alarm status").option("--region <region>","AWS region").action(async(e)=>{w("CloudWatch Alarms");try{let t=await P(),n=e.region||t.project.region||"us-east-1",{AWSClient:i}=await import("./chunk-5rv31chh.js");class o{client;region;constructor(h){this.region=h,this.client=new i}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 s=new o(n),c=new C("Fetching alarms...");c.start();let d=(await s.describeAlarms()).MetricAlarms||[];if(c.succeed(`Found ${d.length} alarm(s)`),d.length===0){a("No CloudWatch alarms configured");return}let u=d.filter((h)=>h.StateValue==="ALARM"),m=d.filter((h)=>h.StateValue==="OK"),f=d.filter((h)=>h.StateValue==="INSUFFICIENT_DATA");if(a(""),a(`Alarming: ${u.length}`),a(`OK: ${m.length}`),a(`Insufficient Data: ${f.length}`),u.length>0){a(`
626
+ Alarms in ALARM state:`);for(let h of u)p(` - ${h.AlarmName}: ${h.AlarmDescription||"No description"}`)}}catch(t){p(`Failed to get alarms: ${t.message}`),process.exit(1)}})}async function Le(r){let{AWSClient:e}=await import("./chunk-5rv31chh.js");class t{client;region;constructor(n){this.region=n,this.client=new e}async jsonRpcRequest(n,i){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.${n}`},body:JSON.stringify(i)})}async listBackupVaults(){return this.jsonRpcRequest("ListBackupVaults",{})}async listBackupPlans(){return this.jsonRpcRequest("ListBackupPlans",{})}async listRecoveryPointsByBackupVault(n,i){return this.jsonRpcRequest("ListRecoveryPointsByBackupVault",{BackupVaultName:n,...i})}async describeBackupVault(n){return this.jsonRpcRequest("DescribeBackupVault",{BackupVaultName:n})}async getBackupPlan(n){return this.jsonRpcRequest("GetBackupPlan",{BackupPlanId:n})}async createBackupVault(n,i){return this.jsonRpcRequest("CreateBackupVault",{BackupVaultName:n,...i})}async deleteBackupVault(n){return this.jsonRpcRequest("DeleteBackupVault",{BackupVaultName:n})}async createBackupPlan(n){return this.jsonRpcRequest("CreateBackupPlan",{BackupPlan:n})}async deleteBackupPlan(n){return this.jsonRpcRequest("DeleteBackupPlan",{BackupPlanId:n})}async startBackupJob(n){return this.jsonRpcRequest("StartBackupJob",n)}async startRestoreJob(n){return this.jsonRpcRequest("StartRestoreJob",n)}async describeBackupJob(n){return this.jsonRpcRequest("DescribeBackupJob",{BackupJobId:n})}async describeRestoreJob(n){return this.jsonRpcRequest("DescribeRestoreJob",{RestoreJobId:n})}async listBackupJobs(n){return this.jsonRpcRequest("ListBackupJobs",n||{})}async listRestoreJobs(n){return this.jsonRpcRequest("ListRestoreJobs",n||{})}async createBackupSelection(n,i){return this.jsonRpcRequest("CreateBackupSelection",{BackupPlanId:n,BackupSelection:i})}async listBackupSelections(n){return this.jsonRpcRequest("ListBackupSelections",{BackupPlanId:n})}}return new t(r)}function ts(r){r.command("backup:vaults","List backup vaults").option("--region <region>","AWS region").action(async(e)=>{w("Backup Vaults");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=await Le(n),o=new C("Fetching vaults...");o.start();let c=(await i.listBackupVaults()).BackupVaultList||[];if(o.succeed(`Found ${c.length} vault(s)`),c.length===0){a("No backup vaults found"),a("Use `cloud backup:create-vault` to create a new vault");return}x(["Vault Name","Recovery Points","Created","Encrypted"],c.map((l)=>[l.BackupVaultName||"N/A",(l.NumberOfRecoveryPoints||0).toString(),l.CreationDate?new Date(l.CreationDate).toLocaleDateString():"N/A",l.EncryptionKeyArn?"Yes":"Default"]))}catch(t){p(`Failed to list vaults: ${t.message}`),process.exit(1)}}),r.command("backup:list","List backup plans").option("--region <region>","AWS region").action(async(e)=>{w("Backup Plans");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=await Le(n),o=new C("Fetching backup plans...");o.start();let c=(await i.listBackupPlans()).BackupPlansList||[];if(o.succeed(`Found ${c.length} backup plan(s)`),c.length===0){a("No backup plans found"),a("Use `cloud backup:create` to create a backup plan");return}x(["Plan Name","Plan ID","Version","Created"],c.map((l)=>[l.BackupPlanName||"N/A",l.BackupPlanId||"N/A",l.VersionId?.substring(0,8)||"N/A",l.CreationDate?new Date(l.CreationDate).toLocaleDateString():"N/A"]))}catch(t){p(`Failed to list backup plans: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w(`Recovery Points: ${e}`);try{let n=await P(),i=t.region||n.project.region||"us-east-1",o=await Le(i),s=new C("Fetching recovery points...");s.start();let l=(await o.listRecoveryPointsByBackupVault(e,{MaxResults:Number.parseInt(t.limit)})).RecoveryPoints||[];if(s.succeed(`Found ${l.length} recovery point(s)`),l.length===0){a("No recovery points found");return}x(["Resource","Status","Created","Size","Lifecycle"],l.map((d)=>[d.ResourceArn?.split(":").pop()||"N/A",d.Status||"N/A",d.CreationDate?new Date(d.CreationDate).toLocaleString():"N/A",d.BackupSizeInBytes?Zl(d.BackupSizeInBytes):"N/A",d.Lifecycle?.DeleteAfterDays?`${d.Lifecycle.DeleteAfterDays} days`:"Indefinite"]))}catch(n){p(`Failed to list recovery points: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create Backup Vault");try{let n=await Le(t.region);if(a(`Vault Name: ${e}`),a(`Region: ${t.region}`),a(`Encryption: ${t.kmsKey?"Custom KMS":"AWS Managed"}`),!await O(`
627
+ Create this vault?`,!0)){a("Operation cancelled");return}let o=new C("Creating vault...");o.start(),await n.createBackupVault(e,{EncryptionKeyArn:t.kmsKey}),o.succeed("Vault created"),g(`
628
+ Vault: ${e}`)}catch(n){p(`Failed to create vault: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Create Backup Plan");try{let n=await Le(t.region);if(a(`Plan Name: ${e}`),a(`Vault: ${t.vault}`),a(`Schedule: ${t.schedule}`),a(`Retention: ${t.retention} days`),!await O(`
629
+ Create this backup plan?`,!0)){a("Operation cancelled");return}let o=new C("Creating backup plan...");o.start();let s={DeleteAfterDays:Number.parseInt(t.retention)};if(t.lifecycleCold)s.MoveToColdStorageAfterDays=Number.parseInt(t.lifecycleCold);let c=await n.createBackupPlan({BackupPlanName:e,Rules:[{RuleName:`${e}-daily`,TargetBackupVaultName:t.vault,ScheduleExpression:t.schedule,StartWindowMinutes:60,CompletionWindowMinutes:180,Lifecycle:s}]});o.succeed("Backup plan created"),g(`
630
+ Plan ID: ${c.BackupPlanId}`),a("\nNote: Add resource selections with `cloud backup:add-selection`")}catch(n){p(`Failed to create backup plan: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Add Backup Selection");try{if(!t.role){p("--role is required (IAM role ARN for AWS Backup)");return}if(!t.resource&&!t.tagKey){p("Specify --resource or --tag-key/--tag-value");return}let n=await Le(t.region),o={SelectionName:t.name||`selection-${Date.now()}`,IamRoleArn:t.role};if(t.resource)o.Resources=[t.resource],a(`Resource: ${t.resource}`);if(t.tagKey&&t.tagValue)o.ListOfTags=[{ConditionType:"STRINGEQUALS",ConditionKey:t.tagKey,ConditionValue:t.tagValue}],a(`Tag: ${t.tagKey}=${t.tagValue}`);if(!await O(`
631
+ Add this selection?`,!0)){a("Operation cancelled");return}let c=new C("Adding selection...");c.start();let l=await n.createBackupSelection(e,o);c.succeed("Selection added"),g(`
632
+ Selection ID: ${l.SelectionId}`)}catch(n){p(`Failed to add selection: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Start Backup Job");try{if(!t.role){p("--role is required (IAM role ARN for AWS Backup)");return}let n=await Le(t.region);if(a(`Resource: ${e}`),a(`Vault: ${t.vault}`),!await O(`
633
+ Start backup?`,!0)){a("Operation cancelled");return}let o=new C("Starting backup job...");o.start();let s=await n.startBackupJob({BackupVaultName:t.vault,ResourceArn:e,IamRoleArn:t.role,IdempotencyToken:`cli-${Date.now()}`});o.succeed("Backup job started"),g(`
634
+ Job ID: ${s.BackupJobId}`),a("Use `cloud backup:jobs` to check status")}catch(n){p(`Failed to start backup: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Start Restore Job");try{if(!t.role){p("--role is required (IAM role ARN for AWS Backup)");return}let n=await Le(t.region);a(`Recovery Point: ${e}`);let i={};if(t.metadata)i=JSON.parse(t.metadata);if(I(`
635
+ Restore will create new resources. Review carefully.`),!await O(`
636
+ Start restore?`,!1)){a("Operation cancelled");return}let s=new C("Starting restore job...");s.start();let c=await n.startRestoreJob({RecoveryPointArn:e,IamRoleArn:t.role,Metadata:i,IdempotencyToken:`cli-${Date.now()}`});s.succeed("Restore job started"),g(`
637
+ Job ID: ${c.RestoreJobId}`),a("Use `cloud backup:restore-jobs` to check status")}catch(n){p(`Failed to start restore: ${n.message}`),process.exit(1)}}),r.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(e)=>{w("Backup Jobs");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=await Le(n),o=new C("Fetching backup jobs...");o.start();let c=(await i.listBackupJobs({ByState:e.state,MaxResults:50})).BackupJobs||[];if(o.succeed(`Found ${c.length} job(s)`),c.length===0){a("No backup jobs found");return}x(["Job ID","Resource","State","Started","Size"],c.map((l)=>[l.BackupJobId?.substring(0,16)||"N/A",l.ResourceArn?.split(":").pop()||"N/A",l.State||"N/A",l.CreationDate?new Date(l.CreationDate).toLocaleString():"N/A",l.BackupSizeInBytes?Zl(l.BackupSizeInBytes):"N/A"]))}catch(t){p(`Failed to list backup jobs: ${t.message}`),process.exit(1)}}),r.command("backup:restore-jobs","List recent restore jobs").option("--region <region>","AWS region").option("--status <status>","Filter by status").action(async(e)=>{w("Restore Jobs");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=await Le(n),o=new C("Fetching restore jobs...");o.start();let c=(await i.listRestoreJobs({ByStatus:e.status,MaxResults:50})).RestoreJobs||[];if(o.succeed(`Found ${c.length} job(s)`),c.length===0){a("No restore jobs found");return}x(["Job ID","Resource","Status","Started","Completed"],c.map((l)=>[l.RestoreJobId?.substring(0,16)||"N/A",l.CreatedResourceArn?.split(":").pop()||"Pending",l.Status||"N/A",l.CreationDate?new Date(l.CreationDate).toLocaleString():"N/A",l.CompletionDate?new Date(l.CompletionDate).toLocaleString():"-"]))}catch(t){p(`Failed to list restore jobs: ${t.message}`),process.exit(1)}}),r.command("backup:schedule","Show backup schedule overview").option("--region <region>","AWS region").action(async(e)=>{w("Backup Schedule Overview");try{let t=await P(),n=e.region||t.project.region||"us-east-1",i=await Le(n),o=new C("Fetching backup plans...");o.start();let c=(await i.listBackupPlans()).BackupPlansList||[];if(o.succeed(`Found ${c.length} backup plan(s)`),c.length===0){a("No backup plans configured");return}for(let l of c){a(`
638
+ ${l.BackupPlanName}`),a("=".repeat(40));try{let u=(await i.getBackupPlan(l.BackupPlanId)).BackupPlan?.Rules||[];for(let f of u)if(a(` Rule: ${f.RuleName}`),a(` Schedule: ${f.ScheduleExpression}`),a(` Vault: ${f.TargetBackupVaultName}`),f.Lifecycle?.DeleteAfterDays)a(` Retention: ${f.Lifecycle.DeleteAfterDays} days`);let m=await i.listBackupSelections(l.BackupPlanId);if(m.BackupSelectionsList&&m.BackupSelectionsList.length>0)a(` Selections: ${m.BackupSelectionsList.length}`)}catch{a(" (Unable to load plan details)")}}}catch(t){p(`Failed to get schedule: ${t.message}`),process.exit(1)}})}function Zl(r){if(r===0)return"0 B";let e=1024,t=["B","KB","MB","GB","TB"],n=Math.floor(Math.log(r)/Math.log(e));return`${Number.parseFloat((r/e**n).toFixed(2))} ${t[n]}`}async function lf(r){let e=await P(),t=q(process.cwd(),e),n=r??Object.keys(e.environments??{})[0]??"production",i=t.environments.get(n);if(!i)throw t.store.close(),Error(`Environment ${n} was not found.`);let o=new We(t.store,{workerId:`cli:${process.pid}`}),s=new Xl(t.store),c=new td(s,o),l=new Gn(t.store),d=new Kn(t.store,le(process.cwd())),u=t.store.getActorByExternalId("system","cli")??t.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:e,controlPlane:t,env:n,environmentRecord:i,queue:o,store:s,coordinator:c,dataServices:l,secrets:d,actor:u}}async function Ye(r,e){let t=await lf(r);try{return await e(t)}finally{t.controlPlane.store.close()}}async function Ze(r){try{await r()}catch(e){p(e instanceof Error?e.message:String(e))}}function dt(r,e,t,n){r.controlPlane.store.appendEvent({organizationId:r.controlPlane.organization.id,projectId:r.controlPlane.project.id,resourceId:n,actorId:r.actor.id,type:`backup.${e}`,payload:t})}function df(r,e){let t=r.store.getPolicy(e)??r.store.listPolicies(r.controlPlane.project.id,r.environmentRecord.id).find((n)=>n.name===e);if(!t||t.environmentId!==r.environmentRecord.id)throw Error(`Backup policy ${e} was not found in ${r.env}.`);return t}function hr(r,e){let t=r.store.getRecoveryPoint(e),n=r.store.listRecoveryPoints(r.controlPlane.project.id).filter((o)=>o.id.startsWith(e)),i=t??(n.length===1?n[0]:void 0);if(!i)throw Error(n.length>1?`Recovery point prefix ${e} is ambiguous.`:`Recovery point ${e} was not found.`);return i}function Ql(r,e){let t=r.store.getDestination(e)??r.store.listDestinations(r.controlPlane.project.id).find((n)=>n.name===e);if(!t)throw Error(`Backup destination ${e} was not found.`);return t}function uf(r,e){return r.kind==="volume"?{volumeName:e}:r.kind==="logical_database"?{targetId:e,dataServiceId:e}:{targetId:e}}function mf(r,e=[]){let t=new Map(e.map((n)=>[n.id,n.name]));return r.map((n)=>[n.id,n.status,n.verificationState,n.kind,t.get(n.policyId??"")??"manual",n.pointInTime,n.sizeBytes?`${n.sizeBytes} B`:"provider",n.held?"hold":n.pinned?"pinned":"retention"])}function ns(r){r.command("recovery:list","Show backup coverage, destination health, and recent recovery points").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>Ze(async()=>Ye(e.env,async(t)=>{let n=t.store.listPolicies(t.controlPlane.project.id,t.environmentRecord.id),i=new Set(n.map((c)=>c.id)),o=t.store.listRecoveryPoints(t.controlPlane.project.id).filter((c)=>!c.policyId||i.has(c.policyId)),s={coverage:t.store.coverage(t.controlPlane.project.id).filter((c)=>i.has(c.policy.id)),destinations:t.store.listDestinations(t.controlPlane.project.id).map((c)=>({...c,credentialRef:void 0,encryptionKeyRef:void 0,credentialsConfigured:!!c.credentialRef,clientEncryptionConfigured:!!c.encryptionKeyRef})),policies:n,recoveryPoints:o,jobs:t.store.listJobs(t.controlPlane.project.id).filter((c)=>!c.policyId||i.has(c.policyId))};if(e.json)a(JSON.stringify(s,null,2));else x(["Policy","RPO","Destination","Latest point","Verification"],s.coverage.map((c)=>[c.policy.name,c.missedRpo?"MISSED":"protected",c.destinationHealthy?"healthy":"attention",c.lastRecoveryPoint?.pointInTime??"none",c.lastRecoveryPoint?.verificationState??"none"])),x(["ID","State","Verification","Kind","Policy","Point in time","Size","Protection"],mf(o,n))}))),r.command("recovery:jobs","List durable backup, verification, restore, drill, and cleanup jobs").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>Ze(async()=>Ye(e.env,async(t)=>{let n=new Set(t.store.listPolicies(t.controlPlane.project.id,t.environmentRecord.id).map((o)=>o.id)),i=t.store.listJobs(t.controlPlane.project.id).filter((o)=>!o.policyId||n.has(o.policyId));if(e.json)a(JSON.stringify(i,null,2));else x(["ID","Kind","State","Phase","Operation","Started","Finished","Error"],i.map((o)=>[o.id,o.kind,o.status,String(o.progress.phase??"queued"),o.operationId??"—",o.startedAt??"—",o.finishedAt??"—",o.error??"—"]))}))),r.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(e,t)=>Ze(async()=>Ye(t.env,async(n)=>{if(!["aws_s3","s3_compatible","aws_backup"].includes(t.provider??""))throw Error("--provider must be aws_s3, s3_compatible, or aws_backup.");if(!["provider","client_side","both"].includes(t.encryption??""))throw Error("--encryption must be provider, client_side, or both.");let i=`secret://data-services/backups/${n.controlPlane.project.id}/${e}`,o=[];try{let s,c;if(t.credentialsEnv){let d=process.env[t.credentialsEnv];if(!d)throw Error(`${t.credentialsEnv} is empty.`);s=`${i}/credentials`,await n.secrets.put(s,d),o.push(s)}if(t.encryption!=="provider"){if(!t.encryptionKeyEnv)throw Error("Client encryption requires --encryption-key-env.");let d=process.env[t.encryptionKeyEnv];if(!d||d.length<32)throw Error(`${t.encryptionKeyEnv} must contain at least 32 characters.`);c=`${i}/encryption-key`,await n.secrets.put(c,d),o.push(c)}let l=n.store.createDestination({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,provider:t.provider,endpoint:t.endpoint,endpointPolicy:t.allowPrivate?"allow_private":"public_https",bucket:t.bucket,prefix:t.prefix??"",region:t.region??n.config.project.region,forcePathStyle:!!t.pathStyle,credentialRef:s,encryption:t.encryption,encryptionKeyRef:c,immutability:{objectLock:!!t.lockDays,defaultRetentionDays:t.lockDays?Number(t.lockDays):void 0},status:"untested"});dt(n,"destination_created",{destinationId:l.id,provider:l.provider}),g(`Created destination ${l.name} (${l.id}); test it with cloud recovery:destination:test ${l.name}`)}catch(s){for(let c of o)await n.secrets.remove(c).catch(()=>{});throw s}}))),r.command("recovery:destination:test <destination>","Write, read, verify, and clean a destination health object").option("--env <environment>","Target environment").action(async(e,t)=>Ze(async()=>Ye(t.env,async(n)=>{let i=Ql(n,e);if(i.provider==="aws_backup")throw Error("AWS Backup is verified by creating a provider recovery point.");try{await new ed(n.secrets).test(i),n.store.recordDestinationTest(i.id,{ok:!0}),dt(n,"destination_tested",{destinationId:i.id,ok:!0}),g(`${i.name} passed write, read, checksum, and cleanup checks.`)}catch(o){let s=o instanceof Error?o.message:String(o);throw n.store.recordDestinationTest(i.id,{ok:!1,error:s}),dt(n,"destination_tested",{destinationId:i.id,ok:!1}),o}}))),r.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(e,t)=>Ze(async()=>Ye(t.env,async(n)=>{if(!t.destination)throw Error("--destination is required.");if(!["managed_database","logical_database","volume","files","control_plane","infrastructure"].includes(t.kind??""))throw Error("--kind must be managed_database, logical_database, volume, files, control_plane, or infrastructure.");let i=Ql(n,t.destination),o=n.dataServices.list(n.controlPlane.project.id,n.environmentRecord.id),s=t.dataService?n.dataServices.get(t.dataService)??o.find((f)=>f.name===t.dataService):void 0,c=n.controlPlane.store.listResources(n.controlPlane.project.id,n.environmentRecord.id),l=t.resource?c.find((f)=>f.id===t.resource||f.slug===t.resource):void 0,d=t.include?Array.isArray(t.include)?t.include:[t.include]:[],u=t.exclude?Array.isArray(t.exclude)?t.exclude:[t.exclude]:[];if(["managed_database","logical_database"].includes(t.kind??"")&&!s)throw Error("--data-service must identify a service in this environment.");if(t.kind==="volume"&&(!l||!t.volume))throw Error("Volume policies require --resource and --volume.");if(t.kind==="files"&&(!l||!d.length))throw Error("File policies require --resource and at least one --include path.");if(t.kind==="infrastructure"&&(!d.some((f)=>f.startsWith("resource:arn:"))||!d.some((f)=>f.startsWith("role:arn:"))))throw Error("Infrastructure policies require --include resource:<arn> and --include role:<arn>.");let m=n.store.createPolicy({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,environmentId:n.environmentRecord.id,resourceId:l?.id,dataServiceId:s?.id,destinationId:i.id,name:e,resourceKind:t.kind,schedule:t.schedule??"daily",timezone:t.timezone??"UTC",retention:{keepLast:Number(t.keep)||7,expireAfterDays:Number(t.expire)||30},compression:t.compression,encryption:"destination",includePatterns:t.volume?[t.volume,...d]:d,excludePatterns:u,expectedRpoMinutes:Number(t.rpo)||1440,expectedRtoMinutes:Number(t.rto)||120,enabled:!t.disabled});dt(n,"policy_created",{policyId:m.id,resourceKind:m.resourceKind},m.resourceId),g(`Created policy ${m.name} (${m.id}); next run ${m.nextRunAt}`)}))),r.command("recovery:run <policy>","Queue an on-demand run of a recovery policy").option("--env <environment>","Target environment").action(async(e,t)=>Ze(async()=>Ye(t.env,async(n)=>{let i=df(n,e),o=n.coordinator.enqueueBackup(i,new Date().toISOString(),n.actor.id);dt(n,"run_queued",{policyId:i.id,backupJobId:o.id},i.resourceId),g(`Queued backup job ${o.id}; inspect it with cloud recovery:jobs`)}))),r.command("recovery:verify <point>","Queue independent verification of a recovery point").option("--env <environment>","Target environment").action(async(e,t)=>Ze(async()=>Ye(t.env,async(n)=>{let i=hr(n,e),o=n.coordinator.enqueueVerification(i);dt(n,"verification_queued",{recoveryPointId:i.id,backupJobId:o.id},i.resourceId),g(`Queued verification job ${o.id}`)}))),r.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(e,t,n)=>Ze(async()=>Ye(n.env,async(i)=>{if(!["isolated","in_place"].includes(n.mode??""))throw Error("--mode must be isolated or in_place.");if(n.drill&&n.mode==="in_place")throw Error("Recovery drills require --mode isolated.");let o=hr(i,e),s={mode:n.mode,target:{...uf(o,t),...n.metadata?{metadata:JSON.parse(n.metadata)}:{},inPlace:n.mode==="in_place"},targetName:t,confirm:n.confirm,recentAuth:!0,downtimeAcknowledged:!!n.ackDowntime,safetyBackupId:n.safety?hr(i,n.safety).id:void 0},c=i.coordinator.planRestore(o,s);if(!n.execute){a(JSON.stringify({...c,productionExecutionCreated:!1},null,2));return}let l=i.coordinator.enqueueRestore(o,{...s,drill:!!n.drill,actorId:i.actor.id});dt(i,n.drill?"drill_queued":"restore_queued",{recoveryPointId:o.id,backupJobId:l.id,mode:s.mode,target:t},o.resourceId),g(`Queued ${n.drill?"drill":"restore"} job ${l.id}`)}))),r.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(e,t)=>Ze(async()=>Ye(t.env,async(n)=>{let i=hr(n,e);if(![t.pin,t.unpin,t.hold,t.releaseHold].filter(Boolean).length)throw Error("Choose --pin, --unpin, --hold, or --release-hold.");let o=n.store.updateRecoveryPoint(i.id,{pinned:t.pin?!0:t.unpin?!1:i.pinned,held:t.hold?!0:t.releaseHold?!1:i.held});dt(n,"protection_updated",{recoveryPointId:i.id,pinned:o.pinned,held:o.held},i.resourceId),g(`${o.id}: pinned=${o.pinned} held=${o.held}`)}))),r.command("recovery:retention","Queue deletion of expired, unlocked, unheld recovery points").option("--env <environment>","Target environment").action(async(e)=>Ze(async()=>Ye(e.env,async(t)=>{let n=t.coordinator.enqueueRetention();dt(t,"retention_queued",{jobs:n.length}),g(`Queued ${n.length} retention cleanup job${n.length===1?"":"s"}.`)})))}function rs(r){r.command("api:list","List all API Gateway APIs").option("--region <region>","AWS region").action(async(e)=>{w("API Gateway APIs");try{let t=await P(),n=e.region||t.project.region||"us-east-1",{LambdaClient:i}=await import("./chunk-ds1z880f.js"),o=new i(n),s=new C("Fetching APIs...");s.start();let{CloudFormationClient:c}=await import("./chunk-dhhz4zyj.js"),u=(await new c(n).listStacks(["CREATE_COMPLETE","UPDATE_COMPLETE"])).StackSummaries.filter((m)=>m.StackName?.includes("api")||m.StackName?.includes("Api")||m.StackName?.includes("API"));if(s.succeed("APIs listed"),a(`
639
+ API-related CloudFormation stacks:`),u.length===0)a("No API Gateway stacks found"),a(`
640
+ To create an API, you can:`),a(" 1. Use cloud.config.ts to define your API"),a(" 2. Deploy with `cloud deploy`");else x(["Stack Name","Status","Created"],u.map((m)=>[m.StackName||"N/A",m.StackStatus||"N/A",m.CreationTime?new Date(m.CreationTime).toLocaleDateString():"N/A"]));a("\nTip: Use AWS Console or `aws apigateway get-rest-apis` for detailed API listing")}catch(t){p(`Failed to list APIs: ${t.message}`),process.exit(1)}}),r.command("api:describe <apiId>","Show API Gateway API details").option("--region <region>","AWS region").action(async(e,t)=>{w(`API: ${e}`);try{let n=await P(),i=t.region||n.project.region||"us-east-1";a(`API ID: ${e}`),a(`Region: ${i}`),a(""),a("For detailed API information, use AWS CLI:"),a(` aws apigateway get-rest-api --rest-api-id ${e} --region ${i}`),a(` aws apigateway get-resources --rest-api-id ${e} --region ${i}`),a(` aws apigateway get-stages --rest-api-id ${e} --region ${i}`)}catch(n){p(`Failed to describe API: ${n.message}`),process.exit(1)}}),r.command("api:stages <apiId>","List API stages").option("--region <region>","AWS region").action(async(e,t)=>{w(`API Stages: ${e}`);try{let n=await P(),i=t.region||n.project.region||"us-east-1";a(`API ID: ${e}`),a(`Region: ${i}`),a(""),a("Common stages:"),a(" - prod (production)"),a(" - staging"),a(" - dev (development)"),a(""),a("For detailed stage information, use AWS CLI:"),a(` aws apigateway get-stages --rest-api-id ${e} --region ${i}`)}catch(n){p(`Failed to list stages: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Deploy API");try{if(a(`API ID: ${e}`),a(`Stage: ${t.stage}`),!await O(`
641
+ Deploy to this stage?`,!0)){a("Operation cancelled");return}if(a(""),a("To deploy an API Gateway API:"),a(" aws apigateway create-deployment \\"),a(` --rest-api-id ${e} \\`),a(` --stage-name ${t.stage} \\`),a(` --region ${t.region}`),t.description)a(` --description "${t.description}"`)}catch(n){p(`Failed to deploy API: ${n.message}`),process.exit(1)}}),r.command("api:domains","List custom domain names").option("--region <region>","AWS region").action(async(e)=>{w("API Gateway Custom Domains");try{let t=await P(),n=e.region||t.project.region||"us-east-1";a(`Region: ${n}`),a(""),a("To list custom domains, use AWS CLI:"),a(` aws apigateway get-domain-names --region ${n}`),a(""),a("To create a custom domain:"),a(" 1. Request or import an SSL certificate in ACM"),a(" 2. Create a custom domain in API Gateway"),a(" 3. Create a base path mapping to your API"),a(" 4. Add a DNS record pointing to the distribution")}catch(t){p(`Failed to list domains: ${t.message}`),process.exit(1)}}),r.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(e,t)=>{w("API Usage Statistics");try{let n=await P(),i=t.region||n.project.region||"us-east-1";a(`API ID: ${e}`),a(`Stage: ${t.stage}`),a(`Period: Last ${t.days} days`),a("");let o=new Date,s=new Date;s.setDate(s.getDate()-Number.parseInt(t.days)),a("To view API metrics in CloudWatch:"),a(" aws cloudwatch get-metric-statistics \\"),a(" --namespace AWS/ApiGateway \\"),a(" --metric-name Count \\"),a(` --dimensions Name=ApiName,Value=${e} Name=Stage,Value=${t.stage} \\`),a(` --start-time ${s.toISOString()} \\`),a(` --end-time ${o.toISOString()} \\`),a(" --period 86400 \\"),a(" --statistics Sum \\"),a(` --region ${i}`),a(""),a("Available metrics:"),a(" - Count: Total API calls"),a(" - Latency: Response latency"),a(" - 4XXError: Client errors"),a(" - 5XXError: Server errors"),a(" - IntegrationLatency: Backend latency")}catch(n){p(`Failed to get usage: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("Export API Specification");try{a(`API ID: ${e}`),a(`Stage: ${t.stage}`),a(`Format: ${t.format==="oas30"?"OpenAPI 3.0":"Swagger 2.0"}`),a("");let n=t.format==="oas30"?"oas30":"swagger";a("To export the API specification:"),a(" aws apigateway get-export \\"),a(` --rest-api-id ${e} \\`),a(` --stage-name ${t.stage} \\`),a(` --export-type ${n} \\`),a(" --accepts application/json \\"),a(` --region ${t.region} \\`),a(` ${t.output||"api-spec.json"}`)}catch(n){p(`Failed to export API: ${n.message}`),process.exit(1)}}),r.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(e,t)=>{w("API Gateway Logs");try{let n=await P(),i=t.region||n.project.region||"us-east-1",o=`API-Gateway-Execution-Logs_${e}/${t.stage}`;if(a(`Log Group: ${o}`),a(""),a("To view logs:"),a(" aws logs filter-log-events \\"),a(` --log-group-name "${o}" \\`),a(` --region ${i}`),t.tail)a(""),a("For real-time log tailing, use:"),a(` aws logs tail "${o}" --follow --region ${i}`);a(""),a("Note: Ensure logging is enabled for the API stage."),a("You can enable it in the stage settings.")}catch(n){p(`Failed to get logs: ${n.message}`),process.exit(1)}}),r.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(e,t,n)=>{w("Test API Endpoint");try{let i=await P(),o=n.region||i.project.region||"us-east-1",s=`https://${e}.execute-api.${o}.amazonaws.com/${n.stage}${t.startsWith("/")?t:`/${t}`}`;a(`URL: ${s}`),a(`Method: ${n.method}`);let c={"Content-Type":"application/json"};if(n.header){let S=Array.isArray(n.header)?n.header:[n.header];for(let y of S){let[v,...R]=y.split(":");c[v.trim()]=R.join(":").trim()}}a("Headers:");for(let[S,y]of Object.entries(c))a(` ${S}: ${y}`);if(n.body)a(`Body: ${n.body}`);if(!await O(`
642
+ Send request?`,!0)){a("Operation cancelled");return}let d=new C("Sending request...");d.start();let u=Date.now(),m=await fetch(s,{method:n.method,headers:c,body:n.body}),f=Date.now()-u,h=await m.text();d.succeed(`Response received (${f}ms)`),a(`
643
+ Status: ${m.status} ${m.statusText}`),a(`
644
+ Response Headers:`),m.headers.forEach((S,y)=>{a(` ${y}: ${S}`)}),a(`
645
+ Response Body:`);try{let S=JSON.parse(h);console.log(JSON.stringify(S,null,2))}catch{console.log(h)}}catch(i){p(`Failed to test API: ${i.message}`),process.exit(1)}})}async function Nt(r){let e=await P(),t=q(process.cwd(),e),n=new We(t.store,{workerId:`cli:${process.pid}`});try{return await r(n,t.project.id,t.organization.id)}finally{t.store.close()}}function pf(r){if(!r)return;if(!["queued","running","succeeded","failed","cancelled","timed_out"].includes(r))throw Error("State must be queued, running, succeeded, failed, cancelled, or timed_out");return r}function ff(r,e){if(!r)return"—";let t=Math.max(0,new Date(e??Date.now()).getTime()-new Date(r).getTime());return t<1000?`${t}ms`:t<60000?`${Math.round(t/1000)}s`:`${Math.round(t/60000)}m`}function is(r){r.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(e)=>{try{await Nt(async(t,n)=>{let i=t.list({projectId:e.allProjects?void 0:n,state:pf(e.state),limit:500});x(["ID","State","Operation","Target","Attempt","Step / blocked","Queued","Duration"],i.map(({operation:o,job:s,approximatePosition:c})=>[o.id,o.state,o.kind,o.resourceId??o.environmentId??o.projectId??"global",`${o.attempt}/${s.maxAttempts}`,s.blockedReason??s.currentStep??(c?`${c.ahead} ahead`:"—"),o.createdAt,ff(o.startedAt,o.finishedAt)]))})}catch(t){p(t instanceof Error?t.message:String(t))}}),r.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(e,t)=>{try{await Nt(async(n)=>{let i=n.view(e);if(!i)throw Error(`Operation ${e} was not found`);a(JSON.stringify(i,null,2));for(let o of n.logs(e,{after:Number(t.after)||0,limit:500}))a(`${o.sequence} ${o.stream.toUpperCase()}${o.step?` [${o.step}]`:""} ${o.message}`)})}catch(n){p(n instanceof Error?n.message:String(n))}}),r.command("ops:cancel <id>","Cancel queued work or request running cancellation").option("--yes","Skip confirmation").action(async(e,t)=>{try{await Nt(async(n)=>{let i=n.view(e);if(!i)throw Error(`Operation ${e} was not found`);if(!t.yes&&!await O(`Cancel ${i.operation.kind} (${e})?`,!1))return;let o=n.requestCancellation(e);g(`${o.state==="cancelled"?"Cancelled":"Cancellation requested for"} ${e}`)})}catch(n){p(n instanceof Error?n.message:String(n))}}),r.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(e,t)=>{try{if(!t.class)throw Error("Pass --class with one of the retry classes shown by ops:show");await Nt(async(n)=>{let i=n.retry(e,t.class,{delayMs:Number(t.delay)||0});g(`Retry queued: ${i.id} · attempt ${i.attempt+1}`)})}catch(n){p(n instanceof Error?n.message:String(n))}}),r.command("ops:concurrency","Show effective durable queue concurrency limits").action(async()=>{try{await Nt(async(e)=>a(JSON.stringify(e.limits,null,2)))}catch(e){p(e instanceof Error?e.message:String(e))}}),r.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(e)=>{try{if(e.confirm!=="update queue limits")throw Error('Pass --confirm "update queue limits" to change production concurrency');let t={};for(let n of["project","environment","provider","builds"])if(e[n]!==void 0)t[n]=Number(e[n]);await Nt(async(n,i,o)=>g(`Queue concurrency updated: ${JSON.stringify(n.configureConcurrency(t,{organizationId:o}))}`))}catch(t){p(t instanceof Error?t.message:String(t))}}),r.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(e)=>{try{await Nt(async(t,n)=>{if(!e.yes&&!await O("Clear completed queue history whose retention has elapsed?",!1))return;let i=t.clearCompleted({projectId:n,before:e.before});g(`Cleared ${i} retained operation${i===1?"":"s"}.`)})}catch(t){p(t instanceof Error?t.message:String(t))}})}import{readFile as gf,writeFile as hf}from"node:fs/promises";function yf(r){if(r==null)return"—";if(r<60)return`${r}s`;if(r<3600)return`${Math.floor(r/60)}m`;if(r<86400)return`${Math.floor(r/3600)}h`;return`${Math.floor(r/86400)}d`}function vf(r){return r==null?"—":Pn(r)}function bf(r){return r.map((e)=>[e.id,e.name,e.provider,e.kind,e.status,e.desiredReplicas==null?"—":`${e.runningReplicas??0}/${e.desiredReplicas}`,e.image??e.runtime??"—",vf(e.resources?.memoryBytes??e.resources?.memoryLimitBytes),yf(e.ageSeconds)])}function wf(r,e){let t=e.search?.toLowerCase();return{...r,workloads:r.workloads.filter((n)=>(!e.provider||n.provider===e.provider)&&(!e.status||n.status===e.status)&&(!t||`${n.name} ${n.id} ${n.links.service??""} ${Object.values(n.tags).join(" ")}`.toLowerCase().includes(t)))}}async function Ot(r){let e=await P(),t=r??Object.keys(e.environments??{})[0]??"production";if(!Object.hasOwn(e.environments??{},t))throw Error(`Environment ${t} was not found`);let n=new os(e,t);return{config:e,env:t,service:n}}async function yr(r,e){if(e!==void 0)return e;return Z(`Type ${r.name} to confirm`,"")}async function Sf(r,e,t,n,i,o){return vr(r,`runtime.${e}`,{workloadId:r.id,provider:r.provider,action:e,replicas:t??null},i,()=>o.run({workloadId:r.id,action:e,replicas:t,confirm:n,recentAuth:!0}))}async function vr(r,e,t,n,i){let o=await P(),s=q(process.cwd(),o),c=new We(s.store,{workerId:`cli:${process.pid}`});try{let l=s.environments.get(n),d=s.store.listResources(s.project.id,l?.id).find((f)=>f.slug===r.links.service),u=s.store.getActorByExternalId("system","cli")??s.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI",metadata:{source:"cli"}}),m=c.enqueue({projectId:s.project.id,environmentId:l?.id,resourceId:d?.id,actorId:u.id,kind:e,input:nd(t),lockKey:d?`resource:${d.id}`:`runtime:${r.id}`,providerKey:r.provider,maxAttempts:1,timeoutSeconds:3600,resumePolicy:"fail",cancellationMode:"provider_non_cancellable",retentionDays:90}).operation;if(!c.claim(m.id))throw Error(`Workload is locked by another operation. Inspect ${m.id} with cloud ops:show.`);try{let f=await i();if(f.stdout)c.appendLog(m.id,f.stdout,{stream:"stdout"});if(f.stderr)c.appendLog(m.id,f.stderr,{stream:"stderr"});if(f.ok)c.complete(m.id,{ok:!0,command:f.command??null});else c.fail(m.id,f.error??f.stderr??"Runtime operation failed",{ok:!1});return{result:f,operationId:m.id}}catch(f){throw c.fail(m.id,f instanceof Error?f.message:String(f),{ok:!1,threw:!0}),f}}finally{s.store.close()}}function ss(r){r.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(e)=>{try{let{service:t}=await Ot(e.env),n=wf(await t.inventory(),e);if(e.json)a(JSON.stringify(n,null,2));else{x(["ID","Name","Provider","Kind","Status","Replicas","Image / runtime","Memory","Age"],bf(n.workloads));for(let i of n.sources.filter((o)=>o.status!=="fresh"))I(`${i.id}: ${i.status}${i.message?` — ${i.message}`:""}`)}}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}}),r.command("runtime:inspect <workload>","Inspect normalized config, replicas, networks, mounts, links, and capabilities").option("--env <environment>","Target environment").action(async(e,t)=>{try{let{service:n}=await Ot(t.env),i=await n.workload(e);if(!i)throw Error("Workload was not found in this project and environment");a(JSON.stringify(i,null,2))}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}}),r.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(e,t)=>{try{let{service:n}=await Ot(t.env),i=await n.logs(e,{limit:Number(t.lines)||200});if(t.json)a(JSON.stringify(i,null,2));else for(let o of i.lines)console.log(`${o.timestamp??"—"} ${o.message}`)}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}}),r.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(e,t,n)=>{try{let i=t;if(!["start","stop","restart","redeploy","scale"].includes(i))throw Error("Action must be start, stop, restart, redeploy, or scale");let o=n.replicas==null?void 0:Number(n.replicas);if(i==="scale"&&(!Number.isInteger(o)||o<0))throw Error("Scale requires a non-negative integer --replicas value");let{env:s,service:c}=await Ot(n.env),l=await c.inventory(),d=l.workloads.find((f)=>f.id===e);if(!d)throw Error("Workload was not found in this project and environment");let u=new os(await P(),s,{inventory:async()=>l}),m=await Sf(d,i,o,await yr(d,n.confirm),s,u);if(!m.result.ok)throw Error(m.result.error??m.result.stderr??"Runtime operation failed");if(g(`${i} completed for ${d.name} · operation ${m.operationId}`),m.result.stdout)a(m.result.stdout)}catch(i){p(i instanceof Error?i.message:String(i)),process.exitCode=1}}),r.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(e,t)=>{try{let{env:n,service:i}=await Ot(t.env),s=(await i.inventory()).workloads.find((u)=>u.id===e);if(!s)throw Error("Workload was not found in this project and environment");let c=["process","sockets","filesystem"].includes(String(t.preset))?t.preset:void 0;if(t.preset&&!c)throw Error("Preset must be process, sockets, or filesystem");if(!c&&!t.command)throw Error("Pass --preset or --command");let l=await yr(s,t.confirm),d=await vr(s,"runtime.exec",{workloadId:e,provider:s.provider,preset:c??null,freeForm:!!t.command},n,()=>i.exec({workloadId:e,preset:c,command:t.command,confirm:l,recentAuth:!0}));if(!d.result.ok)throw Error(d.result.error??d.result.stderr??"Exec failed");if(d.result.stdout)a(d.result.stdout);if(d.result.stderr)I(d.result.stderr);g(`Exec completed for ${s.name} · operation ${d.operationId}`)}catch(n){p(n instanceof Error?n.message:String(n)),process.exitCode=1}}),r.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(e,t,n,i)=>{try{let{env:o,service:s}=await Ot(i.env),l=(await s.inventory()).workloads.find((m)=>m.id===e);if(!l)throw Error("Workload was not found in this project and environment");let d=await yr(l,i.confirm),u=await vr(l,"runtime.file.read",{workloadId:e,provider:l.provider,path:t},o,()=>s.readFile({workloadId:e,path:t,confirm:d,recentAuth:!0}));if(!u.result.ok||!u.result.contentBase64)throw Error(u.result.error??"File download failed");await hf(n,Buffer.from(u.result.contentBase64,"base64")),g(`Downloaded ${u.result.size??0} bytes to ${n} · operation ${u.operationId}${u.result.truncated?" (truncated)":""}`)}catch(o){p(o instanceof Error?o.message:String(o)),process.exitCode=1}}),r.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(e,t,n,i)=>{try{let{env:o,service:s}=await Ot(i.env),l=(await s.inventory()).workloads.find((f)=>f.id===e);if(!l)throw Error("Workload was not found in this project and environment");let d=await yr(l,i.confirm),u=(await gf(t)).toString("base64"),m=await vr(l,"runtime.file.write",{workloadId:e,provider:l.provider,path:n,bytes:Buffer.from(u,"base64").byteLength},o,()=>s.writeFile({workloadId:e,path:n,contentBase64:u,confirm:d,recentAuth:!0}));if(!m.result.ok)throw Error(m.result.error??"File upload failed");g(`Uploaded ${m.result.size??0} bytes to ${n} · operation ${m.operationId}`)}catch(o){p(o instanceof Error?o.message:String(o)),process.exitCode=1}})}function $f(r,e=new Date){let t=r.to?new Date(r.to):e;if(!Number.isFinite(t.getTime()))throw Error("--to must be a valid ISO-8601 instant");if(r.from){let o=new Date(r.from);if(!Number.isFinite(o.getTime()))throw Error("--from must be a valid ISO-8601 instant");return{from:o.toISOString(),to:t.toISOString()}}let n=String(r.range??"1h").match(/^(\d+)(m|h|d)$/);if(!n)throw Error("--range must use minutes, hours, or days (for example 30m, 6h, or 7d)");let i=n[2]==="m"?60000:n[2]==="h"?3600000:86400000;return{from:new Date(t.getTime()-Number(n[1])*i).toISOString(),to:t.toISOString()}}async function xn(r){let e=await P(),t=r??Object.keys(e.environments??{})[0]??"production";if(!Object.hasOwn(e.environments??{},t))throw Error(`Environment ${t} was not found`);let n=q(process.cwd(),e),i=n.environments.get(t),o=new sd(n.store);return{config:e,env:t,controlPlane:n,environmentRecord:i,telemetry:o}}function rd(r,e,t){let n=(o)=>o?.split(",").map((s)=>s.trim()).filter(Boolean)||void 0,i=n(r.kind)?.filter((o)=>["metric","log","trace","request","event"].includes(o));return{projectId:e,environmentId:t,...$f(r),kinds:i,names:n(r.name),sources:n(r.source),text:r.text,limit:Math.min(5000,Math.max(1,Number(r.limit)||200))}}function Cf(r){let e=["timestamp","kind","source","name","level","value","unit","message","traceId","requestId","deploymentId","releaseId","workloadId"];return[e.join(","),...r.map((t)=>e.map((n)=>`"${String(t[n]??"").replaceAll('"','""')}"`).join(","))].join(`
646
+ `)}function cs(r){r.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(e)=>{let t=await xn(e.env);try{let n=await ad({controlPlane:t.controlPlane.store,projectId:t.controlPlane.project.id,environmentId:t.environmentRecord?.id,config:t.config,environment:t.env,force:e.force});if(e.json)a(JSON.stringify(n,null,2));else g(`Collected ${n.collected} normalized records${n.cached?" (cached provider result)":""}.`),x(["Source","Freshness","Lag","Sampling","Retention"],n.statuses.map((i)=>[i.source,i.freshness,i.lagSeconds==null?"—":`${i.lagSeconds}s`,`${Math.round(i.samplingRate*100)}%`,`${i.retentionDays}d`]))}finally{t.controlPlane.store.close()}}),r.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(e)=>{let t=await xn(e.env);try{let n=t.telemetry.query(rd(e,t.controlPlane.project.id,t.environmentRecord?.id));if(e.json)a(JSON.stringify(n,null,2));else x(["Time","Kind","Source","Signal","Value / message","Correlation"],n.records.map((i)=>[i.timestamp,i.kind,i.source,i.name,i.message??(i.value==null?"—":`${i.value} ${i.unit??""}`),i.requestId??i.traceId??i.releaseId??i.deploymentId??"—"]));if(n.truncated)I("More records are available; narrow the window or use the dashboard cursor explorer.")}finally{t.controlPlane.store.close()}}),r.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(e,t)=>{let n=await xn(t.env);try{let i=rd(t,n.controlPlane.project.id,n.environmentRecord?.id),o=n.telemetry.query(i);await Bun.write(e,e.toLowerCase().endsWith(".csv")?Cf(o.records):JSON.stringify({exportedAt:new Date().toISOString(),query:i,...o},null,2)),g(`Exported ${o.records.length} records to ${e}${o.truncated?" (bounded result)":""}.`)}finally{n.controlPlane.store.close()}}),r.command("telemetry:compact","Downsample old metrics and enforce configured retention").option("--env <environment>","Target environment").action(async(e)=>{let t=await xn(e.env);try{let n=as(t.controlPlane.store,t.controlPlane.project.id);a(JSON.stringify(t.telemetry.enforceRetention(n,t.controlPlane.project.id),null,2))}finally{t.controlPlane.store.close()}}),r.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(e)=>{let t=await xn(e.env);try{let n={};if(e.rawDays!=null)n.rawDays=Number(e.rawDays);if(e.downsampleDays!=null)n.downsampleAfterDays=Number(e.downsampleDays);if(e.sampling!=null)n.samplingRate=Number(e.sampling);if(e.storageUsdGbMonth!=null)n.estimatedStorageUsdPerGbMonth=Number(e.storageUsdGbMonth);let i=Object.keys(n).length?id(t.controlPlane.store,t.controlPlane.project.id,n):as(t.controlPlane.store,t.controlPlane.project.id),s=t.telemetry.status(t.controlPlane.project.id,t.environmentRecord?.id,i.rawDays,i.samplingRate).reduce((l,d)=>l+d.estimatedDailyBytes*30,0),c={policy:i,estimatedMonthlyBytes:s,estimatedMonthlyCostUsd:od(s,i),costEstimateConfigured:i.estimatedStorageUsdPerGbMonth>0};if(e.json)a(JSON.stringify(c,null,2));else a(JSON.stringify(i,null,2)),a(c.costEstimateConfigured?`Estimated local storage: $${c.estimatedMonthlyCostUsd}/month`:"Storage cost remains unavailable until --storage-usd-gb-month is set.")}finally{t.controlPlane.store.close()}})}async function $t(r){let e=await P(),t=r??Object.keys(e.environments??{})[0]??"production";if(!Object.hasOwn(e.environments??{},t))throw Error(`Environment ${t} was not found`);let n=q(process.cwd(),e),i=n.environments.get(t),o=new bn(n.store,{encryptionKey:le(process.cwd())});return{config:e,env:t,controlPlane:n,environmentRecord:i,alerts:o}}function cd(r){return new _o(r,{emailImpl:async(e)=>{let{email:t}=await import("./chunk-qtdngvs8.js");return t.send(e)}})}function ls(r){r.command("health:list","List configured health checks and latest results").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>{let t=await $t(e.env);try{let n=t.alerts.listHealthChecks(t.controlPlane.project.id,t.environmentRecord?.id).map((i)=>({...i,latest:t.alerts.listHealthResults(i.id,1)[0]}));if(e.json)a(JSON.stringify(n,null,2));else x(["ID","Name","Kind","Target","Status","Interval","Enabled"],n.map((i)=>[i.id,i.name,i.kind,i.target,i.latest?.status??"no data",`${i.intervalSeconds}s`,i.enabled?"yes":"no"]))}finally{t.controlPlane.store.close()}}),r.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(e,t)=>{let n=await $t(t.env);try{let i=n.alerts.getHealthCheck(e)??n.alerts.listHealthChecks(n.controlPlane.project.id,n.environmentRecord?.id).find((c)=>c.name===e);if(!i)throw Error("Health check was not found");let o=new zo(n.alerts);if(t.test){let c=await o.probe(i);a(JSON.stringify({...c,productionIncidentCreated:!1},null,2));return}let s=await o.runAndEvaluate(i);if(a(JSON.stringify(s,null,2)),s.result.status==="healthy")g(`${i.name} is healthy.`);else I(`${i.name} is ${s.result.status}.`)}finally{n.controlPlane.store.close()}}),r.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(e)=>{let t=await $t(e.env);try{let n=t.alerts.listAlerts(t.controlPlane.project.id,{environmentId:t.environmentRecord?.id,states:e.state?.split(",").filter(Boolean)});if(e.json)a(JSON.stringify(n,null,2));else x(["ID","State","Severity","Title","Owner","First seen","Last seen"],n.map((i)=>[i.id,i.state,i.severity,i.title,i.ownerActorId??"unassigned",i.firstSeenAt,i.lastSeenAt]))}finally{t.controlPlane.store.close()}}),r.command("alerts:evaluate","Evaluate persisted telemetry rules now").option("--env <environment>","Target environment").action(async(e)=>{let t=await $t(e.env);try{let n=Uo(t.alerts,t.controlPlane.project.id,t.environmentRecord?.id);a(JSON.stringify(n,null,2))}finally{t.controlPlane.store.close()}}),r.command("alerts:ack <alert>","Acknowledge an alert as the CLI actor").option("--env <environment>","Target environment").action(async(e,t)=>{let n=await $t(t.env);try{let i=n.controlPlane.store.getActorByExternalId("system","cli")??n.controlPlane.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});g(`Acknowledged ${n.alerts.acknowledge(e,i.id).title}.`)}finally{n.controlPlane.store.close()}}),r.command("notifications:list","List notification channels, routes, and recent delivery diagnostics").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>{let t=await $t(e.env);try{let n={channels:t.alerts.listChannels(t.controlPlane.organization.id),routes:t.alerts.listRoutes(t.controlPlane.organization.id),deliveries:t.alerts.listDeliveries({limit:100})};if(e.json)a(JSON.stringify(n,null,2));else x(["Channel","Type","Status","Tested","Error"],n.channels.map((i)=>[i.name,i.kind,i.status,i.lastTestedAt??"never",i.lastError??"—"])),x(["Event","State","Attempts","Status","Error"],n.deliveries.map((i)=>[i.eventType,i.state,`${i.attempt}/${i.maxAttempts}`,String(i.responseStatus??"—"),i.error??"—"]))}finally{t.controlPlane.store.close()}}),r.command("notifications:test <channel>","Send a channel test without creating a production incident").option("--env <environment>","Target environment").action(async(e,t)=>{let n=await $t(t.env);try{let i=n.alerts.getChannel(e)??n.alerts.listChannels(n.controlPlane.organization.id).find((s)=>s.name===e);if(!i)throw Error("Notification channel was not found");let o=await cd(n.alerts).testChannel(i.id);if(o.ok)g("Test delivered; no production incident was created.");else throw Error(o.error)}finally{n.controlPlane.store.close()}}),r.command("notifications:retry <delivery>","Retry one failed notification delivery").option("--env <environment>","Target environment").action(async(e,t)=>{let n=await $t(t.env);try{let i=n.alerts.getDelivery(e);if(!i)throw Error("Notification delivery was not found");n.alerts.updateDelivery(e,{state:"retrying",attempt:Math.max(0,i.attempt-1),nextAttemptAt:new Date().toISOString()}),a(JSON.stringify(await cd(n.alerts).deliver(e),null,2))}finally{n.controlPlane.store.close()}})}async function Af(r,e){let t=await P(),n=q(process.cwd(),t),i=!r&&e?new ds(n.store).get(e):void 0,o=i?.environmentId?n.store.listEnvironments(n.project.id).find((h)=>h.id===i.environmentId)?.slug:void 0,s=r??o??Object.keys(t.environments??{})[0]??"production";if(!Object.hasOwn(t.environments??{},s))throw n.store.close(),Error(`Environment ${s} was not found`);let c=n.environments.get(s);if(!c)throw n.store.close(),Error(`Environment ${s} was not found in the control plane`);let l=new ds(n.store),d=new We(n.store,{workerId:`cli:${process.pid}`}),u=new hd(l,{queue:d}),m=n.store.listResources(n.project.id,c.id);us(l,t,{organization:n.organization,project:n.project,environment:c,resources:m});let f=n.store.getActorByExternalId("system","cli")??n.store.createActor({kind:"system",externalId:"cli",displayName:"ts-cloud CLI"});return{config:t,env:s,controlPlane:n,environmentRecord:c,resources:m,store:l,queue:d,service:u,actor:f}}async function je(r,e,t){let n=await Af(r,t);try{return await e(n)}finally{n.controlPlane.store.close()}}async function Ae(r){try{await r()}catch(e){p(e instanceof Error?e.message:String(e))}}function Xt(r,e){let t=r.store.list(r.controlPlane.project.id,{environmentId:r.environmentRecord.id}),n=r.store.get(e)??t.find((i)=>i.name===e);if(!n||n.environmentId!==r.environmentRecord.id)throw Error(`Scheduled job ${e} was not found in ${r.env}`);return n}function Lt(r,e,t,n){r.controlPlane.store.appendEvent({organizationId:r.controlPlane.organization.id,projectId:r.controlPlane.project.id,resourceId:n,actorId:r.actor.id,type:`job.${e}`,payload:t})}function ld(r,e,t){if(r.environmentRecord.kind==="production"&&t!==e)throw Error(`Production ${e} requires --confirm ${e}`)}function kf(r){if(!r)return{};if(!/^(?:secret|ssm|arn|env):\/\//.test(r))throw Error("Payloads must use a secret://, ssm://, arn://, or env:// reference");return{input:r}}function Rf(r){return r.map((e)=>[e.id,e.enabled?"enabled":"disabled",e.provider,e.name,e.expression,e.timezone,e.nextRunAt??"—",e.reconciliationStatus])}function ms(r){r.command("jobs:list","List server cron, EventBridge, and recurring control-plane jobs").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>Ae(async()=>je(e.env,async(t)=>{let n=t.store.list(t.controlPlane.project.id,{environmentId:t.environmentRecord.id});if(e.json)a(JSON.stringify(n,null,2));else x(["ID","State","Provider","Name","Expression","Timezone","Next run","Reconciliation"],Rf(n))}))),r.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(e,t)=>Ae(async()=>{let n=fd(e,t.timezone??"UTC",t.from?new Date(t.from):new Date,Number(t.count)||5);a(JSON.stringify({...n,productionExecutionCreated:!1},null,2))})),r.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(e,t,n)=>Ae(async()=>je(n.env,async(i)=>{if(!["skip","catch_up"].includes(n.missed??"skip"))throw Error("--missed must be skip or catch_up");if(!["allow","forbid","replace"].includes(n.overlap??"forbid"))throw Error("--overlap must be allow, forbid, or replace");let o=An(i.config),c={workers:i.store.listWorkers(i.controlPlane.project.id,i.environmentRecord.id).map((h)=>({name:h.name}))},l;if(o==="server"){let h=dd(i.config,c).filter((y)=>!y.danger&&["scheduler","backup","worker"].includes(y.group)),S=n.operation?h.find((y)=>y.id===n.operation):h.length===1?h[0]:void 0;if(!S)throw Error(`Choose a safe target with --operation. Available: ${h.map((y)=>y.id).join(", ")||"none"}`);l={kind:"dashboard_operation",operationId:S.id}}else l={kind:"serverless_scheduler",action:"run"};let d=o==="server"?"server":"eventbridge",u=gd({provider:d,expression:t,flexibleMinutes:Number(n.flexibleMinutes)||0,missedRunPolicy:n.missed??"skip",overlapPolicy:n.overlap??"forbid"});if(!u.supported)throw Error(u.notes.join(" "));let m=n.resource?i.resources.find((h)=>h.id===n.resource||h.slug===n.resource):void 0;if(n.resource&&!m)throw Error(`Resource ${n.resource} was not found in ${i.env}`);let f=i.store.create({organizationId:i.controlPlane.organization.id,projectId:i.controlPlane.project.id,environmentId:i.environmentRecord.id,resourceId:m?.id,name:e,provider:d,expression:t,timezone:n.timezone??"UTC",startsAt:n.startsAt,endsAt:n.endsAt,flexibleMinutes:Number(n.flexibleMinutes)||0,target:l,payloadRefs:kf(n.payloadRef),missedRunPolicy:n.missed??"skip",overlapPolicy:n.overlap??"forbid",retryPolicy:{maxAttempts:Number(n.attempts)||3,backoffSeconds:Number(n.backoff)||30,deadLetterRef:n.deadLetter},timeoutSeconds:Number(n.timeout)||900,enabled:!n.disabled,origin:"managed",ownerActorId:i.actor.id,observedState:{},reconciliationStatus:"pending"});Lt(i,"created",{jobId:f.id,provider:f.provider,expression:f.normalizedExpression},f.resourceId),g(`Created ${f.name} (${f.id}); next run ${f.nextRunAt??"outside the active window"}`)}))),r.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(e,t)=>Ae(async()=>je(t.env,async(n)=>{let i=Xt(n,e);if(i.origin==="config")throw Error("Config-defined schedules must be edited in cloud.config.ts and reconciled");if(t.missed&&!["skip","catch_up"].includes(t.missed))throw Error("--missed must be skip or catch_up");if(t.overlap&&!["allow","forbid","replace"].includes(t.overlap))throw Error("--overlap must be allow, forbid, or replace");let o=n.store.updateSchedule(i.id,{expression:t.expression,timezone:t.timezone,missedRunPolicy:t.missed,overlapPolicy:t.overlap,timeoutSeconds:t.timeout==null?void 0:Number(t.timeout)});Lt(n,"updated",{jobId:o.id,expression:o.normalizedExpression,version:o.version},o.resourceId),g(`Updated ${o.name}; next run ${o.nextRunAt??"outside the active window"}`)}))),r.command("jobs:run <job>","Queue a manual run through the durable operation worker").option("--env <environment>","Target environment").action(async(e,t)=>Ae(async()=>je(t.env,async(n)=>{let i=Xt(n,e),o=n.service.enqueue(i,{trigger:"manual",actorId:n.actor.id});Lt(n,"run_queued",{jobId:i.id,executionId:o.id},i.resourceId),g(`Queued execution ${o.id}; inspect it with cloud jobs:history "${i.name}"`)}))),r.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(e,t)=>Ae(async()=>je(t.env,async(n)=>{let i=Xt(n,e);if(!i.enabled)throw Error(`${i.name} is disabled`);let o=t.scheduledFor?new Date(t.scheduledFor):new Date;if(!t.scheduledFor)o.setUTCSeconds(0,0);let s=o.toISOString(),c=n.service.enqueue(i,{trigger:t.scheduled?"scheduled":"external",scheduledFor:s,actorId:n.actor.id}),l=pd(i.normalizedExpression,i.timezone,o,1)[0];n.store.markScheduled(i.id,s,l),g(`Accepted ${i.name} for ${s} as ${c.id}`)},e)));for(let e of["enable","disable"])r.command(`jobs:${e} <job>`,`${e==="enable"?"Enable":"Disable"} a scheduled job`).option("--env <environment>","Target environment").option("--confirm <text>",`Production ${e} confirmation`).action(async(t,n)=>Ae(async()=>je(n.env,async(i)=>{let o=Xt(i,t);if(e==="disable")ld(i,e,n.confirm);let s=i.store.setEnabled(o.id,e==="enable");Lt(i,e==="enable"?"enabled":"disabled",{jobId:s.id},s.resourceId),g(`${s.name} is ${s.enabled?"enabled":"disabled"}`)})));r.command("jobs:delete <job>","Delete a managed schedule").option("--env <environment>","Target environment").option("--confirm <text>","Must be delete in production").action(async(e,t)=>Ae(async()=>je(t.env,async(n)=>{let i=Xt(n,e);ld(n,"delete",t.confirm),n.store.remove(i.id),Lt(n,"deleted",{jobId:i.id,name:i.name},i.resourceId),g(`Deleted ${i.name}`)}))),r.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(e,t)=>Ae(async()=>je(t.env,async(n)=>{let i=Xt(n,e),o=n.store.listExecutions(i.id,Number(t.limit)||100).map((s)=>({...s,logs:t.logs&&s.operationId?n.queue.logs(s.operationId,{limit:500}):[]}));if(t.json)a(JSON.stringify(o,null,2));else if(x(["ID","Status","Trigger","Scheduled for","Attempt","Started","Finished","Error"],o.map((s)=>[s.id,s.status,s.trigger,s.scheduledFor,String(s.attempt),s.startedAt??"—",s.finishedAt??"—",s.error??"—"])),t.logs)for(let s of o)for(let c of s.logs)a(`${s.id} ${c.sequence} ${c.stream.toUpperCase()} ${c.message}`)}))),r.command("jobs:reconcile","Import declarative schedules and workers without destructive deletion").option("--env <environment>","Target environment").action(async(e)=>Ae(async()=>je(e.env,async(t)=>{let n=us(t.store,t.config,{organization:t.controlPlane.organization,project:t.controlPlane.project,environment:t.environmentRecord,resources:t.resources});Lt(t,"reconciled",n),g(`Reconciled ${n.jobs} schedules and ${n.workers} workers; retained ${n.drifted} missing definitions as drifted`)}))),r.command("workers:list","List configured systemd, ECS, and Lambda workers").option("--env <environment>","Target environment").option("--json","Print structured JSON").action(async(e)=>Ae(async()=>je(e.env,async(t)=>{let n=t.store.listWorkers(t.controlPlane.project.id,t.environmentRecord.id);if(e.json)a(JSON.stringify(n,null,2));else x(["ID","Provider","Name","Queue","Processes","Timeout","Restart","Reconciliation"],n.map((i)=>[i.id,i.provider,i.name,i.queue,String(i.processes),`${i.timeoutSeconds}s`,i.restartPolicy,i.reconciliationStatus]))}))),r.command("workers:restart <worker>","Gracefully restart an allowlisted systemd worker").option("--env <environment>","Target environment").option("--confirm <name>","Exact worker name confirmation").action(async(e,t)=>Ae(async()=>je(t.env,async(n)=>{let i=n.store.listWorkers(n.controlPlane.project.id,n.environmentRecord.id),o=n.store.getWorker(e)??i.find((l)=>l.name===e);if(!o||o.environmentId!==n.environmentRecord.id)throw Error(`Worker ${e} was not found in ${n.env}`);if(t.confirm!==o.name)throw Error(`Restart requires --confirm "${o.name}"`);if(o.provider!=="systemd")throw Error(`${o.provider} workers are provider/config managed and do not expose a safe restart adapter`);let s=ud(String(o.target.operationId??""),n.config,{workers:i.map((l)=>({name:l.name}))});if(!s||s.danger||s.group!=="worker")throw Error("The allowlisted worker restart operation is unavailable");let c=await md(n.config,n.env,s);if(n.store.reconcileWorker(o.id,c.ok?"in_sync":"unavailable",{...o.observedState,lastRestartAt:new Date().toISOString(),command:c.command??null,error:c.error??c.stderr??null}),Lt(n,"worker_restarted",{workerId:o.id,ok:c.ok},o.resourceId),!c.ok)throw Error(c.error??c.stderr??"Worker restart failed");g(`Restarted ${o.name}`)})))}function ps(r){r.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(e)=>{w("Local Tunnel");let t=Number.parseInt(e.port);if(a(`Local server: ${e.host}:${t}`),a(`Tunnel server: ${e.server}`),e.subdomain)a(`Requested subdomain: ${e.subdomain}`);let n=new C("Connecting to tunnel server...");n.start();try{let i;try{i=(await import("localtunnels")).TunnelClient}catch{n.fail("localtunnels package not found"),a(`
647
+ To use tunnels, install the localtunnels package:`),a(" bun add localtunnels"),a(`
648
+ Or use the standalone CLI:`),a(" bunx localtunnels --port 3000");return}let o=e.server.replace(/^(wss?|https?):\/\//,""),s=e.server.startsWith("wss://")||e.server.startsWith("https://")||o==="localtunnel.dev",c=new i({localPort:t,localHost:e.host,subdomain:e.subdomain,host:o,port:s?443:80,secure:s,verbose:e.verbose});c.on("connected",(d)=>{n.succeed("Connected!"),g(`
649
+ Your tunnel URL: ${d.url}`),a(`Subdomain: ${d.subdomain}`),a(`
650
+ Forwarding: ${d.url} -> http://${e.host}:${t}`),a(`
651
+ Press Ctrl+C to stop the tunnel`)}),c.on("request",(d)=>{if(e.verbose)a(`→ ${d.method} ${d.url}`)}),c.on("response",(d)=>{if(e.verbose)a(`← ${d.status} (${d.size} bytes${d.duration?`, ${d.duration}ms`:""})`)}),c.on("reconnecting",(d)=>{a(`Reconnecting... (attempt ${d.attempt}/${d.maxAttempts})`)}),c.on("error",(d)=>{p(`Tunnel error: ${d.message}`)}),c.on("close",()=>{a(`
652
+ Tunnel closed`)});let l=()=>{a(`
653
+ Shutting down tunnel...`),c.disconnect(),process.exit(0)};process.on("SIGINT",l),process.on("SIGTERM",l),await c.connect(),await new Promise(()=>{})}catch(i){n.fail("Failed to connect"),p(`Error: ${i.message}`),process.exit(1)}}),r.command("tunnel:status","Check tunnel server status").option("--server <url>","Tunnel server URL",{default:"https://localtunnel.dev"}).action(async(e)=>{w("Tunnel Server Status");let t=new C("Checking server status...");t.start();try{let n=e.server.startsWith("http")?e.server:`https://${e.server}`,i=await fetch(`${n}/status`,{method:"GET",headers:{Accept:"application/json"}});if(i.ok){let o=await i.json();if(t.succeed("Server is online"),a(`
654
+ Server: ${e.server}`),a(`Status: ${o.status||"operational"}`),o.version)a(`Version: ${o.version}`);if(o.connections!==void 0)a(`Active connections: ${o.connections}`);if(o.uptime)a(`Uptime: ${o.uptime}`);if(o.activeSubdomains?.length)a(`Active subdomains: ${o.activeSubdomains.join(", ")}`)}else t.fail(`Server returned status ${i.status}`)}catch(n){t.fail("Failed to check server status"),p(`Error: ${n.message}`),a(`
655
+ The tunnel server may be offline or unreachable.`)}}),r.command("tunnel:info","Show tunnel configuration and setup info").action(async()=>{w("Local Tunnel Information"),a("ts-cloud uses localtunnels for secure tunnel connections."),a(""),a("Default server: localtunnel.dev"),a(""),a("Usage:"),a(" cloud tunnel --port 3000 # Expose port 3000"),a(" cloud tunnel --port 8080 --subdomain myapp"),a(""),a("Features:"),a(" - Secure WebSocket-based tunnels"),a(" - Custom subdomains (when available)"),a(" - Automatic reconnection"),a(" - Request logging"),a(" - Binary data support"),a(""),a("Self-hosted tunnel server:"),a(" You can run your own tunnel server using localtunnels."),a(" See: https://github.com/stacksjs/localtunnels"),a(""),a("Environment variables:"),a(" TUNNEL_SERVER - Custom tunnel server URL"),a(" TUNNEL_SUBDOMAIN - Default subdomain to request")}),r.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(e)=>{if(w("Deploy Tunnel Infrastructure"),a(`Region: ${e.region}`),a(`Prefix: ${e.prefix}`),a(""),a("This will deploy:"),a(" - DynamoDB tables for connection tracking"),a(" - Lambda functions for handling requests"),a(" - Lambda Function URLs for public access"),a(""),!await O("Deploy tunnel infrastructure?",!1)){a("Operation cancelled");return}let n=new C("Deploying infrastructure...");n.start();try{let i;try{i=(await import("localtunnels/cloud")).deployTunnelInfrastructure}catch{n.fail("localtunnels package not found"),a(`
656
+ To deploy tunnel infrastructure, install localtunnels:`),a(" bun add localtunnels"),a(`
657
+ Or deploy using the localtunnels CLI directly:`),a(" bunx localtunnels deploy --region us-east-1");return}let o=await i({region:e.region,prefix:e.prefix,verbose:e.verbose});if(n.succeed("Deployment complete!"),a(""),a("Resources created:"),a(" DynamoDB Tables:"),a(` - ${o.connectionsTable}`),a(` - ${o.responsesTable}`),a(""),a(" Lambda Functions:"),a(` - ${o.functions.http}`),a(` - ${o.functions.message}`),a(""),o.httpUrl||o.wsUrl){if(a("Endpoints:"),o.httpUrl)a(` HTTP URL: ${o.httpUrl}`);if(o.wsUrl)a(` WebSocket URL: ${o.wsUrl}`)}}catch(i){if(n.fail("Deployment failed"),p(`Error: ${i.message}`),e.verbose)console.error(i.stack);process.exit(1)}}),r.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(e)=>{if(w("Destroy Tunnel Infrastructure"),a(`Region: ${e.region}`),a(`Prefix: ${e.prefix}`),a(""),I("This will permanently delete:"),I(" - All DynamoDB tables and data"),I(" - All Lambda functions"),I(" - All IAM roles and policies"),a(""),!await O("Are you sure you want to destroy this infrastructure?",!1)){a("Operation cancelled");return}let n=new C("Destroying infrastructure...");n.start();try{let i;try{i=(await import("localtunnels/cloud")).destroyTunnelInfrastructure}catch{n.fail("localtunnels package not found"),a(`
658
+ To destroy tunnel infrastructure, install localtunnels:`),a(" bun add localtunnels"),a(`
659
+ Or destroy using the localtunnels CLI directly:`),a(" bunx localtunnels destroy --region us-east-1");return}await i({region:e.region,prefix:e.prefix,verbose:e.verbose}),n.succeed("Infrastructure destroyed!")}catch(i){if(n.fail("Destruction failed"),p(`Error: ${i.message}`),e.verbose)console.error(i.stack);process.exit(1)}}),r.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(e)=>{w("Tunnel Server Logs"),a("Tunnel server log groups:"),a(` - /aws/lambda/${e.prefix}-connect`),a(` - /aws/lambda/${e.prefix}-disconnect`),a(` - /aws/lambda/${e.prefix}-message`),a(` - /aws/lambda/${e.prefix}-http`),a(""),a("To view logs:"),a(` aws logs tail /aws/lambda/${e.prefix}-http --region ${e.region}${e.tail?" --follow":""}`),a(""),a("Or use CloudWatch Insights:"),a(" 1. Go to CloudWatch > Logs Insights"),a(" 2. Select the tunnel log groups"),a(" 3. Run queries like:"),a(" fields @timestamp, @message | filter @message like /error/i")}),r.command("tunnel:test <url>","Test a tunnel connection").action(async(e)=>{w("Test Tunnel Connection");let t=new C(`Testing ${e}...`);t.start();try{let n=Date.now(),i=await fetch(e,{method:"GET",headers:{"User-Agent":"ts-cloud-tunnel-test"}}),o=Date.now()-n;t.succeed(`Connected in ${o}ms`),a(`
660
+ Status: ${i.status} ${i.statusText}`),a(`
661
+ Response headers:`),i.headers.forEach((c,l)=>{a(` ${l}: ${c}`)});let s=await i.text();if(s.length>0){if(a(`
662
+ Response body (first 500 chars):`),console.log(s.substring(0,500)),s.length>500)a(`... (${s.length-500} more characters)`)}}catch(n){t.fail("Connection failed"),p(`Error: ${n.message}`)}}),r.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(e)=>{w("Local Tunnel Server");let t=Number.parseInt(e.port);a(`Listening on: ${e.host}:${t}`),a(`Domain: ${e.domain}`),a("");let n=new C("Starting tunnel server...");n.start();try{let i;try{i=(await import("localtunnels")).TunnelServer}catch{n.fail("localtunnels package not found"),a(`
663
+ To run a tunnel server, install localtunnels:`),a(" bun add localtunnels"),a(`
664
+ Or use the standalone CLI:`),a(" bunx localtunnels server --port 3000");return}let o=new i({port:t,host:e.host,verbose:e.verbose});o.on("connection",(c)=>{a(`+ Client connected: ${c.subdomain} (total: ${c.totalConnections})`)}),o.on("disconnection",(c)=>{a(`- Client disconnected: ${c.subdomain}`)});let s=()=>{a(`
665
+ Shutting down server...`),o.stop(),process.exit(0)};process.on("SIGINT",s),process.on("SIGTERM",s),await o.start(),n.succeed("Server running!"),a(""),a(`WebSocket URL: ws://${e.host==="0.0.0.0"?"localhost":e.host}:${t}`),a(`HTTP URL: http://${e.host==="0.0.0.0"?"localhost":e.host}:${t}`),a(""),a("Clients can connect with:"),a(` cloud tunnel --port 3000 --server ${e.host==="0.0.0.0"?"localhost":e.host}:${t}`),a(""),a("Press Ctrl+C to stop the server"),await new Promise(()=>{})}catch(i){n.fail("Failed to start server"),p(`Error: ${i.message}`),process.exit(1)}})}var U=new Rr("cloud");U.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");yi(U);Er(U);Or(U);Fr(U);Vr(U);Po(U);Do(U);To(U);No(U);Zr(U);Qr(U);Xr(U);ei(U);ni(U);ri(U);Mo(U);Bo(U);Wo(U);qo(U);Go(U);Ko(U);ui(U);wi(U);hi(U);es(U);Ho(U);Jo(U);Yo(U);Zo(U);vi(U);bi(U);Qo(U);Xo(U);ts(U);ns(U);rs(U);is(U);ss(U);cs(U);ls(U);ms(U);Ii(U);Fi(U);Ni(U);_i(U);Ui(U);Wi(U);Gi(U);Hi(U);Ji(U);Yi(U);Lo(U);ps(U);jo(U);Oo(U,Ir);U.version(Ir);U.help();U.parse();