@stacksjs/ts-cloud 0.16.89 → 0.16.91

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 (207) hide show
  1. package/dist/aws/client.d.ts +3 -5
  2. package/dist/aws/credentials.d.ts +9 -4
  3. package/dist/aws/index.js +1 -1
  4. package/dist/bin/chunk-0b6rv9dt.js +1 -0
  5. package/dist/bin/chunk-0qzt0swj.js +5 -0
  6. package/dist/bin/chunk-0rvzy2z7.js +1 -0
  7. package/dist/bin/{chunk-z9g7fa1w.js → chunk-0zepgwq9.js} +2 -2
  8. package/dist/bin/chunk-11hc309t.js +1 -0
  9. package/dist/bin/{chunk-qmwqntnx.js → chunk-1p7rtk98.js} +2 -2
  10. package/dist/bin/{chunk-qpasspad.js → chunk-32dr1fym.js} +5 -5
  11. package/dist/bin/{chunk-v3trt4q1.js → chunk-3h7gmatb.js} +3 -3
  12. package/dist/bin/{chunk-69mejej3.js → chunk-407wz1f4.js} +1 -1
  13. package/dist/bin/{chunk-xycxw00z.js → chunk-42nh9ac7.js} +8 -8
  14. package/dist/bin/chunk-49zcdbn0.js +1 -0
  15. package/dist/bin/chunk-4gv5j5s1.js +1 -0
  16. package/dist/bin/{chunk-gn7rdyz7.js → chunk-4kka906v.js} +2 -2
  17. package/dist/bin/{chunk-jqs2ywj2.js → chunk-55mv5w3m.js} +2 -2
  18. package/dist/bin/{chunk-4mep3m41.js → chunk-57yxbyh6.js} +5 -5
  19. package/dist/bin/chunk-5nfhjr0h.js +1 -0
  20. package/dist/bin/chunk-5njr2s5x.js +1 -0
  21. package/dist/bin/chunk-6qhgzf9a.js +1 -0
  22. package/dist/bin/{chunk-bycbwddj.js → chunk-6ty1wegm.js} +2 -2
  23. package/dist/bin/chunk-8ctmc30y.js +14 -0
  24. package/dist/bin/{chunk-deptcjq9.js → chunk-97j6xrfe.js} +97 -97
  25. package/dist/bin/chunk-9g4qsx30.js +1 -0
  26. package/dist/bin/chunk-afvk9mbs.js +1 -0
  27. package/dist/bin/{chunk-9bmaq8q7.js → chunk-cb0yqrpk.js} +111 -113
  28. package/dist/bin/{chunk-bxks81wn.js → chunk-chx779q9.js} +3 -3
  29. package/dist/bin/chunk-ckbv6y4c.js +1 -0
  30. package/dist/bin/{chunk-6b6yr784.js → chunk-drc9y2fj.js} +2 -2
  31. package/dist/bin/{chunk-b1cb27m8.js → chunk-eqqqbsjm.js} +7 -7
  32. package/dist/bin/{chunk-cgyk48s5.js → chunk-fcst782q.js} +1 -1
  33. package/dist/bin/{chunk-19mhwmfp.js → chunk-fmgv99xw.js} +1 -1
  34. package/dist/bin/{chunk-0e56zmps.js → chunk-g1s5s95g.js} +2 -2
  35. package/dist/bin/{chunk-h6xne9s6.js → chunk-g21yhm1b.js} +96 -96
  36. package/dist/bin/{chunk-ye2gymsr.js → chunk-gkgqss71.js} +3 -3
  37. package/dist/bin/chunk-gvacnz2y.js +1 -0
  38. package/dist/bin/{chunk-em9swj0k.js → chunk-h99kxww0.js} +1 -1
  39. package/dist/bin/chunk-j1dbkpha.js +1 -0
  40. package/dist/bin/{chunk-zfn3a9sx.js → chunk-j34ask5s.js} +2 -2
  41. package/dist/bin/{chunk-f7jzs1h1.js → chunk-j7j7gffr.js} +5 -5
  42. package/dist/bin/chunk-jjw5d8b9.js +1 -0
  43. package/dist/bin/{chunk-1wgyq50e.js → chunk-kbtx3q9m.js} +29 -30
  44. package/dist/bin/{chunk-t0r1rdj2.js → chunk-kdjrcvr6.js} +2 -2
  45. package/dist/bin/{chunk-76ekkat8.js → chunk-kefwtr8r.js} +3 -3
  46. package/dist/bin/{chunk-v1a0b9yq.js → chunk-kp84kged.js} +16 -16
  47. package/dist/bin/chunk-m5tpfe3s.js +1 -0
  48. package/dist/bin/{chunk-83tt2t15.js → chunk-mrkrn8dk.js} +5 -5
  49. package/dist/bin/chunk-n6yvpj86.js +1 -0
  50. package/dist/bin/{chunk-bkx2kapc.js → chunk-ncj8dpdp.js} +2 -2
  51. package/dist/bin/{chunk-w40r35vn.js → chunk-p6swcv5y.js} +1 -1
  52. package/dist/bin/{chunk-ndvma07s.js → chunk-pat56kbr.js} +5 -5
  53. package/dist/bin/chunk-q2hg6hmg.js +1 -0
  54. package/dist/bin/chunk-qd1jfsam.js +1 -0
  55. package/dist/bin/{chunk-yf2drw7r.js → chunk-r1q343bm.js} +1 -1
  56. package/dist/bin/chunk-r50x03ws.js +1 -0
  57. package/dist/bin/chunk-rhncmffy.js +1 -0
  58. package/dist/bin/chunk-seh19g93.js +1 -0
  59. package/dist/bin/{chunk-35325988.js → chunk-sjzzx539.js} +2 -2
  60. package/dist/bin/chunk-sxfdxk39.js +1 -0
  61. package/dist/bin/chunk-ty2pdbz3.js +1 -0
  62. package/dist/bin/chunk-tyeessz3.js +1 -0
  63. package/dist/bin/chunk-vbsk8b9d.js +1 -0
  64. package/dist/bin/chunk-we82q71y.js +1 -0
  65. package/dist/bin/{chunk-zn9wmqd1.js → chunk-xhqfha32.js} +2 -2
  66. package/dist/bin/{chunk-89v3ec9n.js → chunk-xk8nrvjz.js} +1 -1
  67. package/dist/bin/{chunk-88s5w4yt.js → chunk-zpk7fn1s.js} +6 -6
  68. package/dist/bin/cli.js +216 -216
  69. package/dist/bin/dashboard-server.js +1 -1
  70. package/dist/bin/stacks-queue-worker.js +1 -1
  71. package/dist/bin/stacks-scheduler.js +1 -1
  72. package/dist/chunk-0v77443p.js +1 -0
  73. package/dist/{chunk-z56sccfh.js → chunk-1087xz1v.js} +2 -2
  74. package/dist/{chunk-c8kf2268.js → chunk-1psh3mhm.js} +29 -30
  75. package/dist/chunk-4khjjgd3.js +1 -0
  76. package/dist/{chunk-4pztf7ws.js → chunk-4m5gpp6w.js} +5 -5
  77. package/dist/{chunk-3bvhthfb.js → chunk-72p2tdpe.js} +115 -117
  78. package/dist/chunk-7d9ek8rc.js +5 -0
  79. package/dist/chunk-872fdms3.js +1 -0
  80. package/dist/{chunk-cq079p4a.js → chunk-9f18pebj.js} +6 -6
  81. package/dist/{chunk-vq8bejep.js → chunk-a77mhcs7.js} +3 -3
  82. package/dist/chunk-bazah9w0.js +1 -0
  83. package/dist/{chunk-tmp2ym80.js → chunk-by7hejhg.js} +1 -1
  84. package/dist/{chunk-ax320pn8.js → chunk-byr76jcy.js} +19 -19
  85. package/dist/{chunk-m35z9vyn.js → chunk-c6kb5c5v.js} +4 -4
  86. package/dist/{chunk-gpgmp9dt.js → chunk-dmv3vryr.js} +10 -10
  87. package/dist/{chunk-5t92a838.js → chunk-fe6m7wb0.js} +45 -45
  88. package/dist/{chunk-py8dq1ey.js → chunk-fne338jr.js} +5 -5
  89. package/dist/{chunk-nftqsker.js → chunk-h6kptkce.js} +2 -2
  90. package/dist/{chunk-fdf181e6.js → chunk-hcbrgyfz.js} +2 -2
  91. package/dist/{chunk-9pwvnh3f.js → chunk-jz6dacx1.js} +2 -2
  92. package/dist/{chunk-fjs0wgyw.js → chunk-kcem6dzk.js} +2 -2
  93. package/dist/{chunk-7xy7bsyx.js → chunk-mvmxy0z7.js} +110 -110
  94. package/dist/{chunk-bvywngfv.js → chunk-n95wbpge.js} +136 -136
  95. package/dist/{chunk-4ccv5c4m.js → chunk-q7yf28cc.js} +1 -1
  96. package/dist/{chunk-jx1zz1pw.js → chunk-qra9k26f.js} +1 -1
  97. package/dist/chunk-qs7814ya.js +1 -0
  98. package/dist/chunk-sd5a61c6.js +1 -0
  99. package/dist/{bin/chunk-8rkfwgb4.js → chunk-sjfc9my3.js} +7 -7
  100. package/dist/{chunk-84z2a1xn.js → chunk-sxqe8e9c.js} +7 -7
  101. package/dist/{chunk-g0bh9atv.js → chunk-t1wq0ckc.js} +2 -2
  102. package/dist/{chunk-c71hhcsb.js → chunk-tpvmpmxj.js} +1 -1
  103. package/dist/{chunk-tr54e9c0.js → chunk-x7cnfn7y.js} +2 -2
  104. package/dist/{chunk-fpad8xw9.js → chunk-xs9m0v31.js} +4 -4
  105. package/dist/chunk-zft558kj.js +1 -0
  106. package/dist/{chunk-e75dvdqf.js → chunk-zwayp7fz.js} +2 -2
  107. package/dist/{chunk-gdw52mn4.js → chunk-zy3kr4s2.js} +3 -3
  108. package/dist/deploy/index.js +1 -1
  109. package/dist/dns/index.js +1 -1
  110. package/dist/drivers/index.js +1 -1
  111. package/dist/index.js +31 -31
  112. package/dist/mail/index.js +2 -2
  113. package/dist/operations/index.js +1 -1
  114. package/dist/protection/index.js +1 -1
  115. package/dist/spend/index.js +1 -1
  116. package/dist/ui/404.html +1 -1
  117. package/dist/ui/accept-invitation.html +1 -1
  118. package/dist/ui/access-denied.html +1 -1
  119. package/dist/ui/account/automation.html +6 -6
  120. package/dist/ui/account/security.html +2 -2
  121. package/dist/ui/applications/compose.html +6 -6
  122. package/dist/ui/applications/new.html +2 -2
  123. package/dist/ui/data/backups.html +5 -5
  124. package/dist/ui/data/services.html +6 -6
  125. package/dist/ui/data/volumes.html +6 -6
  126. package/dist/ui/index.html +6 -6
  127. package/dist/ui/infrastructure/topology.html +6 -6
  128. package/dist/ui/integrations.html +1 -1
  129. package/dist/ui/operations/alerts.html +6 -6
  130. package/dist/ui/operations/configuration.html +6 -6
  131. package/dist/ui/operations/jobs.html +6 -6
  132. package/dist/ui/operations/maintenance.html +5 -5
  133. package/dist/ui/operations/observability.html +5 -5
  134. package/dist/ui/operations/previews.html +5 -5
  135. package/dist/ui/operations/queue.html +5 -5
  136. package/dist/ui/operations/regions.html +5 -5
  137. package/dist/ui/operations/releases.html +5 -5
  138. package/dist/ui/operations/spend.html +6 -6
  139. package/dist/ui/operations/workloads.html +6 -6
  140. package/dist/ui/security.html +1 -1
  141. package/dist/ui/server/actions.html +6 -6
  142. package/dist/ui/server/activity.html +1 -1
  143. package/dist/ui/server/capacity.html +6 -6
  144. package/dist/ui/server/database.html +6 -6
  145. package/dist/ui/server/deployments.html +6 -6
  146. package/dist/ui/server/diagnostics.html +2 -2
  147. package/dist/ui/server/firewall.html +6 -6
  148. package/dist/ui/server/fleet.html +6 -6
  149. package/dist/ui/server/logs.html +6 -6
  150. package/dist/ui/server/metrics.html +6 -6
  151. package/dist/ui/server/security.html +2 -2
  152. package/dist/ui/server/services.html +1 -1
  153. package/dist/ui/server/sites.html +6 -6
  154. package/dist/ui/server/ssh-keys.html +6 -6
  155. package/dist/ui/server/team.html +5 -5
  156. package/dist/ui/server/terminal.html +2 -2
  157. package/dist/ui/serverless/alarms.html +6 -6
  158. package/dist/ui/serverless/assets.html +2 -2
  159. package/dist/ui/serverless/cost.html +2 -2
  160. package/dist/ui/serverless/data.html +6 -6
  161. package/dist/ui/serverless/deployments.html +2 -2
  162. package/dist/ui/serverless/firewall.html +2 -2
  163. package/dist/ui/serverless/functions.html +6 -6
  164. package/dist/ui/serverless/logs.html +6 -6
  165. package/dist/ui/serverless/metrics.html +2 -2
  166. package/dist/ui/serverless/queues.html +6 -6
  167. package/dist/ui/serverless/secrets.html +6 -6
  168. package/dist/ui/serverless/traces.html +6 -6
  169. package/dist/ui/serverless.html +5 -5
  170. package/package.json +3 -3
  171. package/dist/bin/chunk-1qez75tb.js +0 -1
  172. package/dist/bin/chunk-1rrmswhq.js +0 -1
  173. package/dist/bin/chunk-1vw6khps.js +0 -1
  174. package/dist/bin/chunk-2h1q758c.js +0 -2
  175. package/dist/bin/chunk-3rjer1mp.js +0 -1
  176. package/dist/bin/chunk-4k2x5x3k.js +0 -1
  177. package/dist/bin/chunk-5tzygzdy.js +0 -1
  178. package/dist/bin/chunk-6wvjr3ne.js +0 -1
  179. package/dist/bin/chunk-71fa7yed.js +0 -1
  180. package/dist/bin/chunk-7haewmr7.js +0 -1
  181. package/dist/bin/chunk-86w3vzx8.js +0 -1
  182. package/dist/bin/chunk-8tfqpp9m.js +0 -1
  183. package/dist/bin/chunk-9tt7ahdf.js +0 -1
  184. package/dist/bin/chunk-9zwvysdn.js +0 -1
  185. package/dist/bin/chunk-a66hyvc3.js +0 -1
  186. package/dist/bin/chunk-b9ersj22.js +0 -1
  187. package/dist/bin/chunk-btrxye8p.js +0 -6
  188. package/dist/bin/chunk-haezjam0.js +0 -1
  189. package/dist/bin/chunk-hp1cqk45.js +0 -1
  190. package/dist/bin/chunk-mwprtzxp.js +0 -1
  191. package/dist/bin/chunk-nemj07g1.js +0 -1
  192. package/dist/bin/chunk-nfxxr6g7.js +0 -1
  193. package/dist/bin/chunk-q3mtvtem.js +0 -1
  194. package/dist/bin/chunk-qpd51n1j.js +0 -1
  195. package/dist/bin/chunk-rtpc6djg.js +0 -1
  196. package/dist/bin/chunk-s9k81gq6.js +0 -1
  197. package/dist/bin/chunk-wrcdcg7m.js +0 -1
  198. package/dist/bin/chunk-ybekawxv.js +0 -1
  199. package/dist/chunk-03gf4e58.js +0 -1
  200. package/dist/chunk-86qwnp25.js +0 -1
  201. package/dist/chunk-8kn7rszy.js +0 -1
  202. package/dist/chunk-928z0h9b.js +0 -14
  203. package/dist/chunk-crpbs450.js +0 -1
  204. package/dist/chunk-m9szynnf.js +0 -6
  205. package/dist/chunk-ncet7ba7.js +0 -1
  206. package/dist/chunk-nh7rcks9.js +0 -1
  207. package/dist/chunk-ypsfkgme.js +0 -1
@@ -1,22 +1,22 @@
1
- import{Tt,xi,Oi,_i}from"./chunk-fpad8xw9.js";function S(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function rA(t,e="/var/www"){let n=`${e}/${t}-`;return["set -u",`for TS_CLOUD_DIR in ${n}*; do`,' [ -d "$TS_CLOUD_DIR" ] || continue',' [ -d "$TS_CLOUD_DIR/releases" ] || continue',` TS_CLOUD_SITE="\${TS_CLOUD_DIR#${n}}"`," TS_CLOUD_ACTIVE=no",` for TS_CLOUD_UNIT in $(ls /etc/systemd/system/ 2>/dev/null | grep -E "^${S(t)}-$TS_CLOUD_SITE[-@.]" || true); do`,' systemctl is-active "$TS_CLOUD_UNIT" >/dev/null 2>&1 && TS_CLOUD_ACTIVE=yes'," done",' TS_CLOUD_SIZE="$(du -sh "$TS_CLOUD_DIR" 2>/dev/null | cut -f1)"',' echo "site:$TS_CLOUD_SITE:$TS_CLOUD_ACTIVE:${TS_CLOUD_SIZE:-?}"',"done","exit 0"]}function nA(t){if(!t)return[];let e=[];for(let n of t.split(`
2
- `)){let r=n.trim();if(!r.startsWith("site:"))continue;let[,s,o,i]=r.split(":");if(!s||o!=="no")continue;e.push({name:s,size:i||"?"})}return e}function iA(t,e,n){let r=t.map((o)=>` ${e}-${o.name} (${o.size})`).join(`
1
+ import{wt,_i,Li,$i}from"./chunk-xs9m0v31.js";function S(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function gA(t,e="/var/www"){let n=`${e}/${t}-`;return["set -u",`for TS_CLOUD_DIR in ${n}*; do`,' [ -d "$TS_CLOUD_DIR" ] || continue',' [ -d "$TS_CLOUD_DIR/releases" ] || continue',` TS_CLOUD_SITE="\${TS_CLOUD_DIR#${n}}"`," TS_CLOUD_ACTIVE=no",` for TS_CLOUD_UNIT in $(ls /etc/systemd/system/ 2>/dev/null | grep -E "^${S(t)}-$TS_CLOUD_SITE[-@.]" || true); do`,' systemctl is-active "$TS_CLOUD_UNIT" >/dev/null 2>&1 && TS_CLOUD_ACTIVE=yes'," done",' TS_CLOUD_SIZE="$(du -sh "$TS_CLOUD_DIR" 2>/dev/null | cut -f1)"',' echo "site:$TS_CLOUD_SITE:$TS_CLOUD_ACTIVE:${TS_CLOUD_SIZE:-?}"',"done","exit 0"]}function pA(t){if(!t)return[];let e=[];for(let n of t.split(`
2
+ `)){let r=n.trim();if(!r.startsWith("site:"))continue;let[,s,o,i]=r.split(":");if(!s||o!=="no")continue;e.push({name:s,size:i||"?"})}return e}function mA(t,e,n){let r=t.map((o)=>` ${e}-${o.name} (${o.size})`).join(`
3
3
  `),s=t.length===1?"it":"them";return`This server still holds ${t.length} site tree${t.length===1?"":"s"} with nothing running for ${s}:
4
4
  ${r}
5
5
  `+"A site moved off this box keeps its files here on purpose — they are the rollback, and they are what makes "+`the move reversible until this server is destroyed. Destroying it now discards that.
6
- Verify the site is serving from its new home first, then re-run with ${n} to destroy it anyway.`}var Rp="ts-cloud/project",Dp="ts-cloud/environment",Pp="ts-cloud/role";function d(t){return typeof t==="string"&&t.trim()?t.trim():void 0}function h(t){if(typeof t==="string")return d(t);if(t&&typeof t==="object")return d(t.name);return}function sA(t){let e={};for(let[n,r]of Object.entries(t?.labels??{}))if(typeof r==="string")e[n]=r;return{id:String(t?.id??""),name:d(t?.name)??"(unnamed)",status:d(t?.status)??"unknown",ipv4:d(t?.public_net?.ipv4?.ip)??d(t?.ipv4),ipv6:d(t?.public_net?.ipv6?.ip)??d(t?.ipv6),type:d(t?.server_type?.name)??d(t?.type),location:h(t?.location)??h(t?.datacenter?.location)??h(t?.datacenter),labels:e,project:d(e[Rp]),environment:d(e[Dp]),role:d(e[Pp])}}function w(t){return t.slug?.trim()||"app"}function kp(t){let e=[];for(let n of t){let r=w(n);for(let s of n.proxies??[]){let o=d(s?.to);if(!o)continue;e.push({slug:r,host:o,path:d(s?.path)??"/",...x(s)})}}return e.sort((n,r)=>n.slug.localeCompare(r.slug)||n.host.localeCompare(r.host)||n.path.localeCompare(r.path))}function x(t){let e=t?.from;if(typeof e==="string"&&e.trim())return{target:e.trim(),kind:"app"};if(Array.isArray(e)&&e.length>0)return{target:e.filter((s)=>typeof s==="string").join(", "),kind:"app"};let n=t?.static;if(typeof n==="string"&&n.trim())return{target:n.trim(),kind:"static"};if(n&&typeof n==="object"&&d(n.dir))return{target:d(n.dir),kind:"static"};let r=typeof t?.redirect==="string"?d(t.redirect):d(t?.redirect?.to);if(r)return{target:r,kind:"redirect"};return{target:"(no upstream)",kind:"unknown"}}function Np(t){let e=new Map;for(let n of t){let r=e.get(n.slug);if(r)r.push(n);else e.set(n.slug,[n])}return[...e.entries()].map(([n,r])=>({slug:n,routes:r})).sort((n,r)=>r.routes.length-n.routes.length||n.slug.localeCompare(r.slug))}async function oA(t,e,n=Tt){if(!t.ipv4)return{server:t.name,routes:[],unavailable:"no public IPv4 address to reach it on"};if(t.status!=="running")return{server:t.name,ip:t.ipv4,routes:[],unavailable:`server is ${t.status}`};try{let r=await e(t.ipv4,Oi(n));if(r.code!==0){let s=r.stderr.trim().split(`
7
- `)[0]||`remote command exited ${r.code}`;return{server:t.name,ip:t.ipv4,routes:[],unavailable:s}}return{server:t.name,ip:t.ipv4,routes:kp(_i(r.stdout))}}catch(r){let s=r instanceof Error?r.message.split(`
8
- `)[0]:String(r);return{server:t.name,ip:t.ipv4,routes:[],unavailable:s}}}function u(t,e){let n=e==="/"?"/":e.replace(/\/+$/,"");return`${t.toLowerCase()}${n||"/"}`}function aA(t,e,n){let r=new Set(e.filter((a)=>a.slug===n).map((a)=>u(a.host,a.path))),s=[],o=[],i=[];for(let a of t)if(a.loopbackOnly)i.push(a);else if(a.domain&&r.has(u(a.domain,a.path)))s.push(a);else o.push(a);return{present:s,absent:o,loopback:i,foreign:e.filter((a)=>a.slug!==n)}}function Bp(t,e,n){let r=new Set;for(let s of e)for(let o of s.routes)if(o.slug===n)r.add(u(o.host,o.path));return t.filter((s)=>!s.loopbackOnly&&s.domain!==void 0&&!r.has(u(s.domain,s.path)))}function cA(t){let{slug:e,servers:n,probes:r,declared:s}=t,o=[];if(o.push(n.length===0?"No servers found.":`${n.length} server${n.length===1?"":"s"}:`),n.length>0)o.push("");let i=new Map(r.map((a)=>[a.server,a]));for(let a of n){let p=[a.ipv4,a.type,a.location,a.status].filter(Boolean);o.push(` ${a.name} ${p.join(" ")}`),o.push(` ${k(a)}`);let c=i.get(a.name);if(!c)o.push(" not probed, so co-tenants on this box are not listed");else if(c.unavailable)o.push(` could not read ${Tt}: ${c.unavailable}`);else o.push(...P(c,e));o.push("")}return o.push(...C(t)),o}function k(t){if(!t.project)return"no ts-cloud labels: provisioned outside ts-cloud, or by a version that did not label boxes";let e=[t.environment,t.role&&`role ${t.role}`].filter(Boolean).join(", ");return`owned by '${t.project}'${e?` (${e})`:""}`}function P(t,e){let n=Np(t.routes);if(n.length===0)return[` serves nothing: ${Tt} is empty or absent`];let r=[` serves ${t.routes.length} route${t.routes.length===1?"":"s"} for ${n.length} project${n.length===1?"":"s"}:`];for(let s of n){r.push(` ${s.slug}${s.slug===e?" (this project)":""}`);for(let o of s.routes)r.push(` ${o.host}${o.path==="/"?"/":o.path} -> ${O(o)}`)}return r}function O(t){if(t.kind==="redirect")return`redirect to ${t.target}`;if(t.kind==="static")return`static ${t.target}`;return t.target}function C(t){let{slug:e,declared:n,probes:r,servers:s}=t;if(n.length===0)return[`This project ('${e}') declares no sites.`];let o=[`This project ('${e}') declares ${n.length} site${n.length===1?"":"s"}: `+n.map((c)=>c.name).join(", ")],i=n.filter((c)=>c.loopbackOnly);if(i.length>0)o.push(` ${i.length} with no domain, so the gateway never routes ${i.length===1?"it":"them"} (reached through another site's proxy): ${i.map((c)=>c.name).join(", ")}`);let a=r.filter((c)=>!c.unavailable);if(a.length===0)return o.push(" Nothing to reconcile them against: no box reported what it serves."),o;let p=Bp(n,a,e);if(o.push(` ${n.length-i.length-p.length} routed by a box above`),p.length>0){o.push(` ${p.length} not routed by any box above: ${p.map((b)=>b.name).join(", ")}`);let c=r.length-a.length,g=s.length-r.length;if(c>0)o.push(` Either they were never deployed, or they are on one of the ${c} server${c===1?"":"s"} that could not be read.`);else if(g>0)o.push(` Either they were never deployed, or they are on one of the ${g} server${g===1?"":"s"} this run did not probe.`);else o.push(" Either they were never deployed, or they are on a server this listing did not cover.")}return o}async function dA(t){let e=[];for(let n of t.steps)try{e.push({step:n,state:await n.satisfied()?"satisfied":"pending"})}catch(r){e.push({step:n,state:"unknown",reason:r instanceof Error?r.message:String(r)})}return e}function md(t){return t.filter((e)=>e.state!=="satisfied")}function xp(t){return md(t).some((e)=>e.step.destructive===!0)}function lA(t,e){let n=md(e),r=[`${t.operation} ${t.target}`];if(n.length===0)return r.push(" Nothing to do — every step is already satisfied."),r;for(let{step:o,state:i,reason:a}of e){let p=i==="satisfied"?"ok ":i==="unknown"?"? ":"→ ",c=[o.destructive?"DESTRUCTIVE":"",i==="satisfied"?"already done":""].filter(Boolean);if(r.push(` ${p}${o.title}${c.length>0?` [${c.join(", ")}]`:""}`),o.change)r.push(` ${o.change.from} → ${o.change.to}`);if(a)r.push(` could not check: ${a}`)}let s=n.filter((o)=>o.step.destructive).length;return r.push(` ${n.length} step(s) would run`+(s>0?`, ${s} irreversible — re-run with --confirm ${t.target}`:"")),r}async function uA(t,e,n={}){let r={operation:t.operation,target:t.target,steps:[],success:!0};if(xp(e)&&n.confirm!==t.target)throw Error(`${t.operation} includes an irreversible step. Re-run with --confirm ${t.target} to authorize it.`);for(let{step:s,state:o}of e){if(o==="satisfied"){r.steps.push({id:s.id,title:s.title,state:"skipped"}),n.log?.(`skip ${s.title} (already done)`);continue}n.audit?.({operation:t.operation,target:t.target,step:s.id,state:"started"});try{await s.apply(),r.steps.push({id:s.id,title:s.title,state:"applied"}),n.log?.(`done ${s.title}`),n.audit?.({operation:t.operation,target:t.target,step:s.id,state:"applied"})}catch(i){let a=i instanceof Error?i.message:String(i);return r.steps.push({id:s.id,title:s.title,state:"failed",error:a}),r.success=!1,n.log?.(`FAIL ${s.title}: ${a}`),n.audit?.({operation:t.operation,target:t.target,step:s.id,state:"failed",error:a}),r}}return r}function Op(t){if(t.length===0)throw Error("A server name cannot be empty.");if(t.length>253)throw Error(`'${t}' is longer than the 253 characters a hostname allows.`);for(let e of t.split(".")){if(e.length===0)throw Error(`'${t}' 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(t,e,n){if(Op(e),t===e)throw Error(`'${t}' is already named that.`);if((await n.takenNames()).some((o)=>o===e&&o!==t))throw Error(`'${e}' is already taken. Server names have to be unique within a provider project.`);let s=[];if(n.providerName&&n.renameProvider){let{providerName:o,renameProvider:i}=n;s.push({id:"provider",title:"Rename the server at the provider",change:{from:t,to:e},satisfied:async()=>await o()===e,apply:()=>i(e)})}if(n.stateName&&n.writeStateName){let{stateName:o,writeStateName:i}=n;s.push({id:"state-pin",title:"Update the recorded name in the local driver state",change:{from:t,to:e},satisfied:async()=>{let a=await o();return a===void 0||a===e},apply:()=>i(e)})}if(n.remoteHostname&&n.setRemoteHostname){let{remoteHostname:o,setRemoteHostname:i}=n;s.push({id:"hostname",title:"Set the hostname on the box",change:{from:t,to:e},satisfied:async()=>await o()===e,apply:()=>i(e)})}return s.push({id:"inventory",title:"Rename the fleet inventory record",change:{from:t,to:e},satisfied:async()=>n.inventoryName()===e,apply:async()=>{await n.renameInventory(e)}}),{operation:"server:rename",target:t,steps:s}}function pA(t){let e=`'${t.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',` TS_CLOUD_HOSTS="$(sed -E "s/^127\\.0\\.1\\.1.*/127.0.1.1\\t${t}/" /etc/hosts)"`,` printf '%s\\n' "$TS_CLOUD_HOSTS" > /etc/hosts`,"else",` printf '127.0.1.1\\t%s\\n' ${e} >> /etc/hosts`,"fi",'printf "%s -> %s\\n" "$TS_CLOUD_OLD" "$(hostname)"'].join(`
9
- `)}function mA(t,e,n){let r=e.trim();if(!r)return{problem:"No server named."};let[s,...o]=t.filter((c)=>c.name===r);if(s&&o.length===0)return{server:s};let i=t.filter((c)=>c.project===r);if(i.length>1&&n)i=i.filter((c)=>!c.environment||c.environment===n);let[a,...p]=i;if(a&&p.length===0)return{server:a};if(i.length>1)return{problem:`'${r}' owns ${i.length} servers (${i.map((c)=>c.name).join(", ")}). Name one of them, or narrow it by environment.`};return{problem:`No server matched '${r}'. Nothing is named that, and no box carries the label ts-cloud/project=${r}.`}}function fA(t,e){let n=[];if(!e.project)n.push(`'${e.name}' carries no ts-cloud/project label, so it is not a box ts-cloud provisioned. Attaching to it would deploy into a host nothing manages.`);else if(e.project===t)n.push(`This project's slug is '${t}', which is also the slug that owns '${e.name}'. A tenant deploy owns the gateway fragment named after its slug, so attaching would overwrite '${e.project}'s own fragment and take its sites down. Change this project's slug first.`);if(e.status!=="running")n.push(`'${e.name}' is ${e.status}, so what it serves could not be read.`);if(!e.ipv4)n.push(`'${e.name}' has no public IPv4 address, so it cannot be reached to check what it serves.`);return n}function f(t,e){let n=e==="/"?"/":e.replace(/\/+$/,"");return`${t.toLowerCase()}${n||"/"}`}function hA(t,e,n){let r=[],s=xi(n.filter((i)=>i.kind==="app").map((i)=>({slug:i.slug,proxies:[{from:i.target.split(",").map((a)=>a.trim())}]})),{ignoreSlug:t}),o=new Map;for(let i of n)if(i.slug!==t)o.set(f(i.host,i.path),i.slug);for(let i of e){if(i.port!==void 0){let a=s.get(i.port);if(a)r.push({kind:"port",site:i.name,detail:`port ${i.port}`,heldBy:a})}if(i.domain){let a=o.get(f(i.domain,i.path));if(a)r.push({kind:"route",site:i.name,detail:`${i.domain}${i.path==="/"?"/":i.path}`,heldBy:a})}}return r}function AA(t){return t.registryRead&&t.conflicts.length===0}function yA(t){let{slug:e,owner:n,server:r,declared:s,conflicts:o}=t,i=[];i.push(`Attach '${e}' to '${r.name}' (${r.ipv4??"no IPv4"}), owned by '${n}'.`,""),i.push(` ${s.length} site${s.length===1?"":"s"} would deploy onto this box:`);for(let a of s){let p=a.loopbackOnly?`loopback only${a.port?` on :${a.port}`:""}`:`${a.domain}${a.path==="/"?"/":a.path}${a.port?` on :${a.port}`:""}`;i.push(` ${a.name} ${p} -> ${a.installBase??"(install path unresolved)"}`)}if(i.push(""),!t.registryRead)i.push(` Could not read what '${r.name}' already serves: ${t.registryProblem??"unknown reason"}`),i.push(" So this attach is UNCHECKED: a port or hostname already taken by another"),i.push(" project would not error, it would serve that project's site from your domain.");else if(o.length>0){i.push(` ${o.length} conflict${o.length===1?"":"s"} with what the box already serves:`);for(let a of o)i.push(` site '${a.site}' wants ${a.detail}, held by '${a.heldBy}'`);i.push(""),i.push(" Two services on one port do not error: the kernel load-balances, and each"),i.push(" domain serves the other's site about half the time. Pick free ports and"),i.push(" hostnames, then re-run.")}else i.push(` No conflicts with what '${r.name}' already serves.`);return i}function Bi(t,e){return`/tmp/ts-cloud-move-${t}-${e}.tar.gz`}function fd(t,e){return`${t}-${e}*.service`}function l(t){return`'${t.split("'").join("'\\''")}'`}function _p(t,e){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${l(m(t,e))} || true); do`,' systemctl stop "$unit" 2>/dev/null || true',"done"].join(`
10
- `)}function m(t,e){return`^${t}-${e}-((queue|daemon)-.*|scheduler)\\.service$`}function Lp(t,e){return y(m(t,e))}function y(t){return[`ls /etc/systemd/system/ 2>/dev/null | grep -E ${l(t)} | while read -r unit; do`,' systemctl is-active "$unit" >/dev/null 2>&1 && echo "active:$unit" || true',"done"].join(`
11
- `)}function $p(t){return!t.split(`
12
- `).some((e)=>e.trim().startsWith("active:"))}function Mp(t,e,n){let r=Bi(t,e);return["set -eu",`test -d ${l(n)} || { echo "no site tree at ${n}" >&2; exit 1; }`,`rm -f ${l(r)}`,`tar czf ${l(r)} -C "$(dirname ${l(n)})" "$(basename ${l(n)})" -C /etc/systemd/system $(cd /etc/systemd/system && ls ${fd(t,e)} 2>/dev/null | tr '\\n' ' ')`,`sha256sum ${l(r)} | cut -d' ' -f1`].join(`
13
- `)}function Fp(t,e,n){let r=Bi(t,e);return["set -eu",`test -f ${l(r)} || { echo "archive not staged at ${r}" >&2; exit 1; }`,`mkdir -p "$(dirname ${l(n)})"`,`tar xzf ${l(r)} -C "$(dirname ${l(n)})" "$(basename ${l(n)})"`,`tar xzf ${l(r)} -C /etc/systemd/system --wildcards ${l(fd(t,e))} 2>/dev/null || true`,"systemctl daemon-reload",`systemctl enable ${t}-${e}.service 2>/dev/null || true`,`systemctl restart ${t}-${e}.service`,`rm -f ${l(r)}`].join(`
14
- `)}function Up(t,e,n){return[`test -d ${l(n)} && echo tree:present || echo tree:absent`,`systemctl is-active ${t}-${e}.service >/dev/null 2>&1 && echo unit:active || echo unit:inactive`].join(`
15
- `)}function Qp(t){return t.includes("tree:present")&&t.includes("unit:active")}function Hp(t,e="/",n=10){let r=`http://127.0.0.1:${t}${e.startsWith("/")?e:`/${e}`}`;return["set -eu",`for i in $(seq 1 ${n}); do`,` if curl -fsS -o /dev/null --max-time 5 ${l(r)}; then echo healthy; exit 0; fi`," sleep 3","done",`echo "no healthy response from ${r}" >&2`,"exit 1"].join(`
16
- `)}function Gp(t,e){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${l(v(t,e))} || true); do`,' systemctl disable --now "$unit" 2>/dev/null || true',"done",`rm -f /etc/rpx/sites.d/${t}.json 2>/dev/null || true`,"systemctl reload rpx-gateway.service 2>/dev/null || systemctl restart rpx-gateway.service 2>/dev/null || true"].join(`
17
- `)}function v(t,e){return`^${t}-${e}[-@.]`}function Vp(t,e){return y(v(t,e))}function qp(t){return!t.split(`
18
- `).some((e)=>e.trim().startsWith("active:"))}function SA(t,e){return`/tmp/ts-cloud-move-${t}-${e}-certs.tar.gz`}function IA(t,e){return["set -u",...e.map((n)=>`if [ -s ${l(`${t}/${n}.crt`)} ]; then echo "cert:${n}:$(sha256sum ${l(`${t}/${n}.crt`)} | cut -d' ' -f1)"; else echo "cert:${n}:absent"; fi`)].join(`
19
- `)}function bA(t){let e=new Map;for(let n of t.split(`
20
- `)){let r=n.trim();if(!r.startsWith("cert:"))continue;let[,s,o]=r.split(":");if(s&&o)e.set(s,o)}return e}function CA(t,e){for(let[n,r]of t){if(r==="absent")continue;if(e.get(n)!==r)return!1}return!0}function EA(t,e,n){let r=e.flatMap((s)=>[`${s}.crt`,`${s}.key`]);return["set -eu",`rm -f ${l(n)}`,`cd ${l(t)} 2>/dev/null || { echo "no certs dir at ${t}" >&2; exit 0; }`,`TS_CLOUD_CERTS="$(ls ${r.map(l).join(" ")} 2>/dev/null | tr '\\n' ' ' || true)"`,'[ -n "$TS_CLOUD_CERTS" ] || { echo "no certificates to carry"; exit 0; }',`tar czf ${l(n)} $TS_CLOUD_CERTS`].join(`
21
- `)}function vA(t,e){return["set -eu",`[ -s ${l(e)} ] || { echo "no certificate archive staged"; exit 0; }`,`mkdir -p ${l(t)}`,`tar xzf ${l(e)} -C ${l(t)}`,`chmod 600 ${l(t)}/*.key 2>/dev/null || true`,`rm -f ${l(e)}`].join(`
22
- `)}async function TA(t,e){let{slug:n,siteName:r,appBase:s,from:o,to:i,targetAddress:a}=t;if(o===i)throw Error(`'${r}' is already on ${i}.`);if(t.database&&!e.database)throw Error(`'${r}' uses the on-box database '${t.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(t.database&&e.database&&await e.database.targetHasData())throw Error(`${i} already has a database named '${t.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 p=[{id:"pause-workers",title:`Stop background work on ${o} so the snapshot is consistent`,satisfied:async()=>$p(await e.runOnSource(Lp(n,r))),apply:async()=>{await e.runOnSource(_p(n,r))}},...e.database?[{id:"database-dump",title:`Dump the '${t.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}:${s}`,to:Bi(n,r)},satisfied:async()=>!1,apply:async()=>{await e.runOnSource(Mp(n,r,s))}},{id:"transfer",title:`Carry the archive to ${i}`,change:{from:o,to:i},satisfied:()=>e.archiveStaged(),apply:()=>e.transferArchive()},...e.database?[{id:"database-transfer",title:`Carry the database dump to ${i}`,change:{from:o,to:i},satisfied:()=>e.database.dumpStaged(),apply:()=>e.database.transferDump()},{id:"database-restore",title:`Create and load '${t.database?.name}' on ${i}`,satisfied:async()=>!1,apply:()=>e.database.restore()}]:[],{id:"restore",title:`Unpack the site on ${i} and start it`,change:{from:Bi(n,r),to:`${i}:${s}`},satisfied:async()=>Qp(await e.runOnTarget(Up(n,r,s))),apply:async()=>{await e.runOnTarget(Fp(n,r,s))}}];if(t.port!=null){let{port:c}=t;p.push({id:"health",title:`Wait for the site to answer on ${i} (127.0.0.1:${c})`,satisfied:async()=>!1,apply:async()=>{await e.runOnTarget(Hp(c,t.healthCheckPath??"/"))}})}return p.push(...e.certificates?[{id:"certificates",title:`Carry the site's TLS material to ${i}`,satisfied:()=>e.certificates.inPlace(),apply:()=>e.certificates.carry()}]:[],{id:"gateway",title:`Route the site on ${i}`,satisfied:()=>e.targetRoutesSite(),apply:()=>e.refreshTargetGateway()},{id:"dns",title:`Point DNS at ${i}`,change:{from:`${o}`,to:a},satisfied:async()=>await e.publishedAddress()===a,apply:async()=>{let c=await e.cutoverDns();if(c.length>0)throw Error(c.join("; "))}},{id:"drain-source",title:`Stop the site on ${o}, leaving its files in place`,satisfied:async()=>qp(await e.runOnSource(Vp(n,r))),apply:async()=>{await e.runOnSource(Gp(n,r))}}),{operation:"site:move",target:r,steps:p}}export{rA,nA,iA,Rp,Dp,Pp,sA,kp,Np,oA,aA,Bp,cA,dA,md,xp,lA,uA,Op,gA,pA,mA,fA,hA,AA,yA,Bi,fd,_p,Lp,$p,Mp,Fp,Up,Qp,Hp,Gp,Vp,qp,SA,IA,bA,CA,EA,vA,TA};
6
+ Verify the site is serving from its new home first, then re-run with ${n} to destroy it anyway.`}var Lp="ts-cloud/project",$p="ts-cloud/environment",Mp="ts-cloud/role";function d(t){return typeof t==="string"&&t.trim()?t.trim():void 0}function h(t){if(typeof t==="string")return d(t);if(t&&typeof t==="object")return d(t.name);return}function fA(t){let e={};for(let[n,r]of Object.entries(t?.labels??{}))if(typeof r==="string")e[n]=r;return{id:String(t?.id??""),name:d(t?.name)??"(unnamed)",status:d(t?.status)??"unknown",ipv4:d(t?.public_net?.ipv4?.ip)??d(t?.ipv4),ipv6:d(t?.public_net?.ipv6?.ip)??d(t?.ipv6),type:d(t?.server_type?.name)??d(t?.type),location:h(t?.location)??h(t?.datacenter?.location)??h(t?.datacenter),labels:e,project:d(e[Lp]),environment:d(e[$p]),role:d(e[Mp])}}function w(t){return t.slug?.trim()||"app"}function Fp(t){let e=[];for(let n of t){let r=w(n);for(let s of n.proxies??[]){let o=d(s?.to);if(!o)continue;e.push({slug:r,host:o,path:d(s?.path)??"/",...x(s)})}}return e.sort((n,r)=>n.slug.localeCompare(r.slug)||n.host.localeCompare(r.host)||n.path.localeCompare(r.path))}function x(t){let e=t?.from;if(typeof e==="string"&&e.trim())return{target:e.trim(),kind:"app"};if(Array.isArray(e)&&e.length>0)return{target:e.filter((s)=>typeof s==="string").join(", "),kind:"app"};let n=t?.static;if(typeof n==="string"&&n.trim())return{target:n.trim(),kind:"static"};if(n&&typeof n==="object"&&d(n.dir))return{target:d(n.dir),kind:"static"};let r=typeof t?.redirect==="string"?d(t.redirect):d(t?.redirect?.to);if(r)return{target:r,kind:"redirect"};return{target:"(no upstream)",kind:"unknown"}}function Up(t){let e=new Map;for(let n of t){let r=e.get(n.slug);if(r)r.push(n);else e.set(n.slug,[n])}return[...e.entries()].map(([n,r])=>({slug:n,routes:r})).sort((n,r)=>r.routes.length-n.routes.length||n.slug.localeCompare(r.slug))}async function hA(t,e,n=wt){if(!t.ipv4)return{server:t.name,routes:[],unavailable:"no public IPv4 address to reach it on"};if(t.status!=="running")return{server:t.name,ip:t.ipv4,routes:[],unavailable:`server is ${t.status}`};try{let r=await e(t.ipv4,Li(n));if(r.code!==0){let s=r.stderr.trim().split(`
7
+ `)[0]||`remote command exited ${r.code}`;return{server:t.name,ip:t.ipv4,routes:[],unavailable:s}}return{server:t.name,ip:t.ipv4,routes:Fp($i(r.stdout))}}catch(r){let s=r instanceof Error?r.message.split(`
8
+ `)[0]:String(r);return{server:t.name,ip:t.ipv4,routes:[],unavailable:s}}}function u(t,e){let n=e==="/"?"/":e.replace(/\/+$/,"");return`${t.toLowerCase()}${n||"/"}`}function AA(t,e,n){let r=new Set(e.filter((a)=>a.slug===n).map((a)=>u(a.host,a.path))),s=[],o=[],i=[];for(let a of t)if(a.loopbackOnly)i.push(a);else if(a.domain&&r.has(u(a.domain,a.path)))s.push(a);else o.push(a);return{present:s,absent:o,loopback:i,foreign:e.filter((a)=>a.slug!==n)}}function Qp(t,e,n){let r=new Set;for(let s of e)for(let o of s.routes)if(o.slug===n)r.add(u(o.host,o.path));return t.filter((s)=>!s.loopbackOnly&&s.domain!==void 0&&!r.has(u(s.domain,s.path)))}function yA(t){let{slug:e,servers:n,probes:r,declared:s}=t,o=[];if(o.push(n.length===0?"No servers found.":`${n.length} server${n.length===1?"":"s"}:`),n.length>0)o.push("");let i=new Map(r.map((a)=>[a.server,a]));for(let a of n){let p=[a.ipv4,a.type,a.location,a.status].filter(Boolean);o.push(` ${a.name} ${p.join(" ")}`),o.push(` ${k(a)}`);let c=i.get(a.name);if(!c)o.push(" not probed, so co-tenants on this box are not listed");else if(c.unavailable)o.push(` could not read ${wt}: ${c.unavailable}`);else o.push(...P(c,e));o.push("")}return o.push(...C(t)),o}function k(t){if(!t.project)return"no ts-cloud labels: provisioned outside ts-cloud, or by a version that did not label boxes";let e=[t.environment,t.role&&`role ${t.role}`].filter(Boolean).join(", ");return`owned by '${t.project}'${e?` (${e})`:""}`}function P(t,e){let n=Up(t.routes);if(n.length===0)return[` serves nothing: ${wt} is empty or absent`];let r=[` serves ${t.routes.length} route${t.routes.length===1?"":"s"} for ${n.length} project${n.length===1?"":"s"}:`];for(let s of n){r.push(` ${s.slug}${s.slug===e?" (this project)":""}`);for(let o of s.routes)r.push(` ${o.host}${o.path==="/"?"/":o.path} -> ${O(o)}`)}return r}function O(t){if(t.kind==="redirect")return`redirect to ${t.target}`;if(t.kind==="static")return`static ${t.target}`;return t.target}function C(t){let{slug:e,declared:n,probes:r,servers:s}=t;if(n.length===0)return[`This project ('${e}') declares no sites.`];let o=[`This project ('${e}') declares ${n.length} site${n.length===1?"":"s"}: `+n.map((c)=>c.name).join(", ")],i=n.filter((c)=>c.loopbackOnly);if(i.length>0)o.push(` ${i.length} with no domain, so the gateway never routes ${i.length===1?"it":"them"} (reached through another site's proxy): ${i.map((c)=>c.name).join(", ")}`);let a=r.filter((c)=>!c.unavailable);if(a.length===0)return o.push(" Nothing to reconcile them against: no box reported what it serves."),o;let p=Qp(n,a,e);if(o.push(` ${n.length-i.length-p.length} routed by a box above`),p.length>0){o.push(` ${p.length} not routed by any box above: ${p.map((b)=>b.name).join(", ")}`);let c=r.length-a.length,g=s.length-r.length;if(c>0)o.push(` Either they were never deployed, or they are on one of the ${c} server${c===1?"":"s"} that could not be read.`);else if(g>0)o.push(` Either they were never deployed, or they are on one of the ${g} server${g===1?"":"s"} this run did not probe.`);else o.push(" Either they were never deployed, or they are on a server this listing did not cover.")}return o}async function SA(t){let e=[];for(let n of t.steps)try{e.push({step:n,state:await n.satisfied()?"satisfied":"pending"})}catch(r){e.push({step:n,state:"unknown",reason:r instanceof Error?r.message:String(r)})}return e}function Ed(t){return t.filter((e)=>e.state!=="satisfied")}function Hp(t){return Ed(t).some((e)=>e.step.destructive===!0)}function IA(t,e){let n=Ed(e),r=[`${t.operation} ${t.target}`];if(n.length===0)return r.push(" Nothing to do — every step is already satisfied."),r;for(let{step:o,state:i,reason:a}of e){let p=i==="satisfied"?"ok ":i==="unknown"?"? ":"→ ",c=[o.destructive?"DESTRUCTIVE":"",i==="satisfied"?"already done":""].filter(Boolean);if(r.push(` ${p}${o.title}${c.length>0?` [${c.join(", ")}]`:""}`),o.change)r.push(` ${o.change.from} → ${o.change.to}`);if(a)r.push(` could not check: ${a}`)}let s=n.filter((o)=>o.step.destructive).length;return r.push(` ${n.length} step(s) would run`+(s>0?`, ${s} irreversible — re-run with --confirm ${t.target}`:"")),r}async function bA(t,e,n={}){let r={operation:t.operation,target:t.target,steps:[],success:!0};if(Hp(e)&&n.confirm!==t.target)throw Error(`${t.operation} includes an irreversible step. Re-run with --confirm ${t.target} to authorize it.`);for(let{step:s,state:o}of e){if(o==="satisfied"){r.steps.push({id:s.id,title:s.title,state:"skipped"}),n.log?.(`skip ${s.title} (already done)`);continue}n.audit?.({operation:t.operation,target:t.target,step:s.id,state:"started"});try{await s.apply(),r.steps.push({id:s.id,title:s.title,state:"applied"}),n.log?.(`done ${s.title}`),n.audit?.({operation:t.operation,target:t.target,step:s.id,state:"applied"})}catch(i){let a=i instanceof Error?i.message:String(i);return r.steps.push({id:s.id,title:s.title,state:"failed",error:a}),r.success=!1,n.log?.(`FAIL ${s.title}: ${a}`),n.audit?.({operation:t.operation,target:t.target,step:s.id,state:"failed",error:a}),r}}return r}function Gp(t){if(t.length===0)throw Error("A server name cannot be empty.");if(t.length>253)throw Error(`'${t}' is longer than the 253 characters a hostname allows.`);for(let e of t.split(".")){if(e.length===0)throw Error(`'${t}' 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 CA(t,e,n){if(Gp(e),t===e)throw Error(`'${t}' is already named that.`);if((await n.takenNames()).some((o)=>o===e&&o!==t))throw Error(`'${e}' is already taken. Server names have to be unique within a provider project.`);let s=[];if(n.providerName&&n.renameProvider){let{providerName:o,renameProvider:i}=n;s.push({id:"provider",title:"Rename the server at the provider",change:{from:t,to:e},satisfied:async()=>await o()===e,apply:()=>i(e)})}if(n.stateName&&n.writeStateName){let{stateName:o,writeStateName:i}=n;s.push({id:"state-pin",title:"Update the recorded name in the local driver state",change:{from:t,to:e},satisfied:async()=>{let a=await o();return a===void 0||a===e},apply:()=>i(e)})}if(n.remoteHostname&&n.setRemoteHostname){let{remoteHostname:o,setRemoteHostname:i}=n;s.push({id:"hostname",title:"Set the hostname on the box",change:{from:t,to:e},satisfied:async()=>await o()===e,apply:()=>i(e)})}return s.push({id:"inventory",title:"Rename the fleet inventory record",change:{from:t,to:e},satisfied:async()=>n.inventoryName()===e,apply:async()=>{await n.renameInventory(e)}}),{operation:"server:rename",target:t,steps:s}}function EA(t){let e=`'${t.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',` TS_CLOUD_HOSTS="$(sed -E "s/^127\\.0\\.1\\.1.*/127.0.1.1\\t${t}/" /etc/hosts)"`,` printf '%s\\n' "$TS_CLOUD_HOSTS" > /etc/hosts`,"else",` printf '127.0.1.1\\t%s\\n' ${e} >> /etc/hosts`,"fi",'printf "%s -> %s\\n" "$TS_CLOUD_OLD" "$(hostname)"'].join(`
9
+ `)}function vA(t,e,n){let r=e.trim();if(!r)return{problem:"No server named."};let[s,...o]=t.filter((c)=>c.name===r);if(s&&o.length===0)return{server:s};let i=t.filter((c)=>c.project===r);if(i.length>1&&n)i=i.filter((c)=>!c.environment||c.environment===n);let[a,...p]=i;if(a&&p.length===0)return{server:a};if(i.length>1)return{problem:`'${r}' owns ${i.length} servers (${i.map((c)=>c.name).join(", ")}). Name one of them, or narrow it by environment.`};return{problem:`No server matched '${r}'. Nothing is named that, and no box carries the label ts-cloud/project=${r}.`}}function TA(t,e){let n=[];if(!e.project)n.push(`'${e.name}' carries no ts-cloud/project label, so it is not a box ts-cloud provisioned. Attaching to it would deploy into a host nothing manages.`);else if(e.project===t)n.push(`This project's slug is '${t}', which is also the slug that owns '${e.name}'. A tenant deploy owns the gateway fragment named after its slug, so attaching would overwrite '${e.project}'s own fragment and take its sites down. Change this project's slug first.`);if(e.status!=="running")n.push(`'${e.name}' is ${e.status}, so what it serves could not be read.`);if(!e.ipv4)n.push(`'${e.name}' has no public IPv4 address, so it cannot be reached to check what it serves.`);return n}function f(t,e){let n=e==="/"?"/":e.replace(/\/+$/,"");return`${t.toLowerCase()}${n||"/"}`}function wA(t,e,n){let r=[],s=_i(n.filter((i)=>i.kind==="app").map((i)=>({slug:i.slug,proxies:[{from:i.target.split(",").map((a)=>a.trim())}]})),{ignoreSlug:t}),o=new Map;for(let i of n)if(i.slug!==t)o.set(f(i.host,i.path),i.slug);for(let i of e){if(i.port!==void 0){let a=s.get(i.port);if(a)r.push({kind:"port",site:i.name,detail:`port ${i.port}`,heldBy:a})}if(i.domain){let a=o.get(f(i.domain,i.path));if(a)r.push({kind:"route",site:i.name,detail:`${i.domain}${i.path==="/"?"/":i.path}`,heldBy:a})}}return r}function RA(t){return t.registryRead&&t.conflicts.length===0}function DA(t){let{slug:e,owner:n,server:r,declared:s,conflicts:o}=t,i=[];i.push(`Attach '${e}' to '${r.name}' (${r.ipv4??"no IPv4"}), owned by '${n}'.`,""),i.push(` ${s.length} site${s.length===1?"":"s"} would deploy onto this box:`);for(let a of s){let p=a.loopbackOnly?`loopback only${a.port?` on :${a.port}`:""}`:`${a.domain}${a.path==="/"?"/":a.path}${a.port?` on :${a.port}`:""}`;i.push(` ${a.name} ${p} -> ${a.installBase??"(install path unresolved)"}`)}if(i.push(""),!t.registryRead)i.push(` Could not read what '${r.name}' already serves: ${t.registryProblem??"unknown reason"}`),i.push(" So this attach is UNCHECKED: a port or hostname already taken by another"),i.push(" project would not error, it would serve that project's site from your domain.");else if(o.length>0){i.push(` ${o.length} conflict${o.length===1?"":"s"} with what the box already serves:`);for(let a of o)i.push(` site '${a.site}' wants ${a.detail}, held by '${a.heldBy}'`);i.push(""),i.push(" Two services on one port do not error: the kernel load-balances, and each"),i.push(" domain serves the other's site about half the time. Pick free ports and"),i.push(" hostnames, then re-run.")}else i.push(` No conflicts with what '${r.name}' already serves.`);return i}function Oi(t,e){return`/tmp/ts-cloud-move-${t}-${e}.tar.gz`}function vd(t,e){return`${t}-${e}*.service`}function l(t){return`'${t.split("'").join("'\\''")}'`}function Vp(t,e){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${l(m(t,e))} || true); do`,' systemctl stop "$unit" 2>/dev/null || true',"done"].join(`
10
+ `)}function m(t,e){return`^${t}-${e}-((queue|daemon)-.*|scheduler)\\.service$`}function qp(t,e){return y(m(t,e))}function y(t){return[`ls /etc/systemd/system/ 2>/dev/null | grep -E ${l(t)} | while read -r unit; do`,' systemctl is-active "$unit" >/dev/null 2>&1 && echo "active:$unit" || true',"done"].join(`
11
+ `)}function jp(t){return!t.split(`
12
+ `).some((e)=>e.trim().startsWith("active:"))}function zp(t,e,n){let r=Oi(t,e);return["set -eu",`test -d ${l(n)} || { echo "no site tree at ${n}" >&2; exit 1; }`,`rm -f ${l(r)}`,`tar czf ${l(r)} -C "$(dirname ${l(n)})" "$(basename ${l(n)})" -C /etc/systemd/system $(cd /etc/systemd/system && ls ${vd(t,e)} 2>/dev/null | tr '\\n' ' ')`,`sha256sum ${l(r)} | cut -d' ' -f1`].join(`
13
+ `)}function Kp(t,e,n){let r=Oi(t,e);return["set -eu",`test -f ${l(r)} || { echo "archive not staged at ${r}" >&2; exit 1; }`,`mkdir -p "$(dirname ${l(n)})"`,`tar xzf ${l(r)} -C "$(dirname ${l(n)})" "$(basename ${l(n)})"`,`tar xzf ${l(r)} -C /etc/systemd/system --wildcards ${l(vd(t,e))} 2>/dev/null || true`,"systemctl daemon-reload",`systemctl enable ${t}-${e}.service 2>/dev/null || true`,`systemctl restart ${t}-${e}.service`,`rm -f ${l(r)}`].join(`
14
+ `)}function Wp(t,e,n){return[`test -d ${l(n)} && echo tree:present || echo tree:absent`,`systemctl is-active ${t}-${e}.service >/dev/null 2>&1 && echo unit:active || echo unit:inactive`].join(`
15
+ `)}function Jp(t){return t.includes("tree:present")&&t.includes("unit:active")}function Xp(t,e="/",n=10){let r=`http://127.0.0.1:${t}${e.startsWith("/")?e:`/${e}`}`;return["set -eu",`for i in $(seq 1 ${n}); do`,` if curl -fsS -o /dev/null --max-time 5 ${l(r)}; then echo healthy; exit 0; fi`," sleep 3","done",`echo "no healthy response from ${r}" >&2`,"exit 1"].join(`
16
+ `)}function Yp(t,e){return["set -eu",`for unit in $(ls /etc/systemd/system/ 2>/dev/null | grep -E ${l(v(t,e))} || true); do`,' systemctl disable --now "$unit" 2>/dev/null || true',"done",`rm -f /etc/rpx/sites.d/${t}.json 2>/dev/null || true`,"systemctl reload rpx-gateway.service 2>/dev/null || systemctl restart rpx-gateway.service 2>/dev/null || true"].join(`
17
+ `)}function v(t,e){return`^${t}-${e}[-@.]`}function Zp(t,e){return y(v(t,e))}function em(t){return!t.split(`
18
+ `).some((e)=>e.trim().startsWith("active:"))}function PA(t,e){return`/tmp/ts-cloud-move-${t}-${e}-certs.tar.gz`}function kA(t,e){return["set -u",...e.map((n)=>`if [ -s ${l(`${t}/${n}.crt`)} ]; then echo "cert:${n}:$(sha256sum ${l(`${t}/${n}.crt`)} | cut -d' ' -f1)"; else echo "cert:${n}:absent"; fi`)].join(`
19
+ `)}function NA(t){let e=new Map;for(let n of t.split(`
20
+ `)){let r=n.trim();if(!r.startsWith("cert:"))continue;let[,s,o]=r.split(":");if(s&&o)e.set(s,o)}return e}function BA(t,e){for(let[n,r]of t){if(r==="absent")continue;if(e.get(n)!==r)return!1}return!0}function xA(t,e,n){let r=e.flatMap((s)=>[`${s}.crt`,`${s}.key`]);return["set -eu",`rm -f ${l(n)}`,`cd ${l(t)} 2>/dev/null || { echo "no certs dir at ${t}" >&2; exit 0; }`,`TS_CLOUD_CERTS="$(ls ${r.map(l).join(" ")} 2>/dev/null | tr '\\n' ' ' || true)"`,'[ -n "$TS_CLOUD_CERTS" ] || { echo "no certificates to carry"; exit 0; }',`tar czf ${l(n)} $TS_CLOUD_CERTS`].join(`
21
+ `)}function OA(t,e){return["set -eu",`[ -s ${l(e)} ] || { echo "no certificate archive staged"; exit 0; }`,`mkdir -p ${l(t)}`,`tar xzf ${l(e)} -C ${l(t)}`,`chmod 600 ${l(t)}/*.key 2>/dev/null || true`,`rm -f ${l(e)}`].join(`
22
+ `)}async function _A(t,e){let{slug:n,siteName:r,appBase:s,from:o,to:i,targetAddress:a}=t;if(o===i)throw Error(`'${r}' is already on ${i}.`);if(t.database&&!e.database)throw Error(`'${r}' uses the on-box database '${t.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(t.database&&e.database&&await e.database.targetHasData())throw Error(`${i} already has a database named '${t.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 p=[{id:"pause-workers",title:`Stop background work on ${o} so the snapshot is consistent`,satisfied:async()=>jp(await e.runOnSource(qp(n,r))),apply:async()=>{await e.runOnSource(Vp(n,r))}},...e.database?[{id:"database-dump",title:`Dump the '${t.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}:${s}`,to:Oi(n,r)},satisfied:async()=>!1,apply:async()=>{await e.runOnSource(zp(n,r,s))}},{id:"transfer",title:`Carry the archive to ${i}`,change:{from:o,to:i},satisfied:()=>e.archiveStaged(),apply:()=>e.transferArchive()},...e.database?[{id:"database-transfer",title:`Carry the database dump to ${i}`,change:{from:o,to:i},satisfied:()=>e.database.dumpStaged(),apply:()=>e.database.transferDump()},{id:"database-restore",title:`Create and load '${t.database?.name}' on ${i}`,satisfied:async()=>!1,apply:()=>e.database.restore()}]:[],{id:"restore",title:`Unpack the site on ${i} and start it`,change:{from:Oi(n,r),to:`${i}:${s}`},satisfied:async()=>Jp(await e.runOnTarget(Wp(n,r,s))),apply:async()=>{await e.runOnTarget(Kp(n,r,s))}}];if(t.port!=null){let{port:c}=t;p.push({id:"health",title:`Wait for the site to answer on ${i} (127.0.0.1:${c})`,satisfied:async()=>!1,apply:async()=>{await e.runOnTarget(Xp(c,t.healthCheckPath??"/"))}})}return p.push(...e.certificates?[{id:"certificates",title:`Carry the site's TLS material to ${i}`,satisfied:()=>e.certificates.inPlace(),apply:()=>e.certificates.carry()}]:[],{id:"gateway",title:`Route the site on ${i}`,satisfied:()=>e.targetRoutesSite(),apply:()=>e.refreshTargetGateway()},{id:"dns",title:`Point DNS at ${i}`,change:{from:`${o}`,to:a},satisfied:async()=>await e.publishedAddress()===a,apply:async()=>{let c=await e.cutoverDns();if(c.length>0)throw Error(c.join("; "))}},{id:"drain-source",title:`Stop the site on ${o}, leaving its files in place`,satisfied:async()=>em(await e.runOnSource(Zp(n,r))),apply:async()=>{await e.runOnSource(Yp(n,r))}}),{operation:"site:move",target:r,steps:p}}export{gA,pA,mA,Lp,$p,Mp,fA,Fp,Up,hA,AA,Qp,yA,SA,Ed,Hp,IA,bA,Gp,CA,EA,vA,TA,wA,RA,DA,Oi,vd,Vp,qp,jp,zp,Kp,Wp,Jp,Xp,Yp,Zp,em,PA,kA,NA,BA,xA,OA,_A};
@@ -1,5 +1,5 @@
1
- import{Wo,mn,Ql,hn,ta,ra}from"./chunk-3bvhthfb.js";import{He}from"./chunk-fdf181e6.js";var r={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",cyan:"\x1B[36m",white:"\x1B[37m",gray:"\x1B[90m"};function b(t,e){return`${r[e]}${t}${r.reset}`}function Mn(t){console.log(`${r.green}✓${r.reset} ${t}`)}function bg(t){console.error(`${r.red}✗${r.reset} ${t}`)}function Bs(t){console.warn(`${r.yellow}⚠${r.reset} ${t}`)}function L(t){console.log(`${r.blue}ℹ${r.reset} ${t}`)}function P(t){console.log(`${r.cyan}→${r.reset} ${t}`)}function xs(t){console.log(`
1
+ import{ia,hn,Jl,yn,la,ua}from"./chunk-72p2tdpe.js";import{Ve}from"./chunk-hcbrgyfz.js";var r={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",magenta:"\x1B[35m",cyan:"\x1B[36m",white:"\x1B[37m",gray:"\x1B[90m"};function b(t,e){return`${r[e]}${t}${r.reset}`}function Qn(t){console.log(`${r.green}✓${r.reset} ${t}`)}function kg(t){console.error(`${r.red}✗${r.reset} ${t}`)}function Ms(t){console.warn(`${r.yellow}⚠${r.reset} ${t}`)}function M(t){console.log(`${r.blue}ℹ${r.reset} ${t}`)}function P(t){console.log(`${r.cyan}→${r.reset} ${t}`)}function Fs(t){console.log(`
2
2
  ${r.bright}${r.cyan}${t}${r.reset}
3
- `)}var F=!!(process.env.CI||process.env.GITHUB_ACTIONS||process.env.BUILDKITE||process.env.CIRCLECI||process.env.GITLAB_CI);function Cg(t,e="cyan"){let n=t.split(`
4
- `),i=Math.max(...n.map((l)=>l.length)),a="─".repeat(i+2);console.log(b(`┌${a}┐`,e)),n.forEach((l)=>{console.log(b(`│ ${l.padEnd(i)} │`,e))}),console.log(b(`└${a}┘`,e))}import{execFileSync as k}from"node:child_process";import{mkdirSync as I,mkdtempSync as M,rmSync as U,writeFileSync as d}from"node:fs";import{tmpdir as O}from"node:os";import{dirname as j,join as s}from"node:path";function C(t,e){for(let n of e){let i=s(t,n.name);I(j(i),{recursive:!0}),d(i,n.data,n.mode?{mode:n.mode}:void 0)}}async function Ac(t){let{app:e,projectRoot:n,region:i,repository:a}=t,l=e.architecture??"x86_64",A=l==="arm64"?"linux/arm64":"linux/amd64",R=e.kind==="php",c=t.onStep??(()=>{}),p=M(s(O(),"tscloud-img-"));try{let f=s(p,"app");I(f,{recursive:!0});let y,w;if(R){ta({projectRoot:n,app:e,skipBuild:t.skipBuild,onStep:c}),c("staging application tree");let o=ra(n);C(f,o),C(s(p,"runtime"),hn().map((g)=>({name:g.path,data:g.contents,mode:g.mode})));let B=e.handlers?.http??"public/index.php";y={http:B,queue:e.handlers?.queue??B,cli:e.handlers?.cli??B},w=Wo(o.map((g)=>`${g.name}:${g.data.length}`).join("|"))}else{c("bundling application");let o=await mn({projectRoot:n,app:e,skipBuild:t.skipBuild,onStep:c});d(s(f,"index.mjs"),o.bundle),y=o.handlers,w=o.sha256}let S=w.slice(0,16);d(s(p,"Dockerfile"),Ql({kind:e.kind??"node",phpVersion:e.phpVersion,architecture:l}));let v=new He(i),u;try{u=(await v.describeRepositories({repositoryNames:[a]})).repositories?.[0]?.repositoryUri??""}catch{u=""}if(!u)c(`creating ECR repository ${a}`),u=(await v.createRepository({repositoryName:a,imageScanningConfiguration:{scanOnPush:!0}})).repository?.repositoryUri??"";if(!u)throw Error(`could not resolve ECR repository URI for ${a}`);let m=`${u}:${S}`;c("docker build"),k("docker",["build","--platform",A,"--provenance=false","--sbom=false","-t",m,p],{stdio:"inherit"}),c("ecr auth + push");let h=(await v.getAuthorizationToken()).authorizationData?.[0];if(!h?.authorizationToken||!h.proxyEndpoint)throw Error("failed to get an ECR authorization token");let E=h.proxyEndpoint.replace(/^https?:\/\//,""),x=s(p,".docker");return I(x,{recursive:!0}),d(s(x,"config.json"),JSON.stringify({auths:{[E]:{auth:h.authorizationToken}}})),k("docker",["push",m],{stdio:"inherit",env:{...process.env,DOCKER_CONFIG:x}}),L(`Image: ${m}`),{imageUri:m,tag:S,handlers:y}}finally{U(p,{recursive:!0,force:!0})}}
5
- export{Mn,bg,Bs,L,P,xs,Cg,Ac};
3
+ `)}var F=!!(process.env.CI||process.env.GITHUB_ACTIONS||process.env.BUILDKITE||process.env.CIRCLECI||process.env.GITLAB_CI);function Ng(t,e="cyan"){let n=t.split(`
4
+ `),i=Math.max(...n.map((l)=>l.length)),a="─".repeat(i+2);console.log(b(`┌${a}┐`,e)),n.forEach((l)=>{console.log(b(`│ ${l.padEnd(i)} │`,e))}),console.log(b(`└${a}┘`,e))}import{execFileSync as k}from"node:child_process";import{mkdirSync as I,mkdtempSync as U,rmSync as L,writeFileSync as d}from"node:fs";import{tmpdir as O}from"node:os";import{dirname as j,join as s}from"node:path";function C(t,e){for(let n of e){let i=s(t,n.name);I(j(i),{recursive:!0}),d(i,n.data,n.mode?{mode:n.mode}:void 0)}}async function wc(t){let{app:e,projectRoot:n,region:i,repository:a}=t,l=e.architecture??"x86_64",A=l==="arm64"?"linux/arm64":"linux/amd64",R=e.kind==="php",c=t.onStep??(()=>{}),p=U(s(O(),"tscloud-img-"));try{let f=s(p,"app");I(f,{recursive:!0});let y,w;if(R){la({projectRoot:n,app:e,skipBuild:t.skipBuild,onStep:c}),c("staging application tree");let o=ua(n);C(f,o),C(s(p,"runtime"),yn().map((g)=>({name:g.path,data:g.contents,mode:g.mode})));let B=e.handlers?.http??"public/index.php";y={http:B,queue:e.handlers?.queue??B,cli:e.handlers?.cli??B},w=ia(o.map((g)=>`${g.name}:${g.data.length}`).join("|"))}else{c("bundling application");let o=await hn({projectRoot:n,app:e,skipBuild:t.skipBuild,onStep:c});d(s(f,"index.mjs"),o.bundle),y=o.handlers,w=o.sha256}let S=w.slice(0,16);d(s(p,"Dockerfile"),Jl({kind:e.kind??"node",phpVersion:e.phpVersion,architecture:l}));let v=new Ve(i),u;try{u=(await v.describeRepositories({repositoryNames:[a]})).repositories?.[0]?.repositoryUri??""}catch{u=""}if(!u)c(`creating ECR repository ${a}`),u=(await v.createRepository({repositoryName:a,imageScanningConfiguration:{scanOnPush:!0}})).repository?.repositoryUri??"";if(!u)throw Error(`could not resolve ECR repository URI for ${a}`);let m=`${u}:${S}`;c("docker build"),k("docker",["build","--platform",A,"--provenance=false","--sbom=false","-t",m,p],{stdio:"inherit"}),c("ecr auth + push");let h=(await v.getAuthorizationToken()).authorizationData?.[0];if(!h?.authorizationToken||!h.proxyEndpoint)throw Error("failed to get an ECR authorization token");let E=h.proxyEndpoint.replace(/^https?:\/\//,""),x=s(p,".docker");return I(x,{recursive:!0}),d(s(x,"config.json"),JSON.stringify({auths:{[E]:{auth:h.authorizationToken}}})),k("docker",["push",m],{stdio:"inherit",env:{...process.env,DOCKER_CONFIG:x}}),M(`Image: ${m}`),{imageUri:m,tag:S,handlers:y}}finally{L(p,{recursive:!0,force:!0})}}
5
+ export{Qn,kg,Ms,M,P,Fs,Ng,wc};
@@ -1,13 +1,13 @@
1
- import{ln,mr}from"./chunk-3bvhthfb.js";import{Kr,d}from"./chunk-tr54e9c0.js";import{isIP as L}from"node:net";var go="/etc/rpx/certs",A="/etc/rpx/local-ca",M="rpx-root-ca.crt";function po(e=A){return`${e.replace(/\/+$/,"")}/${M}`}function mo(e){if(e.toLowerCase().endsWith(".local"))return!1;return e.split(".").length===2}function fo(e,t={}){let r=new Set;for(let o of Object.values(e)){if(!o?.domain)continue;if(d(o)==="bucket")continue;r.add(o.domain)}let n=new Set;for(let o of Object.values(e)){if(!o?.domain||d(o)==="bucket")continue;for(let u of o.aliases??[]){let i=String(u).trim().toLowerCase();if(i&&i!==o.domain)n.add(i)}}if(t.autoWww!==!1)for(let o of[...r]){if(!mo(o))continue;r.add(`www.${o}`)}for(let o of n)r.add(o);return[...r]}var k=["1.1.1.1","8.8.8.8"],I="/var/www/acme-challenge";function W(e){if(typeof e==="string")return{to:e};let t={to:e.to};if(e.status!=null)t.status=e.status;if(e.preservePath!=null)t.preservePath=e.preservePath;return t}function U(e){let t=e.auth;if(!t||t.enabled===!1||!t.password)return;return{username:t.username||"admin",password:t.password,...t.realm?{realm:t.realm}:{}}}function dd(e){if(!e)return;return e.tls??"local-ca"}function Cp(e,t){if(e.cloud?.provider!=="ssh")return;let r=e.ssh?.lan;if(!r)return;return{...r.hostname?{hostname:r.hostname}:{},tls:r.tls??"local-ca",...t?{ip:t}:{}}}function Ep(e){if(e.cloud?.provider!=="ssh")return;return e.ssh?.profile}function X(e){let t=e.trim().toLowerCase();if(!t||t.startsWith("*")||t.includes(":")||t.includes("/")||t.includes(" "))return!1;if(L(t)!==0)return!1;return t.endsWith(".local")||!t.includes(".")}function F(e,t,r){let n=[],o=new Set,u=(g,a)=>{let p=g.trim().toLowerCase();if(!p||o.has(p))return;o.add(p),n.push({host:p,source:a})},i=t.hostname?.trim(),c=(i||`${r}.local`).toLowerCase();if(u(c,i?"ssh.lan.hostname":`the ssh provider's default LAN name for project "${r}"`),Object.entries(e).some(([g,a])=>!!a&&mr(g)))u(`dashboard.${c}`,"the management dashboard site");for(let[g,a]of Object.entries(e)){if(!a?.domain)continue;if(d(a)==="bucket")continue;if(!X(a.domain))continue;u(a.domain,`site "${g}"`)}return n}function H(e,t){if(!t?.length)return;return t.find((r)=>{let n=(r.startsWith(".")?r.slice(1):r).toLowerCase();return e===n||e.endsWith(`.${n}`)})}function K(e,t){if(!t?.enabled)return;for(let{host:r,source:n}of e){let o=H(r,t.allowedSuffixes);if(!o)continue;throw Error(`The LAN certificate authority and public on-demand TLS both claim "${r}". LAN source: ${n}. Public source: the on-demand TLS allowed suffix "${o}", which is derived from the site domains. A host is either LAN-only, with a certificate signed by the box's own CA, or public, with one issued by Let's Encrypt, never both. Give the LAN a name no site domain covers (ssh.lan.hostname), or take that domain out of the sites this deploy routes.`)}}function V(e){let t=e.split(".");return t.length>=3&&t[0]==="*"&&t.slice(1).every((r)=>/^[a-z0-9-]+$/i.test(r))}function D(e,t={}){let{wildcardCerts:r,...n}=e;return JSON.stringify({...t,...n},null,2)}function eA(e){if(!e||e==="/")return;let t=`/${e}`.replace(/\/+/g,"/").replace(/\/+$/,"");if(!t.startsWith("/"))t=`/${t}`;return t===""||t==="/"?void 0:t}function ld(e,t){let n=(t?`${e}${t}`:e).replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,128);return n.length>0?n:"rpx"}function Y(e,t){if(!t||t.length===0)return`localhost:${e}`;return t.map((r)=>`${r.privateIp??r.publicIp}:${e}`)}function N(e,t,r){let n=(t.wwwRoot??"/var/www").replace(/\/+$/,""),o=t.slug??"app",u=t.proxy.certsDir??go,i=t.proxy.loadBalancer,c=[],f=new Set,g=new Map;for(let[l,s]of Object.entries(e)){if(!s||!s.domain)continue;let w=d(s);if(w==="bucket")continue;let v=c.length,E=[...new Set((s.aliases??[]).map((m)=>String(m).trim().toLowerCase()).filter((m)=>m&&m!==s.domain))],b=()=>{let m=c.slice(v).filter((h)=>h.to===s.domain);for(let h of E){for(let O of m)c.push({...O,to:h,id:ld(h,O.path)});if(V(h)){if(!g.has(h))g.set(h,{domain:h,dnsEnvFile:`${n}/${o}-${l}/shared/.env`})}else f.add(h)}},C=eA(s.path),S=ld(s.domain,C),y=U(s);if(w==="redirect"){c.push({to:s.domain,path:C,redirect:W(s.redirect),id:S,...y?{auth:y}:{}}),f.add(s.domain),b();continue}if(w==="proxy"){let m=Kr(s);if(m.length===0)continue;let h=m.length===1?m[0]:m;c.push({to:s.domain,path:C,from:h,id:S,...y?{auth:y}:{},...Array.isArray(h)&&i?{loadBalancer:i}:{}}),f.add(s.domain),b();continue}if(w==="server-app"){if(typeof s.port!=="number")continue;let m=Y(s.port,r);c.push({to:s.domain,path:C,from:m,id:S,...y?{auth:y}:{},...Array.isArray(m)&&i?{loadBalancer:i}:{}})}else c.push({to:s.domain,path:C,static:{dir:`${n}/${o}-${l}/current`,spa:s.spa??!1,pathRewriteStyle:s.pathRewriteStyle??"directory"},cleanUrls:s.pathRewriteStyle!=="flat",...y?{auth:y}:{},id:S});f.add(s.domain),b()}if(t.proxy.autoWww!==!1)for(let l of[...f]){if(!mo(l))continue;let s=`www.${l}`;if(f.has(s))continue;c.push({to:s,redirect:{to:`https://${l}`},id:ld(s)}),f.add(s)}c.sort((l,s)=>{if(l.to!==s.to)return l.to.localeCompare(s.to);return(s.path?.length??0)-(l.path?.length??0)});let a={proxies:c,productionCerts:{certsDir:u,certsDirServerNames:[...f,...g.keys()]},https:!0,hostsManagement:!1,cleanup:{hosts:!1,certs:!1},...g.size>0?{wildcardCerts:[...g.values()]}:{}};if(t.proxy.onDemandTls&&f.size>0)a.onDemandTls={enabled:!0,allowedSuffixes:[...f],email:t.proxy.onDemandTlsEmail,certsDir:u,staging:t.proxy.onDemandTlsStaging??!1};if(t.proxy.onDemandTls)a.acmeChallengeWebroot=t.proxy.acmeWebroot??I;let p=t.proxy.cdn,x=p?.frontedHosts?.length?p.frontedHosts:[...f];if(p?.secret&&x.length>0)a.originGuard={header:p.secretHeader??"X-Origin-Verify",value:p.secret,hosts:x};if(t.lan)if(dd(t.lan)==="off")a.https=!1;else{let l=F(e,t.lan,o);K(l,a.onDemandTls);let s=t.lan.ip&&L(t.lan.ip)!==0?[t.lan.ip]:[];a.localCa={dir:A,hosts:l.map((w)=>w.host),...s.length>0?{ips:s}:{},installTrust:!0}}return a}function B(e,t){return N({...e,...t.mail?ln(t.mail):{}},t)}function rr(e,t,r){return N({...e,...r.mail?ln(r.mail):{}},r,t)}function kI(e){return`// Generated by ts-cloud — rpx reverse-proxy gateway.
1
+ import{un,fr}from"./chunk-72p2tdpe.js";import{Wr,d}from"./chunk-x7cnfn7y.js";import{isIP as A}from"node:net";var yo="/etc/rpx/certs",N="/etc/rpx/local-ca",k="rpx-root-ca.crt";function So(e=N){return`${e.replace(/\/+$/,"")}/${k}`}function Io(e){if(e.toLowerCase().endsWith(".local"))return!1;return e.split(".").length===2}function bo(e,t={}){let r=new Set;for(let o of Object.values(e)){if(!o?.domain)continue;if(d(o)==="bucket")continue;r.add(o.domain)}let n=new Set;for(let o of Object.values(e)){if(!o?.domain||d(o)==="bucket")continue;for(let u of o.aliases??[]){let i=String(u).trim().toLowerCase();if(i&&i!==o.domain)n.add(i)}}if(t.autoWww!==!1)for(let o of[...r]){if(!Io(o))continue;r.add(`www.${o}`)}for(let o of n)r.add(o);return[...r]}var B=["1.1.1.1","8.8.8.8"],I="/var/www/acme-challenge";function W(e){if(typeof e==="string")return{to:e};let t={to:e.to};if(e.status!=null)t.status=e.status;if(e.preservePath!=null)t.preservePath=e.preservePath;return t}function G(e){let t=e.auth;if(!t||t.enabled===!1||!t.password)return;return{username:t.username||"admin",password:t.password,...t.realm?{realm:t.realm}:{}}}function yd(e){if(!e)return;return e.tls??"local-ca"}function Np(e,t){if(e.cloud?.provider!=="ssh")return;let r=e.ssh?.lan;if(!r)return;return{...r.hostname?{hostname:r.hostname}:{},tls:r.tls??"local-ca",...t?{ip:t}:{}}}function Bp(e){if(e.cloud?.provider!=="ssh")return;return e.ssh?.profile}function U(e){let t=e.trim().toLowerCase();if(!t||t.startsWith("*")||t.includes(":")||t.includes("/")||t.includes(" "))return!1;if(A(t)!==0)return!1;return t.endsWith(".local")||!t.includes(".")}function X(e,t,r){let n=[],o=new Set,u=(g,a)=>{let p=g.trim().toLowerCase();if(!p||o.has(p))return;o.add(p),n.push({host:p,source:a})},i=t.hostname?.trim(),c=(i||`${r}.local`).toLowerCase();if(u(c,i?"ssh.lan.hostname":`the ssh provider's default LAN name for project "${r}"`),Object.entries(e).some(([g,a])=>!!a&&fr(g)))u(`dashboard.${c}`,"the management dashboard site");for(let[g,a]of Object.entries(e)){if(!a?.domain)continue;if(d(a)==="bucket")continue;if(!U(a.domain))continue;u(a.domain,`site "${g}"`)}return n}function F(e,t){if(!t?.length)return;return t.find((r)=>{let n=(r.startsWith(".")?r.slice(1):r).toLowerCase();return e===n||e.endsWith(`.${n}`)})}function j(e,t){if(!t?.enabled)return;for(let{host:r,source:n}of e){let o=F(r,t.allowedSuffixes);if(!o)continue;throw Error(`The LAN certificate authority and public on-demand TLS both claim "${r}". LAN source: ${n}. Public source: the on-demand TLS allowed suffix "${o}", which is derived from the site domains. A host is either LAN-only, with a certificate signed by the box's own CA, or public, with one issued by Let's Encrypt, never both. Give the LAN a name no site domain covers (ssh.lan.hostname), or take that domain out of the sites this deploy routes.`)}}function H(e){let t=e.split(".");return t.length>=3&&t[0]==="*"&&t.slice(1).every((r)=>/^[a-z0-9-]+$/i.test(r))}function O(e,t={}){let{wildcardCerts:r,...n}=e;return JSON.stringify({...t,...n},null,2)}function lA(e){if(!e||e==="/")return;let t=`/${e}`.replace(/\/+/g,"/").replace(/\/+$/,"");if(!t.startsWith("/"))t=`/${t}`;return t===""||t==="/"?void 0:t}function Sd(e,t){let n=(t?`${e}${t}`:e).replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,128);return n.length>0?n:"rpx"}function Y(e,t){if(!t||t.length===0)return`localhost:${e}`;return t.map((r)=>`${r.privateIp??r.publicIp}:${e}`)}function M(e,t,r){let n=(t.wwwRoot??"/var/www").replace(/\/+$/,""),o=t.slug??"app",u=t.proxy.certsDir??yo,i=t.proxy.loadBalancer,c=[],f=new Set,g=new Map;for(let[l,s]of Object.entries(e)){if(!s||!s.domain)continue;let y=d(s);if(y==="bucket")continue;let E=c.length,T=[...new Set((s.aliases??[]).map((m)=>String(m).trim().toLowerCase()).filter((m)=>m&&m!==s.domain))],v=()=>{let m=c.slice(E).filter((h)=>h.to===s.domain);for(let h of T){for(let P of m)c.push({...P,to:h,id:Sd(h,P.path)});if(H(h)){if(!g.has(h))g.set(h,{domain:h,dnsEnvFile:`${n}/${o}-${l}/shared/.env`})}else f.add(h)}},S=lA(s.path),_=Sd(s.domain,S),C=G(s);if(y==="redirect"){c.push({to:s.domain,path:S,redirect:W(s.redirect),id:_,...C?{auth:C}:{}}),f.add(s.domain),v();continue}if(y==="proxy"){let m=Wr(s);if(m.length===0)continue;let h=m.length===1?m[0]:m;c.push({to:s.domain,path:S,from:h,id:_,...C?{auth:C}:{},...Array.isArray(h)&&i?{loadBalancer:i}:{}}),f.add(s.domain),v();continue}if(y==="server-app"){if(typeof s.port!=="number")continue;let m=Y(s.port,r);c.push({to:s.domain,path:S,from:m,id:_,...C?{auth:C}:{},...Array.isArray(m)&&i?{loadBalancer:i}:{}})}else c.push({to:s.domain,path:S,static:{dir:`${n}/${o}-${l}/current`,spa:s.spa??!1,pathRewriteStyle:s.pathRewriteStyle??"directory"},cleanUrls:s.pathRewriteStyle!=="flat",...C?{auth:C}:{},id:_});f.add(s.domain),v()}if(t.proxy.autoWww!==!1)for(let l of[...f]){if(!Io(l))continue;let s=`www.${l}`;if(f.has(s))continue;c.push({to:s,redirect:{to:`https://${l}`},id:Sd(s)}),f.add(s)}c.sort((l,s)=>{if(l.to!==s.to)return l.to.localeCompare(s.to);return(s.path?.length??0)-(l.path?.length??0)});let a={proxies:c,productionCerts:{certsDir:u,certsDirServerNames:[...f,...g.keys()]},https:!0,hostsManagement:!1,cleanup:{hosts:!1,certs:!1},...g.size>0?{wildcardCerts:[...g.values()]}:{}};if(t.proxy.onDemandTls&&f.size>0)a.onDemandTls={enabled:!0,allowedSuffixes:[...f],email:t.proxy.onDemandTlsEmail,certsDir:u,staging:t.proxy.onDemandTlsStaging??!1};if(t.proxy.onDemandTls)a.acmeChallengeWebroot=t.proxy.acmeWebroot??I;let p=t.proxy.cdn,w=p?.frontedHosts?.length?p.frontedHosts:[...f];if(p?.secret&&w.length>0)a.originGuard={header:p.secretHeader??"X-Origin-Verify",value:p.secret,hosts:w};if(t.lan)if(yd(t.lan)==="off")a.https=!1;else{let l=X(e,t.lan,o);j(l,a.onDemandTls);let s=t.lan.ip&&A(t.lan.ip)!==0?[t.lan.ip]:[];a.localCa={dir:N,hosts:l.map((y)=>y.host),...s.length>0?{ips:s}:{},installTrust:!0}}return a}function x(e,t){return M({...e,...t.mail?un(t.mail):{}},t)}function nr(e,t,r){return M({...e,...r.mail?un(r.mail):{}},r,t)}function HI(e){return`// Generated by ts-cloud — rpx reverse-proxy gateway.
2
2
  // Routes are derived from the \`sites\` model on every \`buddy deploy\`.
3
3
  import { startProxies } from '@stacksjs/rpx'
4
4
 
5
- const config = ${D(e)} as const
5
+ const config = ${O(e)} as const
6
6
 
7
7
  // Verbose is the hard default for ts-cloud-installed gateways (RPX_VERBOSE=false
8
8
  // opts out) — without it rpx's TLS/routing diagnostics never reach the journal.
9
9
  await startProxies({ verbose: process.env.RPX_VERBOSE !== 'false', ...config } as any)
10
- `}function j(e){return e?.webServer==="rpx"||e?.proxy?.engine==="rpx"}var ho="/etc/rpx",R="/opt/rpx-gateway",vp="/etc/rpx/gateway.ts",Et="/etc/rpx/gateway",Ke="rpx-gateway.service",z="/etc/rpx/sites.d";function J(e=z,t=go){return`// Generated by ts-cloud — rpx gateway assembler.
10
+ `}function z(e){return e?.webServer==="rpx"||e?.proxy?.engine==="rpx"}var Co="/etc/rpx",R="/opt/rpx-gateway",xp="/etc/rpx/gateway.ts",vt="/etc/rpx/gateway",Je="rpx-gateway.service",K="/etc/rpx/sites.d";function J(e=K,t=yo){return`// Generated by ts-cloud — rpx gateway assembler.
11
11
  // Merges every app's fragment in ${e} so independent deploys compose
12
12
  // without clobbering each other. Each deploy writes only its own <slug>.json.
13
13
  import { startProxies } from '@stacksjs/rpx'
@@ -119,7 +119,7 @@ const config = {
119
119
  }
120
120
 
121
121
  await startProxies(config)
122
- `}function _(e,t,r,n="0644"){return[`__tsc_tmp="$(mktemp "${e}.XXXXXX")"`,`cat > "$__tsc_tmp" <<'${r}'`,t,r,`mv -f "$__tsc_tmp" ${e}`,`chmod ${n} ${e}`]}function q(e){return`'${e.replaceAll("'","'\\''")}'`}function T(e,t,r,n){if(!n.preserveManagementDashboardRoutes)return _(e,t,r,"0600");let o=`
122
+ `}function b(e,t,r,n="0644"){return[`__tsc_tmp="$(mktemp "${e}.XXXXXX")"`,`cat > "$__tsc_tmp" <<'${r}'`,t,r,`mv -f "$__tsc_tmp" ${e}`,`chmod ${n} ${e}`]}function q(e){return`'${e.replaceAll("'","'\\''")}'`}function D(e,t,r,n){if(!n.preserveManagementDashboardRoutes)return b(e,t,r,"0600");let o=`
123
123
  const { readFileSync, writeFileSync } = require('node:fs')
124
124
  const currentPath = process.env.TS_CLOUD_RPX_CURRENT_FRAGMENT
125
125
  const candidatePath = process.env.TS_CLOUD_RPX_CANDIDATE_FRAGMENT
@@ -130,9 +130,9 @@ const retained = Array.isArray(current.proxies) ? current.proxies.filter(isDashb
130
130
  const next = Array.isArray(candidate.proxies) ? candidate.proxies.filter(route => !isDashboard(route)) : []
131
131
  candidate.proxies = [...next, ...retained]
132
132
  writeFileSync(candidatePath, JSON.stringify(candidate, null, 2) + '\\n', { mode: 0o600 })
133
- `.trim();return[`__tsc_fragment_candidate="$(mktemp "${e}.candidate.XXXXXX")"`,`cat > "$__tsc_fragment_candidate" <<'${r}'`,t,r,'chmod 0600 "$__tsc_fragment_candidate"',`if [ -f ${e} ]; then TS_CLOUD_RPX_CURRENT_FRAGMENT=${e} TS_CLOUD_RPX_CANDIDATE_FRAGMENT="$__tsc_fragment_candidate" ${n.bunBin} -e ${q(o)}; fi`,`mv -f "$__tsc_fragment_candidate" ${e}`,`chmod 0600 ${e}`]}var Q={high:"256M",max:"384M"},Z={high:"512M",max:"768M"};function ud(e){return`rpx-cert-renew-${(e||"app").replace(/[^a-z0-9._-]+/gi,"-")}.service`}function gd(e){let t=new Set;for(let r of e.proxies){let n=r.to;if(!n||n.startsWith("*")||n.includes(":")||!n.includes("."))continue;t.add(n)}return[...t]}function ee(e){if(e.length===0)return[];let t=["# Wildcards: dns-01 (Porkbun), keys read from the site's env file for tlsx alone.","dns_env() {",' [ -r "$1" ] || return 0',` sed -n "s/^$2=//p" "$1" | tail -n 1 | sed -e 's/^"\\(.*\\)"$/\\1/' -e "s/^'\\(.*\\)'$/\\1/"`,"}","wildcard() {",' apex="$1"',' envfile="$2"',' name="*.$apex"',' key=$(dns_env "$envfile" PORKBUN_API_KEY)',' secret=$(dns_env "$envfile" PORKBUN_SECRET_KEY)',' if [ -z "$key" ] || [ -z "$secret" ]; then',' echo "wildcard $name skipped: no PORKBUN_API_KEY / PORKBUN_SECRET_KEY in $envfile (non-fatal)"'," return 0"," fi",' if [ ! -s "$CERTS/_wildcard.$apex.crt" ]; then',' PORKBUN_API_KEY="$key" PORKBUN_SECRET_KEY="$secret" $TLSX acme:issue -d "$name" --method dns-01 --dns-provider porkbun --dir "$CERTS" --prod --email "$EMAIL" || echo "issue $name failed (non-fatal)"'," else",' PORKBUN_API_KEY="$key" PORKBUN_SECRET_KEY="$secret" $TLSX acme:renew --domains "$name" --method dns-01 --dir "$CERTS" --days 30 --prod --email "$EMAIL" || echo "renew $name failed (non-fatal)"'," fi","}"];for(let r of e){let n=r.domain.replace(/^\*\./,"");t.push(`wildcard '${n}' '${r.dnsEnvFile.replace(/'/g,"")}'`)}return t}function P(e){let{config:t,proxy:r}=e,n=t.acmeChallengeWebroot,o=gd(t);if(!r.onDemandTls||!n||o.length===0)return[];let u=e.bunBin??"/usr/local/bin/bun",i=r.tlsxVersion??"latest",c=t.productionCerts.certsDir,f=r.onDemandTlsEmail??`webmaster@${o[0]}`,g=`${u} ${R}/node_modules/@stacksjs/tlsx/dist/bin/cli.js`,a=o.join(","),p=o.join(" "),x=(e.slug||"app").replace(/[^a-z0-9._-]+/gi,"-"),l=`${ho}/renew-certs-${x}.sh`,s=ud(x),w=`rpx-cert-renew-${x}.timer`,v=t.wildcardCerts??[],E=v.length>0?"tlsx http-01 + dns-01 wildcard":"tlsx http-01",b=["#!/bin/sh",`# Generated by ts-cloud — issue/renew rpx gateway TLS certs via ${E}.`,"# The running gateway serves the challenge from $WEBROOT on :80, so this needs","# no downtime and no DNS credentials. Reloads the gateway only if a cert changed.","set -u",`CERTS='${c}'`,`WEBROOT='${n}'`,`EMAIL='${f}'`,`TLSX="${g}"`,`BUN='${u}'`,`DOMAINS='${a}'`,"DNS_ATTEMPTS='24'","DNS_DELAY_SECONDS='5'",`DNS_RESOLVERS='${k.join(" ")}'`,"dns_resolves() {",' d="$1"'," for ns in $DNS_RESOLVERS; do",` if $BUN --eval "const{Resolver}=require('node:dns');const r=new Resolver();r.setServers([process.argv[1]]);r.resolve4(process.argv[2],e=>process.exit(e?1:0))" "$ns" "$d" >/dev/null 2>&1; then`," return 0"," fi"," done",' getent ahosts "$d" >/dev/null 2>&1',"}","wait_for_dns() {",' d="$1"'," attempt=1",' while ! dns_resolves "$d"; do',' if [ "$attempt" -ge "$DNS_ATTEMPTS" ]; then',' echo "DNS for $d did not become resolvable after $DNS_ATTEMPTS attempts" >&2'," return 1"," fi",' echo "Waiting for public DNS before ACME: $d (attempt $attempt/$DNS_ATTEMPTS)"',' sleep "$DNS_DELAY_SECONDS"'," attempt=$((attempt + 1))"," done","}",'before=$(cat "$CERTS"/*.crt 2>/dev/null | sha256sum)',`for d in ${p}; do`,' if [ ! -s "$CERTS/$d.crt" ]; then',' if wait_for_dns "$d"; then',' $TLSX acme:issue -d "$d" --method http-01 --webroot "$WEBROOT" --dir "$CERTS" --prod --email "$EMAIL" || echo "issue $d failed (non-fatal)"'," else",' echo "issue $d skipped until DNS resolves (non-fatal)"'," fi"," # rpx reloads its SNI set when a PEM appears. Complete that reload"," # before the next hostname starts http-01, or :80 can disappear in"," # the middle of the following challenge on a multi-domain deploy.",` [ -s "$CERTS/$d.crt" ] && systemctl restart ${Ke}`," fi","done",'$TLSX acme:renew --domains "$DOMAINS" --method http-01 --webroot "$WEBROOT" --dir "$CERTS" --days 30 --prod --email "$EMAIL" || echo "renew: some domains failed (non-fatal)"',...ee(v),'rm -f "$CERTS"/*.chain.crt','after=$(cat "$CERTS"/*.crt 2>/dev/null | sha256sum)',`[ "$before" = "$after" ] || systemctl restart ${Ke}`].join(`
134
- `),C=["[Unit]",`Description=Issue/renew rpx gateway TLS certs for ${x} (${E})`,`After=network-online.target ${Ke}`,"Wants=network-online.target","","[Service]","Type=oneshot",`ExecStart=${l}`].join(`
135
- `),S=["[Unit]",`Description=Daily rpx gateway TLS cert issuance/renewal for ${x}`,"","[Timer]","OnCalendar=*-*-* 03:30:00","RandomizedDelaySec=1h","Persistent=true","","[Install]","WantedBy=timers.target"].join(`
136
- `);return[`mkdir -p ${n}`,...e.tenant?[]:[`(cd ${R} && ${u} add @stacksjs/tlsx@${i}) || true`],..._(l,b,"TS_CLOUD_RENEW_EOF"),`chmod +x ${l}`,..._(`/etc/systemd/system/${s}`,C,"TS_CLOUD_RENEW_SVC_EOF"),..._(`/etc/systemd/system/${w}`,S,"TS_CLOUD_RENEW_TIMER_EOF"),"systemctl daemon-reload",`systemctl enable --now ${w} || true`,`${l} || true`]}function G(e){let{config:t,proxy:r}=e,n=e.bunBin??"/usr/local/bin/bun",o=r.version??"latest",u=t.productionCerts.certsDir,i=(e.slug||"app").replace(/[^a-z0-9._-]+/gi,"-"),c=D(t,{slug:i}),f=J(z,u);if(e.tenant)return["set -euo pipefail",`if [ ! -x ${Et} ] || [ ! -d ${R}/node_modules/@stacksjs/rpx ]; then`,` echo "ts-cloud: no rpx gateway on this box. This project attaches to another project's compute, so it does not provision one - deploy the owning project first." >&2`," exit 1","fi",`mkdir -p ${z} ${u}`,`__tsc_rpx_before="$(sha256sum ${z}/${i}.json 2>/dev/null | cut -d' ' -f1 || true)"`,...T(`${z}/${i}.json`,c,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:n,preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),`__tsc_rpx_after="$(sha256sum ${z}/${i}.json 2>/dev/null | cut -d' ' -f1 || true)"`,`if [ "$__tsc_rpx_before" != "$__tsc_rpx_after" ] || ! systemctl is-active --quiet ${Ke}; then`,` systemctl restart ${Ke}`,"else",` echo "[ts-cloud] rpx routes for ${i} unchanged - leaving the shared gateway alone"`,"fi",...P(e)];let a=[`Environment=RPX_UPSTREAM_TIMEOUT=${r.upstreamTimeout??60}`];if(typeof r.maxUpstreamConns==="number")a.push(`Environment=RPX_MAX_UPSTREAM_CONNS=${r.maxUpstreamConns}`);let p=e.profile==="raspberry-pi"?Q:Z,x=r.memoryHigh??p.high,l=r.memoryMax??p.max;return["set -euo pipefail",`mkdir -p ${ho} ${z} ${u} ${R}`,`rm -rf ${R}.next ${R}.prev`,`mkdir -p ${R}.next`,`(cd ${R}.next && ${n} add @stacksjs/rpx@${o})`,`mv ${R} ${R}.prev`,`mv ${R}.next ${R}`,`rm -rf ${R}.prev`,`ln -sfn ${R}/node_modules ${ho}/node_modules`,...T(`${z}/${i}.json`,c,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:n,preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),..._(vp,f,"TS_CLOUD_RPX_EOF"),`${n} build --production --compile --outfile ${Et}.next ${vp}`,`chmod 0755 ${Et}.next`,`mv -f ${Et}.next ${Et}`,..._(`/etc/systemd/system/${Ke}`,["[Unit]","Description=rpx reverse-proxy gateway (managed by ts-cloud)","After=network.target network-online.target","Wants=network-online.target","","[Service]","Type=simple",`ExecStart=${Et}`,`WorkingDirectory=${R}`,"Environment=BUN_INSTALL=/root/.bun","Environment=APP_ENV=production","Environment=NODE_ENV=production",...a,"MemoryAccounting=true",`MemoryHigh=${x}`,`MemoryMax=${l}`,"CPUWeight=500","IOWeight=500","OOMPolicy=stop","Restart=always","RestartSec=5","StartLimitIntervalSec=120","StartLimitBurst=5",`ExecStartPre=/bin/sh -c 'for p in 80 443; do fuser -k -n tcp "$p" 2>/dev/null || true; done; sleep 1'`,"LimitNOFILE=1048576","AmbientCapabilities=CAP_NET_BIND_SERVICE","","[Install]","WantedBy=multi-user.target"].join(`
137
- `),"TS_CLOUD_RPX_UNIT_EOF"),"systemctl daemon-reload","systemctl disable --now bun-gateway.service 2>/dev/null || true","systemctl disable --now bun-gateway-renew.timer bun-gateway-renew.service 2>/dev/null || true","rm -f /etc/systemd/system/bun-gateway-renew.timer /etc/systemd/system/bun-gateway-renew.service","systemctl disable --now ts-cloud-nginx.service 2>/dev/null || true",`for timer in /etc/systemd/system/rpx-cert-renew-*.timer; do [ -e "$timer" ] || continue; unit="$(basename "$timer")"; tenant="\${unit#rpx-cert-renew-}"; tenant="\${tenant%.timer}"; [ -f "${z}/$tenant.json" ] && continue; systemctl disable --now "$unit" 2>/dev/null || true; rm -f "$timer" "/etc/systemd/system/rpx-cert-renew-$tenant.service" "${ho}/renew-certs-$tenant.sh"; done`,`systemctl enable ${Ke}`,`systemctl restart ${Ke}`,...P(e)]}function Ne(e){let t=(e.slug||"app").replace(/[^a-z0-9._-]+/gi,"-"),r=D(e.config,{slug:t});return["set -euo pipefail",`mkdir -p ${z}`,...T(`${z}/${t}.json`,r,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:"/usr/local/bin/bun",preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),`systemctl restart ${Ke}`]}
138
- export{go,po,mo,fo,dd,Cp,Ep,eA,ld,B,rr,kI,j,ho,R,vp,Et,Ke,z,ud,gd,G,Ne};
133
+ `.trim();return[`__tsc_fragment_candidate="$(mktemp "${e}.candidate.XXXXXX")"`,`cat > "$__tsc_fragment_candidate" <<'${r}'`,t,r,'chmod 0600 "$__tsc_fragment_candidate"',`if [ -f ${e} ]; then TS_CLOUD_RPX_CURRENT_FRAGMENT=${e} TS_CLOUD_RPX_CANDIDATE_FRAGMENT="$__tsc_fragment_candidate" ${n.bunBin} -e ${q(o)}; fi`,`mv -f "$__tsc_fragment_candidate" ${e}`,`chmod 0600 ${e}`]}var Q={high:"256M",max:"384M"},Z={high:"512M",max:"768M"};function Id(e){return`rpx-cert-renew-${(e||"app").replace(/[^a-z0-9._-]+/gi,"-")}.service`}function bd(e){let t=new Set;for(let r of e.proxies){let n=r.to;if(!n||n.startsWith("*")||n.includes(":")||!n.includes("."))continue;t.add(n)}return[...t]}function ee(e){if(e.length===0)return[];let t=["# Wildcards: dns-01 (Porkbun), keys read from the site's env file for tlsx alone.","dns_env() {",' [ -r "$1" ] || return 0',` sed -n "s/^$2=//p" "$1" | tail -n 1 | sed -e 's/^"\\(.*\\)"$/\\1/' -e "s/^'\\(.*\\)'$/\\1/"`,"}","wildcard() {",' apex="$1"',' envfile="$2"',' name="*.$apex"',' key=$(dns_env "$envfile" PORKBUN_API_KEY)',' secret=$(dns_env "$envfile" PORKBUN_SECRET_KEY)',' if [ -z "$key" ] || [ -z "$secret" ]; then',' echo "wildcard $name skipped: no PORKBUN_API_KEY / PORKBUN_SECRET_KEY in $envfile (non-fatal)"'," return 0"," fi",' if [ ! -s "$CERTS/_wildcard.$apex.crt" ]; then',' PORKBUN_API_KEY="$key" PORKBUN_SECRET_KEY="$secret" $TLSX acme:issue -d "$name" --method dns-01 --dns-provider porkbun --dir "$CERTS" --prod --email "$EMAIL" || echo "issue $name failed (non-fatal)"'," else",' PORKBUN_API_KEY="$key" PORKBUN_SECRET_KEY="$secret" $TLSX acme:renew --domains "$name" --method dns-01 --dir "$CERTS" --days 30 --prod --email "$EMAIL" || echo "renew $name failed (non-fatal)"'," fi","}"];for(let r of e){let n=r.domain.replace(/^\*\./,"");t.push(`wildcard '${n}' '${r.dnsEnvFile.replace(/'/g,"")}'`)}return t}function L(e){let{config:t,proxy:r}=e,n=t.acmeChallengeWebroot,o=bd(t);if(!r.onDemandTls||!n||o.length===0)return[];let u=e.bunBin??"/usr/local/bin/bun",i=r.tlsxVersion??"latest",c=t.productionCerts.certsDir,f=r.onDemandTlsEmail??`webmaster@${o[0]}`,g=`${u} ${R}/node_modules/@stacksjs/tlsx/dist/bin/cli.js`,a=o.join(","),p=o.join(" "),w=(e.slug||"app").replace(/[^a-z0-9._-]+/gi,"-"),l=`${Co}/renew-certs-${w}.sh`,s=Id(w),y=`rpx-cert-renew-${w}.timer`,E=t.wildcardCerts??[],T=E.length>0?"tlsx http-01 + dns-01 wildcard":"tlsx http-01",v=["#!/bin/sh",`# Generated by ts-cloud — issue/renew rpx gateway TLS certs via ${T}.`,"# The running gateway serves the challenge from $WEBROOT on :80, so this needs","# no downtime and no DNS credentials. Reloads the gateway only if a cert changed.","set -u",`CERTS='${c}'`,`WEBROOT='${n}'`,`EMAIL='${f}'`,`TLSX="${g}"`,`BUN='${u}'`,`DOMAINS='${a}'`,"DNS_ATTEMPTS='24'","DNS_DELAY_SECONDS='5'",`DNS_RESOLVERS='${B.join(" ")}'`,"dns_resolves() {",' d="$1"'," for ns in $DNS_RESOLVERS; do",` if $BUN --eval "const{Resolver}=require('node:dns');const r=new Resolver();r.setServers([process.argv[1]]);r.resolve4(process.argv[2],e=>process.exit(e?1:0))" "$ns" "$d" >/dev/null 2>&1; then`," return 0"," fi"," done",' getent ahosts "$d" >/dev/null 2>&1',"}","wait_for_dns() {",' d="$1"'," attempt=1",' while ! dns_resolves "$d"; do',' if [ "$attempt" -ge "$DNS_ATTEMPTS" ]; then',' echo "DNS for $d did not become resolvable after $DNS_ATTEMPTS attempts" >&2'," return 1"," fi",' echo "Waiting for public DNS before ACME: $d (attempt $attempt/$DNS_ATTEMPTS)"',' sleep "$DNS_DELAY_SECONDS"'," attempt=$((attempt + 1))"," done","}",'before=$(cat "$CERTS"/*.crt 2>/dev/null | sha256sum)',`for d in ${p}; do`,' if [ ! -s "$CERTS/$d.crt" ]; then',' if wait_for_dns "$d"; then',' $TLSX acme:issue -d "$d" --method http-01 --webroot "$WEBROOT" --dir "$CERTS" --prod --email "$EMAIL" || echo "issue $d failed (non-fatal)"'," else",' echo "issue $d skipped until DNS resolves (non-fatal)"'," fi"," # rpx reloads its SNI set when a PEM appears. Complete that reload"," # before the next hostname starts http-01, or :80 can disappear in"," # the middle of the following challenge on a multi-domain deploy.",` [ -s "$CERTS/$d.crt" ] && systemctl restart ${Je}`," fi","done",'$TLSX acme:renew --domains "$DOMAINS" --method http-01 --webroot "$WEBROOT" --dir "$CERTS" --days 30 --prod --email "$EMAIL" || echo "renew: some domains failed (non-fatal)"',...ee(E),'rm -f "$CERTS"/*.chain.crt','after=$(cat "$CERTS"/*.crt 2>/dev/null | sha256sum)',`[ "$before" = "$after" ] || systemctl restart ${Je}`].join(`
134
+ `),S=["[Unit]",`Description=Issue/renew rpx gateway TLS certs for ${w} (${T})`,`After=network-online.target ${Je}`,"Wants=network-online.target","","[Service]","Type=oneshot",`ExecStart=${l}`].join(`
135
+ `),_=["[Unit]",`Description=Daily rpx gateway TLS cert issuance/renewal for ${w}`,"","[Timer]","OnCalendar=*-*-* 03:30:00","RandomizedDelaySec=1h","Persistent=true","","[Install]","WantedBy=timers.target"].join(`
136
+ `);return[`mkdir -p ${n}`,...e.tenant?[]:[`(cd ${R} && ${u} add @stacksjs/tlsx@${i}) || true`],...b(l,v,"TS_CLOUD_RENEW_EOF"),`chmod +x ${l}`,...b(`/etc/systemd/system/${s}`,S,"TS_CLOUD_RENEW_SVC_EOF"),...b(`/etc/systemd/system/${y}`,_,"TS_CLOUD_RENEW_TIMER_EOF"),"systemctl daemon-reload",`systemctl enable --now ${y} || true`,`${l} || true`]}function V(e){let{config:t,proxy:r}=e,n=e.bunBin??"/usr/local/bin/bun",o=r.version??"latest",u=t.productionCerts.certsDir,i=(e.slug||"app").replace(/[^a-z0-9._-]+/gi,"-"),c=O(t,{slug:i}),f=J(K,u);if(e.tenant)return["set -euo pipefail",`if [ ! -x ${vt} ] || [ ! -d ${R}/node_modules/@stacksjs/rpx ]; then`,` echo "ts-cloud: no rpx gateway on this box. This project attaches to another project's compute, so it does not provision one - deploy the owning project first." >&2`," exit 1","fi",`mkdir -p ${K} ${u}`,`__tsc_rpx_before="$(sha256sum ${K}/${i}.json 2>/dev/null | cut -d' ' -f1 || true)"`,...D(`${K}/${i}.json`,c,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:n,preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),`__tsc_rpx_after="$(sha256sum ${K}/${i}.json 2>/dev/null | cut -d' ' -f1 || true)"`,`if [ "$__tsc_rpx_before" != "$__tsc_rpx_after" ] || ! systemctl is-active --quiet ${Je}; then`,` systemctl restart ${Je}`,"else",` echo "[ts-cloud] rpx routes for ${i} unchanged - leaving the shared gateway alone"`,"fi",...L(e)];let a=[`Environment=RPX_UPSTREAM_TIMEOUT=${r.upstreamTimeout??60}`];if(typeof r.maxUpstreamConns==="number")a.push(`Environment=RPX_MAX_UPSTREAM_CONNS=${r.maxUpstreamConns}`);let p=e.profile==="raspberry-pi"?Q:Z,w=r.memoryHigh??p.high,l=r.memoryMax??p.max;return["set -euo pipefail",`mkdir -p ${Co} ${K} ${u} ${R}`,`rm -rf ${R}.next ${R}.prev`,`mkdir -p ${R}.next`,`(cd ${R}.next && ${n} add @stacksjs/rpx@${o})`,`mv ${R} ${R}.prev`,`mv ${R}.next ${R}`,`rm -rf ${R}.prev`,`ln -sfn ${R}/node_modules ${Co}/node_modules`,...D(`${K}/${i}.json`,c,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:n,preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),...b(xp,f,"TS_CLOUD_RPX_EOF"),`${n} build --production --compile --outfile ${vt}.next ${xp}`,`chmod 0755 ${vt}.next`,`mv -f ${vt}.next ${vt}`,...b(`/etc/systemd/system/${Je}`,["[Unit]","Description=rpx reverse-proxy gateway (managed by ts-cloud)","After=network.target network-online.target","Wants=network-online.target","","[Service]","Type=simple",`ExecStart=${vt}`,`WorkingDirectory=${R}`,"Environment=BUN_INSTALL=/root/.bun","Environment=APP_ENV=production","Environment=NODE_ENV=production",...a,"MemoryAccounting=true",`MemoryHigh=${w}`,`MemoryMax=${l}`,"CPUWeight=500","IOWeight=500","OOMPolicy=stop","Restart=always","RestartSec=5","StartLimitIntervalSec=120","StartLimitBurst=5",`ExecStartPre=/bin/sh -c 'for p in 80 443; do fuser -k -n tcp "$p" 2>/dev/null || true; done; sleep 1'`,"LimitNOFILE=1048576","AmbientCapabilities=CAP_NET_BIND_SERVICE","","[Install]","WantedBy=multi-user.target"].join(`
137
+ `),"TS_CLOUD_RPX_UNIT_EOF"),"systemctl daemon-reload","systemctl disable --now bun-gateway.service 2>/dev/null || true","systemctl disable --now bun-gateway-renew.timer bun-gateway-renew.service 2>/dev/null || true","rm -f /etc/systemd/system/bun-gateway-renew.timer /etc/systemd/system/bun-gateway-renew.service","systemctl disable --now ts-cloud-nginx.service 2>/dev/null || true",`for timer in /etc/systemd/system/rpx-cert-renew-*.timer; do [ -e "$timer" ] || continue; unit="$(basename "$timer")"; tenant="\${unit#rpx-cert-renew-}"; tenant="\${tenant%.timer}"; [ -f "${K}/$tenant.json" ] && continue; systemctl disable --now "$unit" 2>/dev/null || true; rm -f "$timer" "/etc/systemd/system/rpx-cert-renew-$tenant.service" "${Co}/renew-certs-$tenant.sh"; done`,`systemctl enable ${Je}`,`systemctl restart ${Je}`,...L(e)]}function xe(e){let t=(e.slug||"app").replace(/[^a-z0-9._-]+/gi,"-"),r=O(e.config,{slug:t});return["set -euo pipefail",`mkdir -p ${K}`,...D(`${K}/${t}.json`,r,"TS_CLOUD_RPX_FRAGMENT_EOF",{bunBin:"/usr/local/bin/bun",preserveManagementDashboardRoutes:e.preserveManagementDashboardRoutes}),`systemctl restart ${Je}`]}
138
+ export{yo,So,Io,bo,yd,Np,Bp,lA,Sd,x,nr,HI,z,Co,R,xp,vt,Je,K,Id,bd,V,xe};