@stacksjs/ts-cloud 0.16.35 → 0.16.37

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 (157) hide show
  1. package/dist/aws/index.js +1 -1
  2. package/dist/aws/s3.d.ts +9 -1
  3. package/dist/bin/{chunk-c8439azc.js → chunk-017cjgwt.js} +2 -2
  4. package/dist/bin/{chunk-bqcqzc30.js → chunk-0ffqbgbs.js} +3 -3
  5. package/dist/bin/{chunk-cm1dsffd.js → chunk-1fjhc7w7.js} +4 -4
  6. package/dist/bin/{chunk-v3dck0e4.js → chunk-2bkxxset.js} +2 -2
  7. package/dist/bin/chunk-2gygs4xw.js +1 -0
  8. package/dist/bin/chunk-3gc8y0ta.js +1 -0
  9. package/dist/bin/{chunk-twmeg17s.js → chunk-4zxwtvvf.js} +1 -1
  10. package/dist/bin/{chunk-cnwncp50.js → chunk-5keq8y15.js} +4 -4
  11. package/dist/bin/{chunk-cn5tfp7t.js → chunk-6dm7x6g2.js} +12 -12
  12. package/dist/bin/{chunk-71jh460j.js → chunk-6r35j09y.js} +2 -2
  13. package/dist/bin/{chunk-vkdw6sxn.js → chunk-6sdffc79.js} +1 -1
  14. package/dist/bin/{chunk-exc2mqcc.js → chunk-71z22p71.js} +7 -7
  15. package/dist/bin/{chunk-9n3wmdd6.js → chunk-7xj986z7.js} +2 -2
  16. package/dist/bin/{chunk-bfzdbe4g.js → chunk-8gay06gq.js} +6 -6
  17. package/dist/bin/chunk-cdzas02w.js +1 -0
  18. package/dist/bin/{chunk-16kx70x7.js → chunk-dvyd4bxh.js} +12 -12
  19. package/dist/bin/chunk-dzx0w4zb.js +1 -0
  20. package/dist/bin/chunk-excjyrfx.js +1 -0
  21. package/dist/bin/{chunk-d5q09rxb.js → chunk-f76wtb7d.js} +27 -27
  22. package/dist/bin/chunk-fawjv4dc.js +1 -0
  23. package/dist/bin/{chunk-v3prk467.js → chunk-fxyr2wkt.js} +3 -3
  24. package/dist/bin/{chunk-r8e263ne.js → chunk-gd94501z.js} +5 -5
  25. package/dist/bin/chunk-gewxhd5z.js +1 -0
  26. package/dist/bin/chunk-gjadvs6y.js +1 -0
  27. package/dist/bin/{chunk-qbagghsb.js → chunk-hsek7hvh.js} +93 -116
  28. package/dist/bin/{chunk-svrgx20b.js → chunk-kj4gm2hk.js} +3 -3
  29. package/dist/bin/chunk-n84pv5ns.js +1 -0
  30. package/dist/bin/{chunk-5yv95sqs.js → chunk-na2j5twe.js} +1 -1
  31. package/dist/bin/{chunk-27mfrb34.js → chunk-nhz2az7v.js} +2 -2
  32. package/dist/bin/chunk-r263q1aa.js +1 -0
  33. package/dist/bin/chunk-rbjj0bcn.js +1 -0
  34. package/dist/bin/chunk-sp78dh4p.js +1 -0
  35. package/dist/bin/chunk-t844fst8.js +1 -0
  36. package/dist/bin/chunk-tkz95bkg.js +1 -0
  37. package/dist/bin/{chunk-a0cvd2ez.js → chunk-vg9et0kb.js} +3 -3
  38. package/dist/bin/{chunk-ar6bxdv4.js → chunk-w663zt0w.js} +3 -3
  39. package/dist/bin/chunk-xpzkwgb4.js +1 -0
  40. package/dist/bin/chunk-zswkrc34.js +1 -0
  41. package/dist/bin/cli.js +29 -29
  42. package/dist/bin/dashboard-server.js +1 -1
  43. package/dist/{chunk-tsaffke7.js → chunk-0fhz75e0.js} +3 -3
  44. package/dist/{chunk-tez0qfv9.js → chunk-1kfagk30.js} +1 -1
  45. package/dist/chunk-1mrqpf8e.js +1 -0
  46. package/dist/chunk-2bbm2q0s.js +1 -0
  47. package/dist/{chunk-kcj92xx7.js → chunk-2bvmp0br.js} +2 -2
  48. package/dist/{chunk-n4z75mk8.js → chunk-3bsf7585.js} +1 -1
  49. package/dist/chunk-46jnzd0d.js +1 -0
  50. package/dist/{chunk-7gvnrx46.js → chunk-5np6w0g7.js} +2 -2
  51. package/dist/{chunk-pdjap529.js → chunk-78r4k21k.js} +1 -1
  52. package/dist/{chunk-0kddm2mt.js → chunk-98xzqtnw.js} +1 -1
  53. package/dist/{chunk-annmzhbb.js → chunk-a74kqzne.js} +12 -12
  54. package/dist/{chunk-hvwqct5q.js → chunk-aqxp4s43.js} +1 -1
  55. package/dist/{chunk-ajn6xp8v.js → chunk-azt6sm0r.js} +1 -1
  56. package/dist/{chunk-906prq6g.js → chunk-fsdd2cj8.js} +2 -2
  57. package/dist/chunk-j5v7fgxz.js +1 -0
  58. package/dist/chunk-jfqm2c9q.js +1 -0
  59. package/dist/{chunk-mxw5gkd8.js → chunk-k92sywmh.js} +1 -1
  60. package/dist/{chunk-c97wwdtk.js → chunk-mbs6pr03.js} +6 -6
  61. package/dist/{chunk-fszn9rnn.js → chunk-mce0cpgf.js} +10 -10
  62. package/dist/{chunk-yzns0k2b.js → chunk-n0vz557v.js} +1 -1
  63. package/dist/{chunk-3nqtbyfp.js → chunk-n3p8vr8s.js} +3 -3
  64. package/dist/{chunk-p0ksat08.js → chunk-pjhag27y.js} +1 -1
  65. package/dist/chunk-ppr0bkgd.js +1 -0
  66. package/dist/{chunk-nr6z3e1p.js → chunk-rkjnr9fp.js} +3 -3
  67. package/dist/{chunk-jch6ks7a.js → chunk-svzmsd7g.js} +30 -30
  68. package/dist/{chunk-ctkzxq0d.js → chunk-t0azf37g.js} +2 -2
  69. package/dist/{chunk-9aaj9a0s.js → chunk-vmhfc7nk.js} +91 -114
  70. package/dist/{chunk-abadztwc.js → chunk-x7pnt65y.js} +55 -55
  71. package/dist/{chunk-my6yn5sd.js → chunk-ybdxrf42.js} +1 -1
  72. package/dist/{chunk-sekw1nyx.js → chunk-zg589zg1.js} +10 -10
  73. package/dist/deploy/index.js +1 -1
  74. package/dist/dns/index.js +1 -1
  75. package/dist/drivers/index.js +1 -1
  76. package/dist/generators/infrastructure.d.ts +7 -0
  77. package/dist/index.js +17 -17
  78. package/dist/operations/index.js +1 -1
  79. package/dist/protection/index.js +1 -1
  80. package/dist/spend/index.js +1 -1
  81. package/dist/ui/404.html +1 -1
  82. package/dist/ui/accept-invitation.html +1 -1
  83. package/dist/ui/access-denied.html +1 -1
  84. package/dist/ui/account/automation.html +6 -6
  85. package/dist/ui/account/security.html +2 -2
  86. package/dist/ui/applications/compose.html +6 -6
  87. package/dist/ui/applications/new.html +2 -2
  88. package/dist/ui/data/backups.html +6 -6
  89. package/dist/ui/data/services.html +6 -6
  90. package/dist/ui/data/volumes.html +6 -6
  91. package/dist/ui/index.html +5 -5
  92. package/dist/ui/infrastructure/topology.html +5 -5
  93. package/dist/ui/integrations.html +2 -2
  94. package/dist/ui/operations/alerts.html +6 -6
  95. package/dist/ui/operations/configuration.html +6 -6
  96. package/dist/ui/operations/jobs.html +6 -6
  97. package/dist/ui/operations/maintenance.html +6 -6
  98. package/dist/ui/operations/observability.html +6 -6
  99. package/dist/ui/operations/previews.html +6 -6
  100. package/dist/ui/operations/queue.html +5 -5
  101. package/dist/ui/operations/regions.html +6 -6
  102. package/dist/ui/operations/releases.html +5 -5
  103. package/dist/ui/operations/spend.html +6 -6
  104. package/dist/ui/operations/workloads.html +6 -6
  105. package/dist/ui/security.html +1 -1
  106. package/dist/ui/server/actions.html +6 -6
  107. package/dist/ui/server/activity.html +1 -1
  108. package/dist/ui/server/capacity.html +6 -6
  109. package/dist/ui/server/database.html +6 -6
  110. package/dist/ui/server/deployments.html +6 -6
  111. package/dist/ui/server/diagnostics.html +1 -1
  112. package/dist/ui/server/firewall.html +5 -5
  113. package/dist/ui/server/fleet.html +5 -5
  114. package/dist/ui/server/logs.html +5 -5
  115. package/dist/ui/server/metrics.html +6 -6
  116. package/dist/ui/server/security.html +1 -1
  117. package/dist/ui/server/services.html +2 -2
  118. package/dist/ui/server/sites.html +6 -6
  119. package/dist/ui/server/ssh-keys.html +6 -6
  120. package/dist/ui/server/team.html +6 -6
  121. package/dist/ui/server/terminal.html +2 -2
  122. package/dist/ui/serverless/alarms.html +6 -6
  123. package/dist/ui/serverless/assets.html +2 -2
  124. package/dist/ui/serverless/cost.html +2 -2
  125. package/dist/ui/serverless/data.html +6 -6
  126. package/dist/ui/serverless/deployments.html +2 -2
  127. package/dist/ui/serverless/firewall.html +2 -2
  128. package/dist/ui/serverless/functions.html +6 -6
  129. package/dist/ui/serverless/logs.html +6 -6
  130. package/dist/ui/serverless/metrics.html +2 -2
  131. package/dist/ui/serverless/queues.html +6 -6
  132. package/dist/ui/serverless/secrets.html +6 -6
  133. package/dist/ui/serverless/traces.html +6 -6
  134. package/dist/ui/serverless.html +6 -6
  135. package/package.json +3 -3
  136. package/dist/bin/chunk-3gg55dkc.js +0 -1
  137. package/dist/bin/chunk-3pbbc03q.js +0 -1
  138. package/dist/bin/chunk-691k55nw.js +0 -1
  139. package/dist/bin/chunk-7n84q4np.js +0 -1
  140. package/dist/bin/chunk-8pnm151y.js +0 -1
  141. package/dist/bin/chunk-dgxzk04r.js +0 -1
  142. package/dist/bin/chunk-e9mkg5dn.js +0 -1
  143. package/dist/bin/chunk-h5darb19.js +0 -1
  144. package/dist/bin/chunk-hfswzdzj.js +0 -1
  145. package/dist/bin/chunk-qfkfhg84.js +0 -1
  146. package/dist/bin/chunk-s4x1pb7n.js +0 -1
  147. package/dist/bin/chunk-tcstvf11.js +0 -1
  148. package/dist/bin/chunk-th86qyty.js +0 -1
  149. package/dist/bin/chunk-x8wvsf1k.js +0 -1
  150. package/dist/bin/chunk-y1qk3df2.js +0 -1
  151. package/dist/bin/chunk-zjw9sdr5.js +0 -1
  152. package/dist/chunk-0my5jwq8.js +0 -1
  153. package/dist/chunk-f2xqeers.js +0 -1
  154. package/dist/chunk-f4m9k2q6.js +0 -1
  155. package/dist/chunk-fxk29btr.js +0 -1
  156. package/dist/chunk-fz62nas4.js +0 -1
  157. package/dist/chunk-yhjagaff.js +0 -1
@@ -1,4 +1,4 @@
1
- import{Bd,L,d,g,_i,_d,Ld,me,p,te,Kr,yt,St,Xt,Jr,Mi}from"./chunk-9aaj9a0s.js";import{o}from"./chunk-jch6ks7a.js";import{_,Br,xr,u}from"./chunk-ctkzxq0d.js";import{fi}from"./chunk-50m6sq01.js";import{Pr,De}from"./chunk-ajn6xp8v.js";import{h}from"./chunk-mxw5gkd8.js";import{Oi}from"./chunk-pfav9qnt.js";import{readFileSync as Ks}from"node:fs";import{join as ja}from"node:path";function sg(e={}){let t=new Set;for(let r of Object.values(e)){if(!r.domain)continue;if(r.redirect||r.deploy==="server"||r.start)t.add(r.domain)}return t}function Ur(e){return e.replace(/\.$/,"").toLowerCase()}function Ri(e,t,r){let n=Ur(e.name),s=Ur(t),i=Ur(r),a=i===s?"@":i.endsWith(`.${s}`)?i.slice(0,-(s.length+1)):i;return n===i||n===a||a==="@"&&(n===""||n===s)}async function uc(e,t,r,n,s="A"){let i=await e.listRecords(t);if(!i.success)return[`could not list ${s} records: ${i.message||"unknown provider error"}`];let a=i.records.filter((m)=>m.type===s&&Ri(m,t,r)),c=a.findIndex((m)=>m.content===n);if(a.length<=1||c===-1)return[];let l=[];for(let[m,S]of a.entries()){if(m===c)continue;let b=await e.deleteRecord(t,S);if(!b.success)l.push(`could not remove stale ${S.name} ${s} ${S.content}: ${b.message||"unknown provider error"}`)}return l}function Ee(e){if(!e)return;let t=e.trim();if(!t)return;if(!t.includes("/"))return t;let[r]=t.split("/");if(!r.endsWith("::"))return r||void 0;return`${r}1`}var gc=new Set(["mail","smtp","imap","mx"]);function Ln(e){return!gc.has(e.split(".")[0]??"")}async function og(e){let{provider:t,zone:r,fqdn:n,ipv4:s,ipv6:i,ttl:a=600}=e,c={published:[],warnings:[]},l=[{type:"A",content:s}];if(i&&Ln(n))l.push({type:"AAAA",content:i});for(let{type:m,content:S}of l){let b=await t.upsertRecord(r,{name:n,type:m,content:S,ttl:a});if(b?.success===!1){c.warnings.push(`${n} → ${S} failed: ${b.error||b.message||"unknown error"}`);continue}if(c.warnings.push(...(await uc(t,r,n,S,m)).map((y)=>`${n} cleanup: ${y}`)),await pc(t,r,n,S,m))c.published.push({fqdn:n,type:m,content:S});else c.warnings.push(`${n} → ${S} reported success at ${t.name} but no matching ${m} record exists — create it manually: ${m} ${n} → ${S}`)}return c}async function pc(e,t,r,n,s){try{if(typeof e?.listRecords!=="function")return!0;let i=await e.listRecords(t);if(!i?.success||!Array.isArray(i.records))return!0;return i.records.some((a)=>{let c=typeof a?.name==="string"?a.name.replace(/\.$/,""):"";return a?.type===s&&c===r&&a?.content===n})}catch{return!0}}function Hr(e){return(Buffer.isBuffer(e)?e.toString("utf8"):typeof e==="string"?e:"").replace(/(^|\n)(\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=).*$/gm,"$1$2[redacted]").replace(/encrypted:[A-Za-z0-9+/=]+/g,"encrypted:[redacted]").trim().slice(-8000)}function $n(e,t){let r=Hr(e?.stderr),n=/line (\d+): syntax error/.exec(r);if(!n)return"";let s=Number(n[1]),i=t.split(`
1
+ import{Bd,L,d,g,_i,_d,Ld,me,p,te,Kr,yt,St,Xt,Jr,Mi}from"./chunk-vmhfc7nk.js";import{o}from"./chunk-svzmsd7g.js";import{_,Br,xr,u}from"./chunk-t0azf37g.js";import{fi}from"./chunk-50m6sq01.js";import{Pr,De}from"./chunk-azt6sm0r.js";import{h}from"./chunk-k92sywmh.js";import{Oi}from"./chunk-pfav9qnt.js";import{readFileSync as Ks}from"node:fs";import{join as ja}from"node:path";function sg(e={}){let t=new Set;for(let r of Object.values(e)){if(!r.domain)continue;if(r.redirect||r.deploy==="server"||r.start)t.add(r.domain)}return t}function Ur(e){return e.replace(/\.$/,"").toLowerCase()}function Ri(e,t,r){let n=Ur(e.name),s=Ur(t),i=Ur(r),a=i===s?"@":i.endsWith(`.${s}`)?i.slice(0,-(s.length+1)):i;return n===i||n===a||a==="@"&&(n===""||n===s)}async function uc(e,t,r,n,s="A"){let i=await e.listRecords(t);if(!i.success)return[`could not list ${s} records: ${i.message||"unknown provider error"}`];let a=i.records.filter((m)=>m.type===s&&Ri(m,t,r)),c=a.findIndex((m)=>m.content===n);if(a.length<=1||c===-1)return[];let l=[];for(let[m,S]of a.entries()){if(m===c)continue;let b=await e.deleteRecord(t,S);if(!b.success)l.push(`could not remove stale ${S.name} ${s} ${S.content}: ${b.message||"unknown provider error"}`)}return l}function Ee(e){if(!e)return;let t=e.trim();if(!t)return;if(!t.includes("/"))return t;let[r]=t.split("/");if(!r.endsWith("::"))return r||void 0;return`${r}1`}var gc=new Set(["mail","smtp","imap","mx"]);function Ln(e){return!gc.has(e.split(".")[0]??"")}async function og(e){let{provider:t,zone:r,fqdn:n,ipv4:s,ipv6:i,ttl:a=600}=e,c={published:[],warnings:[]},l=[{type:"A",content:s}];if(i&&Ln(n))l.push({type:"AAAA",content:i});for(let{type:m,content:S}of l){let b=await t.upsertRecord(r,{name:n,type:m,content:S,ttl:a});if(b?.success===!1){c.warnings.push(`${n} → ${S} failed: ${b.error||b.message||"unknown error"}`);continue}if(c.warnings.push(...(await uc(t,r,n,S,m)).map((y)=>`${n} cleanup: ${y}`)),await pc(t,r,n,S,m))c.published.push({fqdn:n,type:m,content:S});else c.warnings.push(`${n} → ${S} reported success at ${t.name} but no matching ${m} record exists — create it manually: ${m} ${n} → ${S}`)}return c}async function pc(e,t,r,n,s){try{if(typeof e?.listRecords!=="function")return!0;let i=await e.listRecords(t);if(!i?.success||!Array.isArray(i.records))return!0;return i.records.some((a)=>{let c=typeof a?.name==="string"?a.name.replace(/\.$/,""):"";return a?.type===s&&c===r&&a?.content===n})}catch{return!0}}function Hr(e){return(Buffer.isBuffer(e)?e.toString("utf8"):typeof e==="string"?e:"").replace(/(^|\n)(\s*(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=).*$/gm,"$1$2[redacted]").replace(/encrypted:[A-Za-z0-9+/=]+/g,"encrypted:[redacted]").trim().slice(-8000)}function $n(e,t){let r=Hr(e?.stderr),n=/line (\d+): syntax error/.exec(r);if(!n)return"";let s=Number(n[1]),i=t.split(`
2
2
  `),a=Math.max(1,s-12),c=Math.min(i.length,s+2),l=i.slice(a-1,c).map((m,S)=>{let b=a+S,y=/^[A-Z_][A-Z0-9_]*=/.test(m)?m.replace(/=.*/,"=<redacted>"):m;return` ${String(b).padStart(4)}${b===s?" >":" "} ${y}`});return["",`The deploy script bash rejected is ${i.length} lines; it stopped at ${s}.`,...l].join(`
3
3
  `)}function Lt(e){let t=e,r=typeof t?.status==="number"?` (exit ${t.status})`:"",n=t?.signal?` (signal ${t.signal})`:"",s=[Hr(t?.stderr),Hr(t?.stdout)].filter(Boolean).join(`
4
4
  `);return`Remote SSH command failed${r}${n}${s?`
@@ -8,7 +8,7 @@ ${e.slice(-ns)}`:e}function $t(e,t){let r=e.filter((a)=>a.status!=="Success");if
8
8
  ${Di(c)}`:l}),s=r.length-n.length,i=s>0?[`(+${s} more instance${s===1?"":"s"} failed)`]:[];return[t,...n,...i].join(`
9
9
  `)}function Ii(e){let t=2166136261,r=16777619;for(let n=0;n<e.length;n++){let s=e.charCodeAt(n);t=Math.imul(t^s,16777619)>>>0,r=Math.imul(r+s,2246822507)>>>0}return`${t.toString(36)}${r.toString(36)}`.padEnd(12,"0").slice(0,12)}function ag(e,t,r={}){let n=r.mode??"testing",s=r.maxAge??604800,i=r.mx?.length?r.mx:[e.hostname],a=`${["version: STSv1",`mode: ${n}`,...i.map((l)=>`mx: ${l}`),`max_age: ${s}`].join(`\r
10
10
  `)}\r
11
- `,c=Ii(a);return{host:`mta-sts.${t}`,body:a,id:c,txt:`v=STSv1; id=${c}`}}var cg=8461;function rh(e,t=cg){return[`const policy = ${JSON.stringify(e.body)}`,"Bun.serve({",` port: ${t},`," hostname: '127.0.0.1',"," fetch(req) {"," const { pathname } = new URL(req.url)"," if (pathname !== '/.well-known/mta-sts.txt') return new Response('Not Found', { status: 404 })"," return new Response(policy, { headers: { 'content-type': 'text/plain; charset=utf-8' } })"," },","})"].join(`
11
+ `,c=Ii(a);return{host:`mta-sts.${t}`,body:a,id:c,txt:`v=STSv1; id=${c}`}}var cg=8461;function nh(e,t=cg){return[`const policy = ${JSON.stringify(e.body)}`,"Bun.serve({",` port: ${t},`," hostname: '127.0.0.1',"," fetch(req) {"," const { pathname } = new URL(req.url)"," if (pathname !== '/.well-known/mta-sts.txt') return new Response('Not Found', { status: 404 })"," return new Response(policy, { headers: { 'content-type': 'text/plain; charset=utf-8' } })"," },","})"].join(`
12
12
  `)}var bt={php:"php.net",nginx:"nginx.org",composer:"getcomposer.org",mysql:"mysql.com",mariadb:"mariadb.com/server",postgres:"postgresql.org",vitess:"vitess.io",etcd:"etcd.io",redis:"redis.io",memcached:"memcached.org",meilisearch:"meilisearch.com",mail:"github.com/mail-os/mail",git:"git-scm.com",certbot:"certbot.eff.org",bun:"bun.sh",node:"nodejs.org",deno:"deno.land"};var v="/opt/pantry";function kt(e){return`'${e.split("'").join("'\\''")}'`}function yr(e={}){return["export DEBIAN_FRONTEND=noninteractive","export PANTRY_SERVICE_SCOPE=system","export PANTRY_INSTALL_DIR=/usr/local/bin",e.version?`export PANTRY_VERSION=${kt(e.version)}`:'export PANTRY_VERSION="${PANTRY_VERSION:-latest}"',"command -v curl >/dev/null 2>&1 || (apt-get update -y && apt-get install -y curl ca-certificates)","command -v unzip >/dev/null 2>&1 || (apt-get update -y && apt-get install -y unzip)",'OS=$(uname -s | tr \'[:upper:]\' \'[:lower:]\'); ARCH=$(uname -m); case "$ARCH" in x86_64|amd64) ARCH=x64 ;; arm64|aarch64) ARCH=arm64 ;; esac; ZIP="pantry-${OS}-${ARCH}.zip"; if [ "${PANTRY_VERSION:-latest}" = latest ]; then REL=\'latest/download\'; else REL="download/v${PANTRY_VERSION}"; fi; TMP=$(mktemp -d); curl -fsSL -o "${TMP}/${ZIP}" "https://github.com/pantry-pm/pantry/releases/${REL}/${ZIP}"; unzip -o "${TMP}/${ZIP}" -d /usr/local/bin; chmod +x /usr/local/bin/pantry; rm -rf "${TMP}"','export PATH="/usr/local/bin:$PATH"',"mkdir -p /opt/pantry"]}function _e(){return'eval "$(cd /opt/pantry && pantry env 2>/dev/null)" || true'}function fe(e){if(e.length===0)return[];return[`(cd /opt/pantry && pantry install ${[...new Set(e)].map(kt).join(" ")})`]}function Bt(e){return[...new Set(e)].flatMap((t)=>[`(cd /opt/pantry && pantry start ${kt(t)})`,`(cd /opt/pantry && pantry enable ${kt(t)})`])}var re="/etc/mail",he="mail-server",Nr="mail",kr=`${v}/pantry/.bin`;function Se(e){return`'${String(e).split("'").join("'\\''")}'`}function ke(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,"\\\"")}"`}function Ai(e){return[e.version?`${bt.mail}@${e.version}`:bt.mail]}function Mr(e,t){return`${re}/dkim/${t}.${e.dkim.selector}.pem`}function ss(e){return`${e.storagePath}/smtp.db`}function Li(e){let t=["# Generated by ts-cloud. Edits are overwritten on the next deploy —","# change `services.mail` in the cloud config instead.","","[server]",`host = ${ke(e.bindAddress)}`,`port = ${e.ports.smtp}`,`hostname = ${ke(e.hostname)}`];if(e.domains.length>0)t.push(`local_domains = ${ke(e.domains.join(", "))}`);if(e.mode==="catcher")t.push("catch_all = true");if(t.push(`max_message_size = ${e.maxMessageSize}`,"","[tls]",`enabled = ${e.tls.enabled}`),e.tls.certPath)t.push(`cert_path = ${ke(e.tls.certPath)}`);if(e.tls.keyPath)t.push(`key_path = ${ke(e.tls.keyPath)}`);if(t.push("","[auth]",`enabled = ${e.mode==="server"}`,"","[antispam]",`dnsbl = ${e.spam.dnsbl}`,`greylist = ${e.spam.greylist}`,`spam_filter = ${e.spam.enabled}`,`junk_score = ${e.spam.junkScore}`,`reject_score = ${e.spam.rejectScore}`,"","[dkim]",`rotation_enabled = ${e.dkim.enabled&&e.dkim.rotate}`,`rotation_interval_days = ${e.dkim.rotateIntervalDays}`,"","[acme]",`enabled = ${e.tls.acme}`),e.tls.acmeEmail)t.push(`email = ${ke(e.tls.acmeEmail)}`);if(t.push("","[webhook]",`enabled = ${!!e.webhookUrl}`),e.webhookUrl)t.push(`url = ${ke(e.webhookUrl)}`);return t.push(""),t.join(`
13
13
  `)}function Ui(e){let t=["# Generated by ts-cloud. See /etc/mail/config.toml for the rest.",`SMTP_DB_PATH=${ss(e)}`,`SMTP_DELIVERY_METHOD=${e.delivery==="ses"?"ses":"direct"}`];if(e.delivery==="ses")t.push(`SMTP_SES_REGION=${e.sesRegion}`);if(e.ports.submission)t.push(`SUBMISSION_PORT=${e.ports.submission}`);if(e.ports.submissions)t.push(`SMTPS_PORT=${e.ports.submissions}`);if(e.ports.imap)t.push("IMAP_ENABLED=1",`IMAP_PORT=${e.ports.imap}`);if(e.webmail.enabled)t.push("SMTP_ENABLE_WEBMAIL=true",`SMTP_WEBMAIL_PORT=${e.webmail.port}`,`SMTP_WEBMAIL_SECURE_COOKIES=${e.tls.enabled}`);if(e.tls.requireForAuth)t.push("SMTP_REQUIRE_TLS_FOR_AUTH=true");if(e.dkim.enabled&&e.domains.length>0){let[r,...n]=e.domains;if(t.push(`DKIM_DOMAIN=${r}`,`DKIM_SELECTOR=${e.dkim.selector}`,`DKIM_PRIVATE_KEY_PATH=${Mr(e,r)}`),n.length>0){let s=n.map((i)=>`${i}:${e.dkim.selector}:${Mr(e,i)}`);t.push(`DKIM_EXTRA_KEYS=${s.join(",")}`)}}return t.push(""),t.join(`
14
14
  `)}function Hi(e){let t=[e.ports.smtp,e.ports.submission,e.ports.submissions,e.ports.imap,e.ports.imaps].some((r)=>typeof r==="number"&&r<1024);return["[Unit]","Description=Mail server (SMTP/IMAP + webmail)","After=network-online.target","Wants=network-online.target","","[Service]","Type=simple",`User=${he}`,`Group=${he}`,`WorkingDirectory=${e.storagePath}`,`EnvironmentFile=${re}/mail.env`,`Environment="PATH=${kr}:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"`,`ExecStart=${kr}/mail serve --config ${re}/config.toml`,"Restart=always","RestartSec=5","LimitNOFILE=65536",...t?["AmbientCapabilities=CAP_NET_BIND_SERVICE","CapabilityBoundingSet=CAP_NET_BIND_SERVICE"]:[],"NoNewPrivileges=true","PrivateTmp=true","ProtectSystem=strict","ProtectHome=true","ProtectKernelTunables=true","ProtectKernelModules=true","ProtectControlGroups=true",`ReadWritePaths=${e.storagePath} ${re}`,"","[Install]","WantedBy=multi-user.target",""].join(`
@@ -22,7 +22,7 @@ EOF`,`DISK_PCT=$(df -P / | awk 'NR==2{gsub("%","",$5); print $5}')`,"UPTIME_SEC=
22
22
  `)}
23
23
  `}return`${["server {"," listen 80;"," listen [::]:80;",` server_name ${r};`,...n,"}"].join(`
24
24
  `)}
25
- `}function Xr(e){let t=`/etc/nginx/sites-available/${e.siteName}`,r=`/etc/nginx/sites-enabled/${e.siteName}`,n=ya(e),s=[...Qr()];if(e.auth){let i=hs(e.siteName),a=(m)=>m.split("'").join("'\\''"),c=a(e.auth.password),l=a(e.auth.username);s.push(`TS_CLOUD_HTPASS=$(openssl passwd -apr1 '${c}')`,`printf '%s:%s\\n' '${l}' "$TS_CLOUD_HTPASS" > ${i}`,`chmod 640 ${i}`,`chown root:www-data ${i} 2>/dev/null || true`)}return s.push(`cat > ${t} <<'TS_CLOUD_NGINX_EOF'`,n.replace(/\n$/,""),"TS_CLOUD_NGINX_EOF",`ln -sf ${t} ${r}`,"rm -f /etc/nginx/sites-enabled/default",`${Ae} -t`,"systemctl reload ts-cloud-nginx 2>/dev/null || systemctl restart ts-cloud-nginx 2>/dev/null || true"),s}var Ae="/usr/local/bin/ts-cloud-nginx";function Qr(e="/opt/pantry"){return[...yr(),...fe(["nginx.org"]),"mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled /var/log/nginx /var/lib/nginx/body /var/lib/nginx/proxy /var/lib/nginx/fastcgi /var/lib/nginx/uwsgi /var/lib/nginx/scgi","getent passwd www-data >/dev/null || useradd --system --no-create-home --shell /usr/sbin/nologin www-data","chown -R www-data:www-data /var/lib/nginx /var/log/nginx",`cat > ${Ae} <<'TS_CLOUD_NGINXBIN_EOF'`,"#!/bin/sh",`eval "$(cd ${e} && pantry env 2>/dev/null)"`,"NGINX_BIN=/opt/pantry/pantry/.bin/nginx",'if [ ! -x "$NGINX_BIN" ]; then NGINX_BIN=$(command -v nginx || true); fi','if [ -z "$NGINX_BIN" ]; then echo "nginx binary not found; run pantry install nginx.org or install nginx explicitly" >&2; exit 1; fi','exec "$NGINX_BIN" -c /etc/nginx/nginx.conf "$@"',"TS_CLOUD_NGINXBIN_EOF",`chmod +x ${Ae}`,"cat > /etc/nginx/fastcgi_params <<'TS_CLOUD_FCGI_EOF'","fastcgi_param QUERY_STRING $query_string;","fastcgi_param REQUEST_METHOD $request_method;","fastcgi_param CONTENT_TYPE $content_type;","fastcgi_param CONTENT_LENGTH $content_length;","fastcgi_param SCRIPT_NAME $fastcgi_script_name;","fastcgi_param REQUEST_URI $request_uri;","fastcgi_param DOCUMENT_URI $document_uri;","fastcgi_param DOCUMENT_ROOT $document_root;","fastcgi_param SERVER_PROTOCOL $server_protocol;","fastcgi_param REQUEST_SCHEME $scheme;","fastcgi_param HTTPS $https if_not_empty;","fastcgi_param GATEWAY_INTERFACE CGI/1.1;","fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;","fastcgi_param REMOTE_ADDR $remote_addr;","fastcgi_param REMOTE_PORT $remote_port;","fastcgi_param SERVER_ADDR $server_addr;","fastcgi_param SERVER_PORT $server_port;","fastcgi_param SERVER_NAME $server_name;","fastcgi_param REDIRECT_STATUS 200;","TS_CLOUD_FCGI_EOF","cat > /etc/nginx/nginx.conf <<'TS_CLOUD_NGINXCONF_EOF'","user www-data;","worker_processes auto;","pid /run/nginx.pid;","error_log /var/log/nginx/error.log;","events { worker_connections 1024; }","http {"," default_type application/octet-stream;"," types {"," text/html html htm;"," text/css css;"," application/javascript js;"," application/json json;"," image/svg+xml svg;"," image/png png;"," image/jpeg jpg jpeg;"," image/gif gif;"," image/x-icon ico;"," image/webp webp;"," font/woff2 woff2;"," font/woff woff;"," text/plain txt;"," }"," access_log /var/log/nginx/access.log;"," sendfile on;"," tcp_nopush on;"," keepalive_timeout 65;"," server_tokens off;"," client_max_body_size 100m;"," client_body_temp_path /var/lib/nginx/body;"," proxy_temp_path /var/lib/nginx/proxy;"," fastcgi_temp_path /var/lib/nginx/fastcgi;"," uwsgi_temp_path /var/lib/nginx/uwsgi;"," scgi_temp_path /var/lib/nginx/scgi;"," server {"," listen 80 default_server;"," listen [::]:80 default_server;"," server_name _;"," return 444;"," }"," include /etc/nginx/sites-enabled/*;","}","TS_CLOUD_NGINXCONF_EOF","cat > /etc/systemd/system/ts-cloud-nginx.service <<'TS_CLOUD_NGINXUNIT_EOF'","[Unit]","Description=ts-cloud nginx (pantry)","After=network.target","","[Service]","Type=simple",`ExecStartPre=${Ae} -t`,`ExecStart=${Ae} -g 'daemon off;'`,`ExecReload=${Ae} -s reload`,"Restart=always","RestartSec=3","","[Install]","WantedBy=multi-user.target","TS_CLOUD_NGINXUNIT_EOF","systemctl daemon-reload","systemctl enable ts-cloud-nginx","ls /etc/nginx/sites-enabled/* >/dev/null 2>&1 && systemctl restart ts-cloud-nginx || true"]}function Ct(e,t){return!e.events||e.events.includes(t)}async function Zr(e,t,r,n={}){if(!e)return[];let s=n.fetchImpl??globalThis.fetch,i=[],a=[],c=(l,m)=>s(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(m)}).catch(()=>{return});if(e.slack?.webhookUrl&&Ct(e,t))i.push("slack"),a.push(c(e.slack.webhookUrl,{text:r}));if(e.discord?.webhookUrl&&Ct(e,t))i.push("discord"),a.push(c(e.discord.webhookUrl,{content:r}));if(e.telegram?.botToken&&e.telegram.chatId&&Ct(e,t)){i.push("telegram");let l=`https://api.telegram.org/bot${e.telegram.botToken}/sendMessage`;a.push(c(l,{chat_id:e.telegram.chatId,text:r}))}if(e.webhook?.url&&Ct(e,t))if(i.push("webhook"),(e.webhook.method||"POST")==="GET"){let l=e.webhook.url.includes("?")?"&":"?",m=`${e.webhook.url}${l}event=${encodeURIComponent(t)}&message=${encodeURIComponent(r)}`;a.push(s(m,{method:"GET"}).catch(()=>{return}))}else a.push(c(e.webhook.url,{event:t,message:r}));if(e.email?.to&&Ct(e,t))i.push("email"),a.push($a(e.email,t,r));return await Promise.all(a),i}async function $a(e,t,r){try{let n=await import("./chunk-f4m9k2q6.js");await n.email.send({to:e.to,from:e.from,subject:`[ts-cloud] ${t}`,text:r})}catch{}}function Ss(e,t){return t??e}function Ze(e){if(!e)return[];let t=[];if(e.slack?.webhookUrl)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"text\\":\\"$MSG\\"}" ${e.slack.webhookUrl} || true`);if(e.discord?.webhookUrl)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"content\\":\\"$MSG\\"}" ${e.discord.webhookUrl} || true`);if(e.telegram?.botToken&&e.telegram.chatId)t.push(`curl -fsS -X POST "https://api.telegram.org/bot${e.telegram.botToken}/sendMessage" --data-urlencode "chat_id=${e.telegram.chatId}" --data-urlencode "text=$MSG" || true`);if(e.webhook?.url)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"message\\":\\"$MSG\\"}" ${e.webhook.url} || true`);if(t.length===0)return[];return["cat > /usr/local/bin/ts-cloud-notify <<'TS_CLOUD_NOTIFY_EOF'","#!/bin/bash",'MSG="$1"',...t,"TS_CLOUD_NOTIFY_EOF","chmod +x /usr/local/bin/ts-cloud-notify"]}import{isIP as vs}from"node:net";var Un="/etc/rpx/certs",ys="/etc/rpx/local-ca",ba="rpx-root-ca.crt";function Wt(e=ys){return`${e.replace(/\/+$/,"")}/${ba}`}function Qn(e){if(e.toLowerCase().endsWith(".local"))return!1;return e.split(".").length===2}function Hn(e,t={}){let r=new Set;for(let n of Object.values(e)){if(!n?.domain)continue;if(u(n)==="bucket")continue;r.add(n.domain)}if(t.autoWww!==!1)for(let n of[...r]){if(!Qn(n))continue;r.add(`www.${n}`)}return[...r]}var Ta=["1.1.1.1","8.8.8.8"],wa="/var/www/acme-challenge";function Ca(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 Pa(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 tn(e){if(!e)return;return e.tls??"local-ca"}function bs(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 Ts(e){if(e.cloud?.provider!=="ssh")return;return e.ssh?.profile}function Ea(e){let t=e.trim().toLowerCase();if(!t||t.startsWith("*")||t.includes(":")||t.includes("/")||t.includes(" "))return!1;if(vs(t)!==0)return!1;return t.endsWith(".local")||!t.includes(".")}function Ra(e,t,r){let n=[],s=new Set,i=(m,S)=>{let b=m.trim().toLowerCase();if(!b||s.has(b))return;s.add(b),n.push({host:b,source:S})},a=t.hostname?.trim(),c=(a||`${r}.local`).toLowerCase();if(i(c,a?"ssh.lan.hostname":`the ssh provider's default LAN name for project "${r}"`),Object.entries(e).some(([m,S])=>!!S&&Jr(m)))i(`dashboard.${c}`,"the management dashboard site");for(let[m,S]of Object.entries(e)){if(!S?.domain)continue;if(u(S)==="bucket")continue;if(!Ea(S.domain))continue;i(S.domain,`site "${m}"`)}return n}function xa(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 Oa(e,t){if(!t?.enabled)return;for(let{host:r,source:n}of e){let s=xa(r,t.allowedSuffixes);if(!s)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 "${s}", 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 lg(e){if(!e||e==="/")return;let t=`/${e}`.replace(/\/+/g,"/").replace(/\/+$/,"");if(!t.startsWith("/"))t=`/${t}`;return t===""||t==="/"?void 0:t}function mc(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 Da(e,t){if(!t||t.length===0)return`localhost:${e}`;return t.map((r)=>`${r.privateIp??r.publicIp}:${e}`)}function ws(e,t,r){let n=(t.wwwRoot??"/var/www").replace(/\/+$/,""),s=t.slug??"app",i=t.proxy.certsDir??Un,a=t.proxy.loadBalancer,c=[],l=new Set;for(let[y,f]of Object.entries(e)){if(!f||!f.domain)continue;let T=u(f);if(T==="bucket")continue;let D=lg(f.path),R=mc(f.domain,D),C=Pa(f);if(T==="redirect"){c.push({to:f.domain,path:D,redirect:Ca(f.redirect),id:R,...C?{auth:C}:{}}),l.add(f.domain);continue}if(T==="proxy"){let P=xr(f);if(P.length===0)continue;let w=P.length===1?P[0]:P;c.push({to:f.domain,path:D,from:w,id:R,...C?{auth:C}:{},...Array.isArray(w)&&a?{loadBalancer:a}:{}}),l.add(f.domain);continue}if(T==="server-app"){if(typeof f.port!=="number")continue;let P=Da(f.port,r);c.push({to:f.domain,path:D,from:P,id:R,...C?{auth:C}:{},...Array.isArray(P)&&a?{loadBalancer:a}:{}})}else c.push({to:f.domain,path:D,static:{dir:`${n}/${s}-${y}/current`,spa:f.spa??!1,pathRewriteStyle:f.pathRewriteStyle??"directory"},cleanUrls:f.pathRewriteStyle!=="flat",...C?{auth:C}:{},id:R});l.add(f.domain)}if(t.proxy.autoWww!==!1)for(let y of[...l]){if(!Qn(y))continue;let f=`www.${y}`;if(l.has(f))continue;c.push({to:f,redirect:{to:`https://${y}`},id:mc(f)}),l.add(f)}c.sort((y,f)=>{if(y.to!==f.to)return y.to.localeCompare(f.to);return(f.path?.length??0)-(y.path?.length??0)});let m={proxies:c,productionCerts:{certsDir:i,certsDirServerNames:[...l]},https:!0,hostsManagement:!1,cleanup:{hosts:!1,certs:!1}};if(t.proxy.onDemandTls&&l.size>0)m.onDemandTls={enabled:!0,allowedSuffixes:[...l],email:t.proxy.onDemandTlsEmail,certsDir:i,staging:t.proxy.onDemandTlsStaging??!1};if(t.proxy.onDemandTls)m.acmeChallengeWebroot=t.proxy.acmeWebroot??wa;let S=t.proxy.cdn,b=S?.frontedHosts?.length?S.frontedHosts:[...l];if(S?.secret&&b.length>0)m.originGuard={header:S.secretHeader??"X-Origin-Verify",value:S.secret,hosts:b};if(t.lan)if(tn(t.lan)==="off")m.https=!1;else{let y=Ra(e,t.lan,s);Oa(y,m.onDemandTls);let f=t.lan.ip&&vs(t.lan.ip)!==0?[t.lan.ip]:[];m.localCa={dir:ys,hosts:y.map((T)=>T.host),...f.length>0?{ips:f}:{},installTrust:!0}}return m}function X(e,t){return ws(e,t)}function Ir(e,t,r){return ws(e,r,t)}function nh(e){return`// Generated by ts-cloud — rpx reverse-proxy gateway.
25
+ `}function Xr(e){let t=`/etc/nginx/sites-available/${e.siteName}`,r=`/etc/nginx/sites-enabled/${e.siteName}`,n=ya(e),s=[...Qr()];if(e.auth){let i=hs(e.siteName),a=(m)=>m.split("'").join("'\\''"),c=a(e.auth.password),l=a(e.auth.username);s.push(`TS_CLOUD_HTPASS=$(openssl passwd -apr1 '${c}')`,`printf '%s:%s\\n' '${l}' "$TS_CLOUD_HTPASS" > ${i}`,`chmod 640 ${i}`,`chown root:www-data ${i} 2>/dev/null || true`)}return s.push(`cat > ${t} <<'TS_CLOUD_NGINX_EOF'`,n.replace(/\n$/,""),"TS_CLOUD_NGINX_EOF",`ln -sf ${t} ${r}`,"rm -f /etc/nginx/sites-enabled/default",`${Ae} -t`,"systemctl reload ts-cloud-nginx 2>/dev/null || systemctl restart ts-cloud-nginx 2>/dev/null || true"),s}var Ae="/usr/local/bin/ts-cloud-nginx";function Qr(e="/opt/pantry"){return[...yr(),...fe(["nginx.org"]),"mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled /var/log/nginx /var/lib/nginx/body /var/lib/nginx/proxy /var/lib/nginx/fastcgi /var/lib/nginx/uwsgi /var/lib/nginx/scgi","getent passwd www-data >/dev/null || useradd --system --no-create-home --shell /usr/sbin/nologin www-data","chown -R www-data:www-data /var/lib/nginx /var/log/nginx",`cat > ${Ae} <<'TS_CLOUD_NGINXBIN_EOF'`,"#!/bin/sh",`eval "$(cd ${e} && pantry env 2>/dev/null)"`,"NGINX_BIN=/opt/pantry/pantry/.bin/nginx",'if [ ! -x "$NGINX_BIN" ]; then NGINX_BIN=$(command -v nginx || true); fi','if [ -z "$NGINX_BIN" ]; then echo "nginx binary not found; run pantry install nginx.org or install nginx explicitly" >&2; exit 1; fi','exec "$NGINX_BIN" -c /etc/nginx/nginx.conf "$@"',"TS_CLOUD_NGINXBIN_EOF",`chmod +x ${Ae}`,"cat > /etc/nginx/fastcgi_params <<'TS_CLOUD_FCGI_EOF'","fastcgi_param QUERY_STRING $query_string;","fastcgi_param REQUEST_METHOD $request_method;","fastcgi_param CONTENT_TYPE $content_type;","fastcgi_param CONTENT_LENGTH $content_length;","fastcgi_param SCRIPT_NAME $fastcgi_script_name;","fastcgi_param REQUEST_URI $request_uri;","fastcgi_param DOCUMENT_URI $document_uri;","fastcgi_param DOCUMENT_ROOT $document_root;","fastcgi_param SERVER_PROTOCOL $server_protocol;","fastcgi_param REQUEST_SCHEME $scheme;","fastcgi_param HTTPS $https if_not_empty;","fastcgi_param GATEWAY_INTERFACE CGI/1.1;","fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;","fastcgi_param REMOTE_ADDR $remote_addr;","fastcgi_param REMOTE_PORT $remote_port;","fastcgi_param SERVER_ADDR $server_addr;","fastcgi_param SERVER_PORT $server_port;","fastcgi_param SERVER_NAME $server_name;","fastcgi_param REDIRECT_STATUS 200;","TS_CLOUD_FCGI_EOF","cat > /etc/nginx/nginx.conf <<'TS_CLOUD_NGINXCONF_EOF'","user www-data;","worker_processes auto;","pid /run/nginx.pid;","error_log /var/log/nginx/error.log;","events { worker_connections 1024; }","http {"," default_type application/octet-stream;"," types {"," text/html html htm;"," text/css css;"," application/javascript js;"," application/json json;"," image/svg+xml svg;"," image/png png;"," image/jpeg jpg jpeg;"," image/gif gif;"," image/x-icon ico;"," image/webp webp;"," font/woff2 woff2;"," font/woff woff;"," text/plain txt;"," }"," access_log /var/log/nginx/access.log;"," sendfile on;"," tcp_nopush on;"," keepalive_timeout 65;"," server_tokens off;"," client_max_body_size 100m;"," client_body_temp_path /var/lib/nginx/body;"," proxy_temp_path /var/lib/nginx/proxy;"," fastcgi_temp_path /var/lib/nginx/fastcgi;"," uwsgi_temp_path /var/lib/nginx/uwsgi;"," scgi_temp_path /var/lib/nginx/scgi;"," server {"," listen 80 default_server;"," listen [::]:80 default_server;"," server_name _;"," return 444;"," }"," include /etc/nginx/sites-enabled/*;","}","TS_CLOUD_NGINXCONF_EOF","cat > /etc/systemd/system/ts-cloud-nginx.service <<'TS_CLOUD_NGINXUNIT_EOF'","[Unit]","Description=ts-cloud nginx (pantry)","After=network.target","","[Service]","Type=simple",`ExecStartPre=${Ae} -t`,`ExecStart=${Ae} -g 'daemon off;'`,`ExecReload=${Ae} -s reload`,"Restart=always","RestartSec=3","","[Install]","WantedBy=multi-user.target","TS_CLOUD_NGINXUNIT_EOF","systemctl daemon-reload","systemctl enable ts-cloud-nginx","ls /etc/nginx/sites-enabled/* >/dev/null 2>&1 && systemctl restart ts-cloud-nginx || true"]}function Ct(e,t){return!e.events||e.events.includes(t)}async function Zr(e,t,r,n={}){if(!e)return[];let s=n.fetchImpl??globalThis.fetch,i=[],a=[],c=(l,m)=>s(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(m)}).catch(()=>{return});if(e.slack?.webhookUrl&&Ct(e,t))i.push("slack"),a.push(c(e.slack.webhookUrl,{text:r}));if(e.discord?.webhookUrl&&Ct(e,t))i.push("discord"),a.push(c(e.discord.webhookUrl,{content:r}));if(e.telegram?.botToken&&e.telegram.chatId&&Ct(e,t)){i.push("telegram");let l=`https://api.telegram.org/bot${e.telegram.botToken}/sendMessage`;a.push(c(l,{chat_id:e.telegram.chatId,text:r}))}if(e.webhook?.url&&Ct(e,t))if(i.push("webhook"),(e.webhook.method||"POST")==="GET"){let l=e.webhook.url.includes("?")?"&":"?",m=`${e.webhook.url}${l}event=${encodeURIComponent(t)}&message=${encodeURIComponent(r)}`;a.push(s(m,{method:"GET"}).catch(()=>{return}))}else a.push(c(e.webhook.url,{event:t,message:r}));if(e.email?.to&&Ct(e,t))i.push("email"),a.push($a(e.email,t,r));return await Promise.all(a),i}async function $a(e,t,r){try{let n=await import("./chunk-46jnzd0d.js");await n.email.send({to:e.to,from:e.from,subject:`[ts-cloud] ${t}`,text:r})}catch{}}function Ss(e,t){return t??e}function Ze(e){if(!e)return[];let t=[];if(e.slack?.webhookUrl)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"text\\":\\"$MSG\\"}" ${e.slack.webhookUrl} || true`);if(e.discord?.webhookUrl)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"content\\":\\"$MSG\\"}" ${e.discord.webhookUrl} || true`);if(e.telegram?.botToken&&e.telegram.chatId)t.push(`curl -fsS -X POST "https://api.telegram.org/bot${e.telegram.botToken}/sendMessage" --data-urlencode "chat_id=${e.telegram.chatId}" --data-urlencode "text=$MSG" || true`);if(e.webhook?.url)t.push(`curl -fsS -X POST -H 'Content-Type: application/json' -d "{\\"message\\":\\"$MSG\\"}" ${e.webhook.url} || true`);if(t.length===0)return[];return["cat > /usr/local/bin/ts-cloud-notify <<'TS_CLOUD_NOTIFY_EOF'","#!/bin/bash",'MSG="$1"',...t,"TS_CLOUD_NOTIFY_EOF","chmod +x /usr/local/bin/ts-cloud-notify"]}import{isIP as vs}from"node:net";var Un="/etc/rpx/certs",ys="/etc/rpx/local-ca",ba="rpx-root-ca.crt";function Wt(e=ys){return`${e.replace(/\/+$/,"")}/${ba}`}function Qn(e){if(e.toLowerCase().endsWith(".local"))return!1;return e.split(".").length===2}function Hn(e,t={}){let r=new Set;for(let n of Object.values(e)){if(!n?.domain)continue;if(u(n)==="bucket")continue;r.add(n.domain)}if(t.autoWww!==!1)for(let n of[...r]){if(!Qn(n))continue;r.add(`www.${n}`)}return[...r]}var Ta=["1.1.1.1","8.8.8.8"],wa="/var/www/acme-challenge";function Ca(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 Pa(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 tn(e){if(!e)return;return e.tls??"local-ca"}function bs(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 Ts(e){if(e.cloud?.provider!=="ssh")return;return e.ssh?.profile}function Ea(e){let t=e.trim().toLowerCase();if(!t||t.startsWith("*")||t.includes(":")||t.includes("/")||t.includes(" "))return!1;if(vs(t)!==0)return!1;return t.endsWith(".local")||!t.includes(".")}function Ra(e,t,r){let n=[],s=new Set,i=(m,S)=>{let b=m.trim().toLowerCase();if(!b||s.has(b))return;s.add(b),n.push({host:b,source:S})},a=t.hostname?.trim(),c=(a||`${r}.local`).toLowerCase();if(i(c,a?"ssh.lan.hostname":`the ssh provider's default LAN name for project "${r}"`),Object.entries(e).some(([m,S])=>!!S&&Jr(m)))i(`dashboard.${c}`,"the management dashboard site");for(let[m,S]of Object.entries(e)){if(!S?.domain)continue;if(u(S)==="bucket")continue;if(!Ea(S.domain))continue;i(S.domain,`site "${m}"`)}return n}function xa(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 Oa(e,t){if(!t?.enabled)return;for(let{host:r,source:n}of e){let s=xa(r,t.allowedSuffixes);if(!s)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 "${s}", 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 lg(e){if(!e||e==="/")return;let t=`/${e}`.replace(/\/+/g,"/").replace(/\/+$/,"");if(!t.startsWith("/"))t=`/${t}`;return t===""||t==="/"?void 0:t}function mc(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 Da(e,t){if(!t||t.length===0)return`localhost:${e}`;return t.map((r)=>`${r.privateIp??r.publicIp}:${e}`)}function ws(e,t,r){let n=(t.wwwRoot??"/var/www").replace(/\/+$/,""),s=t.slug??"app",i=t.proxy.certsDir??Un,a=t.proxy.loadBalancer,c=[],l=new Set;for(let[y,f]of Object.entries(e)){if(!f||!f.domain)continue;let T=u(f);if(T==="bucket")continue;let D=lg(f.path),R=mc(f.domain,D),C=Pa(f);if(T==="redirect"){c.push({to:f.domain,path:D,redirect:Ca(f.redirect),id:R,...C?{auth:C}:{}}),l.add(f.domain);continue}if(T==="proxy"){let P=xr(f);if(P.length===0)continue;let w=P.length===1?P[0]:P;c.push({to:f.domain,path:D,from:w,id:R,...C?{auth:C}:{},...Array.isArray(w)&&a?{loadBalancer:a}:{}}),l.add(f.domain);continue}if(T==="server-app"){if(typeof f.port!=="number")continue;let P=Da(f.port,r);c.push({to:f.domain,path:D,from:P,id:R,...C?{auth:C}:{},...Array.isArray(P)&&a?{loadBalancer:a}:{}})}else c.push({to:f.domain,path:D,static:{dir:`${n}/${s}-${y}/current`,spa:f.spa??!1,pathRewriteStyle:f.pathRewriteStyle??"directory"},cleanUrls:f.pathRewriteStyle!=="flat",...C?{auth:C}:{},id:R});l.add(f.domain)}if(t.proxy.autoWww!==!1)for(let y of[...l]){if(!Qn(y))continue;let f=`www.${y}`;if(l.has(f))continue;c.push({to:f,redirect:{to:`https://${y}`},id:mc(f)}),l.add(f)}c.sort((y,f)=>{if(y.to!==f.to)return y.to.localeCompare(f.to);return(f.path?.length??0)-(y.path?.length??0)});let m={proxies:c,productionCerts:{certsDir:i,certsDirServerNames:[...l]},https:!0,hostsManagement:!1,cleanup:{hosts:!1,certs:!1}};if(t.proxy.onDemandTls&&l.size>0)m.onDemandTls={enabled:!0,allowedSuffixes:[...l],email:t.proxy.onDemandTlsEmail,certsDir:i,staging:t.proxy.onDemandTlsStaging??!1};if(t.proxy.onDemandTls)m.acmeChallengeWebroot=t.proxy.acmeWebroot??wa;let S=t.proxy.cdn,b=S?.frontedHosts?.length?S.frontedHosts:[...l];if(S?.secret&&b.length>0)m.originGuard={header:S.secretHeader??"X-Origin-Verify",value:S.secret,hosts:b};if(t.lan)if(tn(t.lan)==="off")m.https=!1;else{let y=Ra(e,t.lan,s);Oa(y,m.onDemandTls);let f=t.lan.ip&&vs(t.lan.ip)!==0?[t.lan.ip]:[];m.localCa={dir:ys,hosts:y.map((T)=>T.host),...f.length>0?{ips:f}:{},installTrust:!0}}return m}function X(e,t){return ws(e,t)}function Ir(e,t,r){return ws(e,r,t)}function ih(e){return`// Generated by ts-cloud — rpx reverse-proxy gateway.
26
26
  // Routes are derived from the \`sites\` model on every \`buddy deploy\`.
27
27
  import { startProxies } from '@stacksjs/rpx'
28
28
 
@@ -273,7 +273,7 @@ echo "ts-cloud bootstrap complete — instance is ready for site deploys"
273
273
  `,y}var Ls="/aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id";function Ns(e){let t=[{port:22,protocol:"tcp",cidr:"0.0.0.0/0"},{port:80,protocol:"tcp",cidr:"0.0.0.0/0"},{port:443,protocol:"tcp",cidr:"0.0.0.0/0"}],r=new Set;for(let n of Object.values(e.sites||{}))if(n&&typeof n.port==="number"&&![22,80,443].includes(n.port))r.add(n.port);for(let n of r)t.push({port:n,protocol:"tcp",cidr:"0.0.0.0/0"});return t}function ks(e){let t=e.infrastructure?.compute??{},r=Me(e),n=t.proxy?.engine==="rpx"?Te({proxy:t.proxy,config:X(e.sites??{},{proxy:t.proxy,slug:e.project.slug}),slug:e.project.slug,bunBin:t.runtime==="node"||t.runtime==="deno"?void 0:"/usr/local/bin/bun"}):void 0;return x({runtime:r.runtime,runtimeVersion:r.runtimeVersion,systemPackages:t.systemPackages,database:e.infrastructure?.database,phpProvision:r.phpProvision,servicesProvision:r.servicesProvision,rpxProvision:n,baked:t.bakedImage===!0,swapGb:t.swapGb})}function Bs(e){return Buffer.from(e,"utf8").toString("base64")}function zs(e){return e.infrastructure?.compute?.image??null}function Yt(e,t){return`${e}-${t}-app-sg`}function sn(e,t,r="app"){return[{Key:"Project",Value:e},{Key:"Environment",Value:t},{Key:"Role",Value:r},{Key:"ManagedBy",Value:"ts-cloud"}]}function za(e,t,r){if(!e?.length)return!1;let n=(s)=>e.find((i)=>i.Key===s)?.Value;return n("Project")===t&&n("Environment")===r}function js(e,t){let r=Yt(t.slug,t.environment);return e.find((n)=>{if(n.GroupName!==r)return!1;if(t.vpcId&&n.VpcId&&n.VpcId!==t.vpcId)return!1;if(!!n.Tags?.some((i)=>i.Key==="Project"))return za(n.Tags,t.slug,t.environment);return!!t.vpcId&&n.VpcId===t.vpcId})}function Ga(e){try{let t=Ks(ja(process.cwd(),"storage/cloud/state",`${e}.json`),"utf8"),r=JSON.parse(t);return typeof r.instanceId==="string"&&r.instanceId.length>0?r.instanceId:null}catch{return null}}class br{name="aws";usesCloudFormation=!0;region;ec2Client;cfnClient;constructor(e={}){this.region=e.region||"us-east-1",this.ec2Client=e.ec2Client??((t)=>new Pr(t)),this.cfnClient=e.cfnClient??((t)=>new h(t))}resolveRegion(e){return e.project.region||this.region}async provisionComputeInfrastructure(e){let{config:t,environment:r}=e,n=this.resolveRegion(t),s=t.project.slug,i=t.infrastructure?.compute;if(!i)throw Error("infrastructure.compute is required to provision AWS compute");let a=this.ec2Client(n),c=await this.findComputeTargets({slug:s,environment:r,role:"app",region:n});if(c.length>0){let A=c[0];return{appInstanceId:A.id,appPublicIp:A.publicIp,appPublicIpv6:A.publicIpv6,sshUser:"ubuntu",deployStoragePath:"/var/ts-cloud/staging"}}let l=zs(t);if(!l){if(l=(await new De(n).getParameter({Name:Ls})).Parameter?.Value??null,!l)throw Error("Could not resolve the Ubuntu 24.04 AMI from SSM")}let m=await a.describeVpcs(),S=(m.Vpcs||[]).find((A)=>A.IsDefault)||(m.Vpcs||[])[0];if(!S?.VpcId)throw Error("No VPC found to launch the instance into");let b=(await a.describeSubnets({Filters:[{Name:"vpc-id",Values:[S.VpcId]}]})).Subnets||[],y=b.find((A)=>A.MapPublicIpOnLaunch)||b[0],f=y?.SubnetId;if(!y?.MapPublicIpOnLaunch)console.warn("ts-cloud: no public subnet found; the instance may not get a public IP (deploys/SSL need one).");let T=Yt(s,r),R=(await a.describeSecurityGroups({Filters:[{Name:"group-name",Values:[T]},{Name:"vpc-id",Values:[S.VpcId]}]})).SecurityGroups?.[0]?.GroupId;if(!R)R=(await a.createSecurityGroup({GroupName:T,Description:`ts-cloud ${s}/${r} app`,VpcId:S.VpcId,TagSpecifications:[{ResourceType:"security-group",Tags:sn(s,r)}]})).GroupId;let C=Ns(t);await a.authorizeSecurityGroupIngress({GroupId:R,IpPermissions:C.map((A)=>({IpProtocol:A.protocol,FromPort:A.port,ToPort:A.port,IpRanges:[{CidrIp:A.cidr}]}))}).catch((A)=>{if(!/InvalidPermission\.Duplicate/.test(A instanceof Error?A.message:""))throw A});let P=Bs(ks(t)),w=i.server?.instanceType||"t3.small",H=(await a.runInstances({ImageId:l,InstanceType:w,MinCount:1,MaxCount:1,SecurityGroupIds:R?[R]:void 0,SubnetId:f,UserData:P,IamInstanceProfile:i.server?.iamInstanceProfile?{Name:i.server.iamInstanceProfile}:void 0,TagSpecifications:[{ResourceType:"instance",Tags:[{Key:"Name",Value:`${s}-${r}-app`},...sn(s,r)]}]})).Instances?.[0]?.InstanceId;if(!H)throw Error("RunInstances did not return an instance id");let I=await a.waitForInstanceState(H,"running");if(!I)throw Error(`Instance ${H} did not reach 'running' before timeout`);return{appInstanceId:H,appPublicIp:I.PublicIpAddress,appPublicIpv6:Ee(I.Ipv6Address),sshUser:"ubuntu",deployStoragePath:"/var/ts-cloud/staging"}}async destroyCompute(e){let{config:t,environment:r}=e,n=t.project.slug,s=this.resolveRegion(t),i=this.ec2Client(s),a=[],l=(await this.findComputeTargets({slug:n,environment:r,role:"app",region:s})).map((T)=>T.id),m;if(l.length>0)m=(await i.describeInstances({InstanceIds:l}).catch(()=>({Reservations:[]}))).Reservations?.flatMap((D)=>D.Instances??[]).find((D)=>D.VpcId)?.VpcId;if(l.length>0)await i.terminateInstances(l),a.push(...l.map((T)=>`instance ${T}`)),await Promise.all(l.map((T)=>i.waitForInstanceState(T,"terminated").catch(()=>{return})));let S=Yt(n,r),b=[{Name:"group-name",Values:[S]}];if(m)b.push({Name:"vpc-id",Values:[m]});let y=await i.describeSecurityGroups({Filters:b}).catch(()=>({SecurityGroups:[]})),f=js(y.SecurityGroups??[],{slug:n,environment:r,vpcId:m});if(f?.GroupId)for(let T=0;T<10;T++)try{await i.deleteSecurityGroup(f.GroupId),a.push(`security group ${S}`);break}catch{await new Promise((D)=>setTimeout(D,3000))}return{destroyed:a}}async getComputeOutputs(e){let t=this.resolveRegion(e.config),r=p(e.config,e.environment),n=this.cfnClient(t);try{let s=await n.getStackOutputs(r);return{deployBucketName:s.deployBucketName,appInstanceId:s.appInstanceId,appPublicIp:s.appPublicIp,sshUser:"ec2-user"}}catch(s){if(!/does not exist|ValidationError/i.test(s instanceof Error?s.message:""))throw s;let a=(await this.findComputeTargets({slug:e.config.project.slug,environment:e.environment,role:"app",region:t}))[0];return{appInstanceId:a?.id,appPublicIp:a?.publicIp,appPublicIpv6:a?.publicIpv6,sshUser:"ubuntu",deployStoragePath:"/var/ts-cloud/staging"}}}async uploadRelease(e){let t=this.resolveRegion(e.config),n=(await this.getComputeOutputs({config:e.config,environment:e.environment})).deployBucketName;if(!n)throw Error("No deployBucketName in stack outputs. Re-deploy infrastructure to add the staging bucket.");return await new o(t).putObject({bucket:n,key:e.remoteKey,body:Ks(e.localPath),contentType:"application/gzip"}),{artifactRef:`s3://${n}/${e.remoteKey}`}}async findComputeTargets(e){let t=e.region??this.region,r=this.ec2Client(t),n=[{Name:"tag:Project",Values:[e.slug]},{Name:"tag:Environment",Values:[e.environment]},{Name:"tag:Role",Values:[e.role||"app"]},{Name:"instance-state-name",Values:["running","pending"]}],s=await r.describeInstances({Filters:n}),i=this.reservationsToTargets(s.Reservations);if(i.length>0||(e.role||"app")!=="app")return i;let a=e.stackName??`${e.slug}-${e.environment}`,c=Ga(a);if(!c)try{c=(await this.cfnClient(t).getStackOutputs(a)).appInstanceId??null}catch{c=null}if(!c)return[];try{let l=await r.describeInstances({InstanceIds:[c],Filters:[{Name:"instance-state-name",Values:["running","pending"]}]});return this.reservationsToTargets(l.Reservations)}catch{return[]}}reservationsToTargets(e){let t=[];for(let r of e||[])for(let n of r.Instances||[]){if(!n.InstanceId)continue;let s=n.Tags?.find((i)=>i.Key==="Name")?.Value;t.push({id:n.InstanceId,name:s,publicIp:n.PublicIpAddress,publicIpv6:Ee(n.Ipv6Address),privateIp:n.PrivateIpAddress,status:n.State?.Name})}return t}bashWrap(e){return["bash <<'TS_CLOUD_BASH_EOF'",e.join(`
274
274
  `),"TS_CLOUD_BASH_EOF"]}async runRemoteDeploy(e){let t=this.region,r=new De(t);if(e.targets.length>0){let s=await r.sendCommand({InstanceIds:e.targets.map((i)=>i.id),DocumentName:"AWS-RunShellScript",Parameters:{commands:this.bashWrap(e.commands)},TimeoutSeconds:e.timeoutSeconds||600,Comment:e.comment});if(!s.CommandId)return{success:!1,instanceCount:0,perInstance:[],error:"Failed to send SSM command"};return this.pollSsmCommand(r,s.CommandId,e.targets.length,((e.timeoutSeconds||600)+30)*1000)}if(!e.tags||Object.keys(e.tags).length===0)return{success:!1,instanceCount:0,perInstance:[],error:"No targets or tags provided for AWS deploy"};let n=await r.sendCommandByTags({tags:e.tags,commands:this.bashWrap(e.commands),timeoutSeconds:e.timeoutSeconds||600,comment:e.comment});return{success:n.success,instanceCount:n.instanceCount,perInstance:n.perInstance.map((s)=>({instanceId:s.instanceId,status:s.status,output:s.output,error:s.error})),error:n.error}}async pollSsmCommand(e,t,r,n=600000){let i=Date.now(),a=new Set(["Success","Failed","Cancelled","TimedOut"]),c=[];while(Date.now()-i<n){await new Promise((S)=>setTimeout(S,3000));try{if(c=await e.listCommandInvocations({CommandId:t,Details:!0}),c.length>=r&&c.every((b)=>a.has(b.Status||"")))break}catch{}}let l=c.map((S)=>({instanceId:S.InstanceId,status:S.Status||"Unknown",output:S.StandardOutputContent,error:S.StandardErrorContent})),m=l.length>0&&l.every((S)=>S.status==="Success");return{success:m,instanceCount:l.length,perInstance:l,error:m?void 0:$t(l,"One or more SSM command invocations failed")}}}function Vn(e={},t="ssh"){let r=[];if(e.identityFile)r.push("-i",e.identityFile);if(e.port)r.push(t==="scp"?"-P":"-p",String(e.port));return r.push("-o","StrictHostKeyChecking=no","-o","UserKnownHostsFile=/dev/null","-o","LogLevel=ERROR","-o",`ConnectTimeout=${e.connectTimeoutSec??10}`),r}async function Cr(e,t,r={}){let n=Bun.spawn(["ssh",...Vn(r),`${r.user??"root"}@${e}`,t],{stdout:"pipe",stderr:"pipe"}),[s,i]=await Promise.all([new Response(n.stdout).text(),new Response(n.stderr).text()]);return{code:await n.exited,stdout:s,stderr:i}}async function ge(e,t,r={}){let n=await Cr(e,t,r);if(n.code!==0)throw Error(`ssh \`${t}\` on ${e} failed (${n.code}): ${n.stderr.trim()||n.stdout.trim()}`);return n.stdout}async function hc(e,t,r,n={}){let s=Bun.spawn(["scp",...Vn(n,"scp"),...t,`${n.user??"root"}@${e}:${r}`],{stdout:"pipe",stderr:"pipe"}),i=await s.exited;if(i!==0){let a=await new Response(s.stderr).text();throw Error(`scp to ${e}:${r} failed (${i}): ${a.trim()}`)}}async function qn(e,t={}){let r=t.timeoutMs??180000,n=t.pollIntervalMs??5000,s=Date.now();while(Date.now()-s<r){let i=await Cr(e,"echo ready",t);if(i.code===0&&i.stdout.includes("ready"))return;await new Promise((a)=>setTimeout(a,n))}throw Error(`SSH not ready on ${e} after ${r}ms`)}async function Ac(e,t={}){let r=t.timeoutMs??300000,n=t.pollIntervalMs??5000,s=Date.now();while(Date.now()-s<r){let i=await Cr(e,"cloud-init status 2>/dev/null || echo unknown",t);if(i.stdout.includes("status: done"))return;if(i.stdout.includes("status: error"))throw Error(`cloud-init reported an error on ${e}; check /var/log/cloud-init-output.log`);await new Promise((a)=>setTimeout(a,n))}throw Error(`cloud-init did not finish on ${e} after ${r}ms`)}function jn(e={}){return["set -e","ts_cloud_script=$(mktemp /tmp/ts-cloud-deploy.XXXXXXXX)",`trap 'rm -f "$ts_cloud_script"' EXIT`,'cat > "$ts_cloud_script"',e.sudo?'sudo -n -H bash "$ts_cloud_script" < /dev/null':'bash "$ts_cloud_script" < /dev/null'].join(`
275
275
  `)}var yc=jn();import{execFileSync as qc,execSync as ho}from"node:child_process";import{createHash as Yc,randomUUID as So}from"node:crypto";import{createReadStream as Xc,existsSync as Jc,readFileSync as Qc}from"node:fs";function Jt(e){return Object.entries(e).map(([t,r])=>`${t}=${Ka(String(r))}`).join(`
276
- `)}function Ka(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t")}"`}var Qt=["storage",".env"],Pt=4;function be(e){return`${e.replace(/\/+$/,"")}/.ts-cloud`}function Va(e){return`${be(e)}/deploy-history.log`}function Wa(e,t){return`${be(e)}/deploys/${t}.log`}function Ws(e){return`${be(e)}/shared-paths`}function Zt(e){return`${e.shared}/stx-images`}function Ft(e,t){let r=e.replace(/\/+$/,"");return{base:r,releases:`${r}/releases`,shared:`${r}/shared`,current:`${r}/current`,release:`${r}/releases/${t}`}}function Ys(e){let t=e.split("/").pop()||e;if(t===".env"||t.startsWith(".env."))return!0;return/\.[a-z0-9]+$/i.test(t)}function on(e){return typeof e==="string"?e:e.path}function Xs(e){let t=new Map;for(let r of e)t.set(on(r),r);return[...t.values()]}function Js(e,t){if(typeof t==="string")return{path:t,target:`${e.shared}/${t}`,seed:!0};return{path:t.path,target:t.target??`${e.shared}/${t.path}`,seed:t.seed!==!1}}function qa(e){return["ts_cloud_adopt_shared() {",' TS_CLOUD_SP="$1"',' TS_CLOUD_DST="$2"',` TS_CLOUD_SRC="${e.current}/$TS_CLOUD_SP"`,' if [ -e "$TS_CLOUD_DST" ]; then return 0; fi',' if [ -L "$TS_CLOUD_SRC" ] || [ ! -e "$TS_CLOUD_SRC" ]; then return 0; fi',' mkdir -p "$(dirname "$TS_CLOUD_DST")"',' cp -a "$TS_CLOUD_SRC" "$TS_CLOUD_DST"'," for TS_CLOUD_SIDECAR in -wal -shm; do",' if [ -f "$TS_CLOUD_SRC$TS_CLOUD_SIDECAR" ]; then cp -a "$TS_CLOUD_SRC$TS_CLOUD_SIDECAR" "$TS_CLOUD_DST$TS_CLOUD_SIDECAR"; fi'," done",' echo "[ts-cloud] adopted $TS_CLOUD_SP from the live release into shared/ — it now survives deploys"'," return 0","}"]}function Et(e,t=Qt){let r=[`mkdir -p ${e.releases} ${e.shared} ${Zt(e)}`,...qa(e)],n=t.map((i)=>Js(e,i));for(let{path:i,target:a,seed:c}of n){if(!c)continue;if(r.push(`ts_cloud_adopt_shared '${i}' '${a}'`),Ys(i))r.push(`mkdir -p "$(dirname ${a})"`,`touch ${a}`);else if(i==="storage")r.push(`mkdir -p ${a}/app/public`,`mkdir -p ${a}/framework/cache/data`,`mkdir -p ${a}/framework/sessions`,`mkdir -p ${a}/framework/testing`,`mkdir -p ${a}/framework/views`,`mkdir -p ${a}/logs`);else r.push(`mkdir -p ${a}`)}let s=Ws(e.base);return r.push(`mkdir -p ${be(e.base)}`,`cat > ${s} <<'TS_CLOUD_SHARED_PATHS_EOF'`,...n.map((i)=>`${i.path} ${i.target}`),"TS_CLOUD_SHARED_PATHS_EOF"),r}function an(e,t=900){let r=`${be(e.base)}/deploy.lock`;return[`mkdir -p "$(dirname ${r})"`,`exec 9>${r}`,`flock -w ${t} 9 || { echo "[ts-cloud] another deploy of this site has held the lock for ${t}s — refusing to race it" >&2; exit 1; }`]}function cn(e){let t=`${e.release}.incoming`;return[`rm -rf ${t}`,`mkdir -p ${t}`,...Ya(e),'if [ "$TS_CLOUD_IS_LIVE" = "yes" ]; then',` TS_CLOUD_STAGED=${t}`,"else",` rm -rf ${e.release}`,` mkdir -p ${e.release}`,` rmdir ${t}`,` TS_CLOUD_STAGED=${e.release}`,"fi"]}function ln(e){let t=`TS_CLOUD_TRAP_LIVE="$(readlink -f ${e.current} 2>/dev/null || true)"`,r=`TS_CLOUD_TRAP_TARGET="$(readlink -f ${e.release} 2>/dev/null || echo ${e.release})"`;return`trap '${`if [ $? -ne 0 ]; then ${t}; ${r}; [ "$TS_CLOUD_TRAP_LIVE" = "$TS_CLOUD_TRAP_TARGET" ] || rm -rf ${e.release}; fi`}' EXIT`}function Ya(e){return[`TS_CLOUD_LIVE_PATH="$(readlink -f ${e.current} 2>/dev/null || true)"`,`TS_CLOUD_RELEASE_PATH="$(readlink -f ${e.release} 2>/dev/null || echo ${e.release})"`,'if [ -n "$TS_CLOUD_LIVE_PATH" ] && [ "$TS_CLOUD_LIVE_PATH" = "$TS_CLOUD_RELEASE_PATH" ]; then TS_CLOUD_IS_LIVE=yes; else TS_CLOUD_IS_LIVE=no; fi']}function un(e){let t=`${e.release}.incoming`;return[`if [ "$TS_CLOUD_STAGED" = "${t}" ]; then`,` rm -rf ${e.release}.previous`,` mv ${e.release} ${e.release}.previous`,` mv ${t} ${e.release}`,` rm -rf ${e.release}.previous`,"fi"]}function Xa(e,t){return[`if [ -f ${e} ] && [ ! -L ${e} ] && [ -s ${e} ] && [ ! -s ${t} ]; then`,` cp -a ${e} ${t}`,` echo "[ts-cloud] seeded shared/ from the release's own copy of the file"`,"fi"]}function er(e,t=Qt){let r=[];for(let n of t){let{path:s,target:i,seed:a}=Js(e,n),c=`${e.release}/${s}`,l=[`rm -rf ${c}`,`mkdir -p "$(dirname ${c})"`,`ln -sfn ${i} ${c}`];if(a){if(s!==".env"&&Ys(s))r.push(...Xa(c,i));r.push(...l);continue}r.push(`if [ -e ${i} ]; then`,...l.map((m)=>` ${m}`),"fi")}return r}function Ja(e,t){let r=Ws(e.base);return[`if [ -f ${r} ]; then`,' while IFS="\t" read -r TS_CLOUD_SP TS_CLOUD_TGT; do',' [ -n "$TS_CLOUD_SP" ] || continue',` [ -n "$TS_CLOUD_TGT" ] || TS_CLOUD_TGT="${e.shared}/$TS_CLOUD_SP"`,' [ -e "$TS_CLOUD_TGT" ] || continue',` rm -rf ${t}/"$TS_CLOUD_SP"`,` mkdir -p "$(dirname ${t}/"$TS_CLOUD_SP")"`,` ln -sfn "$TS_CLOUD_TGT" ${t}/"$TS_CLOUD_SP"`,` done < ${r}`,"fi"]}function st(e){return[`ln -sfn ${e.release} ${e.current}.tmp`,`mv -Tf ${e.current}.tmp ${e.current}`]}function pn(e){return`${be(e)}/previous-release`}function dn(e,t="TS_CLOUD_PREV_CURRENT"){let r=pn(e.base);return[`if [ -n "\${${t}:-}" ] && [ -d "$${t}" ] && [ "$(readlink -f "$${t}")" != "$(readlink -f ${e.release})" ]; then mkdir -p ${be(e.base)}; basename "$${t}" > ${r}; fi`]}function Zs(e,t){let r=pn(e.base);return[`TS_CLOUD_CURRENT=$(readlink -f ${e.current} 2>/dev/null || true)`,...t?[`TS_CLOUD_RB_TARGET=${e.releases}/${t}`]:["TS_CLOUD_RB_TARGET=",`if [ -f ${r} ]; then TS_CLOUD_REC="${e.releases}/$(cat ${r})"; if [ -d "$TS_CLOUD_REC" ] && [ "$(readlink -f "$TS_CLOUD_REC")" != "$TS_CLOUD_CURRENT" ]; then TS_CLOUD_RB_TARGET="$TS_CLOUD_REC"; fi; fi`,`[ -n "$TS_CLOUD_RB_TARGET" ] || TS_CLOUD_RB_TARGET=$(ls -1dt ${e.releases}/*/ 2>/dev/null | sed 's#/$##' | while read -r r; do [ "$(readlink -f "$r")" != "$TS_CLOUD_CURRENT" ] && { echo "$r"; break; }; done)`],'[ -n "$TS_CLOUD_RB_TARGET" ] && [ -d "$TS_CLOUD_RB_TARGET" ] || { echo "no release to roll back to (looked for ${TS_CLOUD_RB_TARGET:-a previous release})" >&2; exit 1; }','[ "$(readlink -f "$TS_CLOUD_RB_TARGET")" != "$TS_CLOUD_CURRENT" ] || { echo "$(basename "$TS_CLOUD_RB_TARGET") is already the live release" >&2; exit 1; }','TS_CLOUD_RB_ID=$(basename "$TS_CLOUD_RB_TARGET")']}function ug(e,t={}){return[...Zs(e,t.to),'echo "would roll back from $(basename "${TS_CLOUD_CURRENT:-none}") to $TS_CLOUD_RB_ID (dry run: nothing changed)"']}function gg(e,t={}){let r=Math.max(1,t.gateSeconds??5),n=t.systemdDir??"/etc/systemd/system",s=[`ln -sfn "$TS_CLOUD_RB_TARGET" ${e.current}.tmp`,`mv -Tf ${e.current}.tmp ${e.current}`,`rm -f ${pn(e.base)}`,'echo "rolled back to $TS_CLOUD_RB_ID"'],i=[...Zs(e,t.to),...Ja(e,'"$TS_CLOUD_RB_TARGET"')];if(!t.unitBase)return[...i,...s];let a=t.unitBase,c=`${a}@\${TS_CLOUD_RB_ID}.service`;return[...i,`if [ -f ${n}/${a}@.service ]; then`,` systemctl start "${c}"`,` for TS_CLOUD_I in $(seq 1 ${r}); do sleep 1; systemctl is-active --quiet "${c}" || { echo "the release rolled back to, $TS_CLOUD_RB_ID, did not stay up; the current release keeps serving" >&2; journalctl -u "${c}" -n 30 --no-pager >&2 || true; systemctl stop "${c}" 2>/dev/null || true; exit 1; }; done`,...s.map((l)=>` ${l}`),` systemctl enable "${c}" 2>/dev/null || true`,` systemctl list-units --plain --no-legend --type=service "${a}@*.service" 2>/dev/null | awk '{print $1}' | { grep -v "^${a}@\${TS_CLOUD_RB_ID}.service$" || true; } | while read -r TS_CLOUD_U; do systemctl stop "$TS_CLOUD_U" 2>/dev/null || true; systemctl disable "$TS_CLOUD_U" 2>/dev/null || true; done`,`elif [ -f ${n}/${a}.service ]; then`,...s.map((l)=>` ${l}`),` systemctl restart ${a}.service`,"else",...s.map((l)=>` ${l}`),"fi"]}function ot(e,t=4){let r=Math.max(1,t);return[`TS_CLOUD_CURRENT=$(readlink -f ${e.current} 2>/dev/null || true)`,`ls -1dt ${e.releases}/*/ 2>/dev/null | sed 's#/$##' | tail -n +${r+1} | while read -r TS_CLOUD_OLD; do`,' [ "$(readlink -f "$TS_CLOUD_OLD")" = "$TS_CLOUD_CURRENT" ] || rm -rf "$TS_CLOUD_OLD"',"done"]}function Qa(e){return`${be(e)}/owner`}function mn(e,t){let r=Qa(e);return[`if [ -f "${r}" ]; then`,` TS_CLOUD_OWNER=$(cat "${r}")`,` if [ "$TS_CLOUD_OWNER" != "${t}" ]; then`,` echo "[ts-cloud] REFUSING deploy: ${e} belongs to project '$TS_CLOUD_OWNER', not '${t}'. Another project on this box derives the same site key — give one of them a distinct site name/domain, or remove ${r} to transfer ownership." >&2`," exit 1"," fi","else",` mkdir -p "$(dirname "${r}")"`,` printf '%s\\n' "${t}" > "${r}"`,"fi"]}function tr(e,t){let r=be(e),n=Wa(e,t.releaseId),s=Va(e),i=Math.max(1,t.keepLogs??20),a=t.commit||"",c=t.branch||"";return[`mkdir -p ${r}/deploys`,`exec > >(tee -a ${n}) 2>&1`,`echo "[ts-cloud] deploy ${t.releaseId} commit=${a} branch=${c} starting $(date -u +%Y-%m-%dT%H:%M:%SZ)"`,"ts_cloud_record_deploy() {"," TS_CLOUD_RC=$?",' if [ "$TS_CLOUD_RC" -eq 0 ]; then TS_CLOUD_ST=success; else TS_CLOUD_ST=failed; fi',` printf '%s\\t%s\\t%s\\t%s\\trc=%s\\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${t.releaseId}" "${a}" "$TS_CLOUD_ST" "$TS_CLOUD_RC" >> ${s}`,"}","trap ts_cloud_record_deploy EXIT",`ls -1t ${r}/deploys/*.log 2>/dev/null | tail -n +${i+1} | while read -r TS_CLOUD_OLDLOG; do rm -f "$TS_CLOUD_OLDLOG"; done`]}var Za=new Set(["sqlite","sqlite3"]),ec=new Set([":memory:","memory"]);function tc(e){let t=e?.DB_CONNECTION?.trim().toLowerCase();return t!=null&&Za.has(t)}function rc(e){let t=e.trim().replace(/^\.\//,"");if(t===""||t.startsWith("/")||t.startsWith("~"))return;if(t.split("/").includes(".."))return;return t.replace(/\/+$/,"")}function fn(e,t=[]){if(!tc(e))return{};let r=e?.DB_DATABASE_PATH?.trim()||e?.DB_DATABASE?.trim();if(r&&ec.has(r.toLowerCase()))return{};if(!r)return{warning:"DB_CONNECTION is sqlite but neither DB_DATABASE_PATH nor DB_DATABASE names a file, so ts-cloud cannot tell where the database lives. If it is written inside the release directory, the next deploy discards it. Set DB_DATABASE_PATH, or list the file in the site's `sharedPaths`."};let n=rc(r);if(!n)return{};if(t.some((s)=>on(s)===n))return{};return{path:n}}function rr(e,t=[]){let{path:r}=fn(e,t);return r?[r]:[]}var Er="/var/ts-cloud/artifacts",ih="/usr/local/bin/ts-cloud-host-cleanup.sh",sh="ts-cloud-host-cleanup";var pg=Object.freeze({stagingMaxAgeMinutes:60,artifactMaxAgeDays:2,artifactUploadMaxAgeMinutes:60,bunCacheMaxAgeDays:7,journalMaxAgeDays:14,journalMaxSizeMb:512,containerImageMaxAgeHours:168}),mg=Object.freeze({artifactMaxAgeDays:0,bunCacheMaxAgeDays:1,journalMaxAgeDays:3,journalMaxSizeMb:256,containerImageMaxAgeHours:24}),oh=50,ah=85,ch="daily";function eo(e){return`'${e.replace(/'/g,`'"'"'`)}'`}function Be(e,t,r=0){if(!Number.isInteger(t)||t<r)throw Error(`hostCleanup: ${e} must be a whole number >= ${r} (got ${t})`)}function to(e,t){for(let[r,n]of Object.entries(t))Be(`${e}.${r}`,n);Be(`${e}.stagingMaxAgeMinutes`,t.stagingMaxAgeMinutes,15),Be(`${e}.artifactUploadMaxAgeMinutes`,t.artifactUploadMaxAgeMinutes,15),Be(`${e}.journalMaxSizeMb`,t.journalMaxSizeMb,1)}function nc(e,t){let r=`hostCleanup.paths[${t}]`,n=e.path?.replace(/\/+$/,"")??"";if(!n.startsWith("/")||n===""||/[\s'"`$\\]/.test(n)||n.split("/").includes(".."))throw Error(`${r}.path must be an absolute directory with no spaces, quotes or '..' (got ${JSON.stringify(e.path)})`);if(n==="/var/www"||n.startsWith("/var/www/"))throw Error(`${r}.path must not be under /var/www: releases are pruned by keepReleases`);if(!e.pattern||/^[*?]+$/.test(e.pattern)||e.pattern.includes("/")||/[\n'"`]/.test(e.pattern))throw Error(`${r}.pattern must be a file-name glob naming something, not a bare wildcard (got ${JSON.stringify(e.pattern)})`);if(Be(`${r}.maxAgeDays`,e.maxAgeDays),e.type!==void 0&&e.type!=="file"&&e.type!=="directory")throw Error(`${r}.type must be 'file' or 'directory'`)}function vr(e){let t=typeof e==="object"&&e!==null?e:{},r=e!==!1,n={...pg,...ro(t.retention)};to("hostCleanup.retention",n);let s=null;if(t.pressure!==!1){let c=typeof t.pressure==="object"?t.pressure:{},l=c.lowWaterPercent??50,m=c.highWaterPercent??85;if(Be("hostCleanup.pressure.lowWaterPercent",l),Be("hostCleanup.pressure.highWaterPercent",m,1),m>100||l>=m)throw Error(`hostCleanup.pressure: need 0 <= lowWaterPercent < highWaterPercent <= 100 (got ${l} and ${m})`);let S={...mg,...ro(c.escalated)},b=Object.fromEntries(Object.keys(n).map((y)=>[y,Math.min(n[y],S[y]??n[y])]));to("hostCleanup.pressure.escalated",b),s={lowWaterPercent:l,highWaterPercent:m,escalated:b}}let i=t.schedule??"daily";if(!/^[\w*:,./~ -]+$/.test(i))throw Error(`hostCleanup.schedule must be a systemd OnCalendar expression (got ${JSON.stringify(i)})`);let a=t.paths??[];return a.forEach(nc),{enabled:r,timer:r&&t.timer!==!1,schedule:i,retention:n,pressure:s,paths:a}}function ro(e){return Object.fromEntries(Object.entries(e??{}).filter(([,t])=>t!==void 0))}var Rt={stagingMaxAgeMinutes:"TS_CLOUD_HC_STAGING_MIN",artifactMaxAgeDays:"TS_CLOUD_HC_ARTIFACT_DAYS",artifactUploadMaxAgeMinutes:"TS_CLOUD_HC_UPLOAD_MIN",bunCacheMaxAgeDays:"TS_CLOUD_HC_BUN_DAYS",journalMaxAgeDays:"TS_CLOUD_HC_JOURNAL_DAYS",journalMaxSizeMb:"TS_CLOUD_HC_JOURNAL_MB",containerImageMaxAgeHours:"TS_CLOUD_HC_IMAGE_HOURS"};function gn(e){return Object.keys(Rt).map((t)=>`${Rt[t]}=${e[t]}`).join(" ")}var Fe=(e)=>`"$${Rt[e]}"`;function no(e){return[`find /var/ts-cloud/staging -xdev -maxdepth 1 -type f -mmin +${Fe("stagingMaxAgeMinutes")} -delete 2>/dev/null || true`,`find /tmp -xdev -maxdepth 1 -type f -name "*-release.tar.gz" -mmin +${Fe("stagingMaxAgeMinutes")} -delete 2>/dev/null || true`,`find /var/ts-cloud/artifacts -xdev -maxdepth 1 -type f -name "*.tar.gz" -mtime +${Fe("artifactMaxAgeDays")} -delete 2>/dev/null || true`,`find /var/ts-cloud/artifacts -xdev -maxdepth 1 -type f -name ".*.tmp" -mmin +${Fe("artifactUploadMaxAgeMinutes")} -delete 2>/dev/null || true`,...e.map(sc),`journalctl --vacuum-time=${Fe("journalMaxAgeDays")}d --vacuum-size=${Fe("journalMaxSizeMb")}M >/dev/null 2>&1 || true`]}function sc(e){let t=eo(e.path.replace(/\/+$/,"")),r=eo(e.pattern);return e.type==="directory"?`find ${t} -xdev -mindepth 1 -maxdepth 1 -type d -name ${r} -mtime +${e.maxAgeDays} -exec rm -rf -- {} + 2>/dev/null || true`:`find ${t} -xdev -mindepth 1 -maxdepth 1 -type f -name ${r} -mtime +${e.maxAgeDays} -delete 2>/dev/null || true`}function so(){return[`find /root/.bun/install/cache -xdev -type f -mtime +${Fe("bunCacheMaxAgeDays")} -delete 2>/dev/null || true`,"find /root/.bun/install/cache -xdev -depth -type d -empty -delete 2>/dev/null || true",`if command -v docker >/dev/null 2>&1; then docker image prune --all --force --filter "until=\${${Rt.containerImageMaxAgeHours}}h" >/dev/null 2>&1 || true; fi`,`if command -v podman >/dev/null 2>&1; then podman image prune --all --force --filter "until=\${${Rt.containerImageMaxAgeHours}}h" >/dev/null 2>&1 || true; fi`,"if command -v apt-get >/dev/null 2>&1; then apt-get clean >/dev/null 2>&1 || true; fi"]}function oo(e){return[`${e}="$(df -P / 2>/dev/null | awk 'NR==2 {sub(/%/, "", $5); print $5}' || true)"`,`case "$${e}" in ''|*[!0-9]*) ${e}=-1 ;; esac`]}function zn(e){let{retention:t,pressure:r,paths:n}=vr(e),s='echo "[ts-cloud] host cleanup (disk before): $(df -h / | tail -1)"',i='echo "[ts-cloud] host cleanup (disk after): $(df -h / | tail -1)"';if(!r)return['echo "[ts-cloud] host cleanup (disk before): $(df -h / | tail -1)"',gn(t),...no(n),...so(),'echo "[ts-cloud] host cleanup (disk after): $(df -h / | tail -1)"'];let{lowWaterPercent:a,highWaterPercent:c,escalated:l}=r;return['echo "[ts-cloud] host cleanup (disk before): $(df -h / | tail -1)"',...oo("TS_CLOUD_HC_BEFORE"),"TS_CLOUD_HC_TIER=normal",gn(t),`if [ "$TS_CLOUD_HC_BEFORE" -ge 0 ] && [ "$TS_CLOUD_HC_BEFORE" -lt ${a} ]; then`," TS_CLOUD_HC_TIER=light",` echo "[ts-cloud] host cleanup: / at \${TS_CLOUD_HC_BEFORE}% (under ${a}%), skipping cache and image pruning"`,`elif [ "$TS_CLOUD_HC_BEFORE" -ge ${c} ]; then`," TS_CLOUD_HC_TIER=pressure",` ${gn(l)}`,` echo "[ts-cloud] warning: / at \${TS_CLOUD_HC_BEFORE}% (at or above ${c}%), host cleanup is using shortened retention windows" >&2`,"fi",...no(n),'if [ "$TS_CLOUD_HC_TIER" != light ]; then',...so().map((m)=>` ${m}`),"fi",'echo "[ts-cloud] host cleanup (disk after): $(df -h / | tail -1)"',...oo("TS_CLOUD_HC_AFTER"),`echo "[ts-cloud] host-cleanup {\\"event\\":\\"host-cleanup\\",\\"tier\\":\\"$TS_CLOUD_HC_TIER\\",\\"diskBeforePercent\\":$TS_CLOUD_HC_BEFORE,\\"diskAfterPercent\\":$TS_CLOUD_HC_AFTER,\\"lowWaterPercent\\":${a},\\"highWaterPercent\\":${c}}"`,"mkdir -p /var/lib/ts-cloud 2>/dev/null || true",'TS_CLOUD_HC_PREV="$(cat /var/lib/ts-cloud/host-cleanup.alert 2>/dev/null || echo ok)"',`if [ "$TS_CLOUD_HC_AFTER" -ge ${c} ]; then`,` echo "[ts-cloud] warning: / still at \${TS_CLOUD_HC_AFTER}% after host cleanup (high-water ${c}%); something outside ts-cloud's retention rules is filling the disk" >&2`,` if [ "$TS_CLOUD_HC_PREV" != alert ] && [ -x /usr/local/bin/ts-cloud-notify ]; then /usr/local/bin/ts-cloud-notify "⚠️ $(hostname): / at \${TS_CLOUD_HC_AFTER}% after host cleanup (high-water ${c}%)" >/dev/null 2>&1 || true; fi`," echo alert > /var/lib/ts-cloud/host-cleanup.alert 2>/dev/null || true","else",` if [ "$TS_CLOUD_HC_PREV" = alert ] && [ -x /usr/local/bin/ts-cloud-notify ]; then /usr/local/bin/ts-cloud-notify "✅ $(hostname): / back under ${c}% after host cleanup (\${TS_CLOUD_HC_AFTER}%)" >/dev/null 2>&1 || true; fi`," echo ok > /var/lib/ts-cloud/host-cleanup.alert 2>/dev/null || true","fi"]}function fg(e){return["#!/usr/bin/env bash","# Managed by ts-cloud: rewritten on every deploy. Configure compute.hostCleanup instead of editing.","set -uo pipefail","TS_CLOUD_HC_LOCK=/run/lock/ts-cloud-host-cleanup.lock","[ -d /run/lock ] || TS_CLOUD_HC_LOCK=/tmp/ts-cloud-host-cleanup.lock",'if command -v flock >/dev/null 2>&1 && exec 9>"$TS_CLOUD_HC_LOCK"; then',' if ! flock -n 9; then echo "[ts-cloud] host cleanup already running; skipping"; exit 0; fi',"fi",...zn(e),"exit 0"]}function hn(e,t,r,n,s){return[`cat > ${e}.ts-cloud-new <<'${r}' || true`,...t,r,`if [ -s ${e}.ts-cloud-new ] && ! cmp -s ${e}.ts-cloud-new ${e}; then chmod ${n} ${e}.ts-cloud-new && mv -f ${e}.ts-cloud-new ${e}${s?` && ${s}=1`:""} || true; else rm -f ${e}.ts-cloud-new; fi`]}function Fs(e){let t=vr(e),r="/etc/systemd/system/ts-cloud-host-cleanup",n=["if [ -f /etc/systemd/system/ts-cloud-host-cleanup.timer ]; then"," systemctl disable --now ts-cloud-host-cleanup.timer >/dev/null 2>&1 || true"," rm -f /etc/systemd/system/ts-cloud-host-cleanup.timer /etc/systemd/system/ts-cloud-host-cleanup.service"," systemctl daemon-reload >/dev/null 2>&1 || true","fi"];if(!t.enabled)return[...n,"rm -f /usr/local/bin/ts-cloud-host-cleanup.sh"];let s=hn("/usr/local/bin/ts-cloud-host-cleanup.sh",fg(e),"TS_CLOUD_HOST_CLEANUP_EOF","755");if(!t.timer)return[...s,...n];return[...s,"TS_CLOUD_HC_UNITS_CHANGED=0","if command -v systemctl >/dev/null 2>&1 && [ -d /etc/systemd/system ]; then",...hn("/etc/systemd/system/ts-cloud-host-cleanup.service",["[Unit]","Description=ts-cloud host cleanup (bounded disk retention)","After=local-fs.target","","[Service]","Type=oneshot","ExecStart=/usr/local/bin/ts-cloud-host-cleanup.sh","Nice=19","IOSchedulingClass=idle"],"TS_CLOUD_HOST_CLEANUP_SVC_EOF","644","TS_CLOUD_HC_UNITS_CHANGED"),...hn("/etc/systemd/system/ts-cloud-host-cleanup.timer",["[Unit]","Description=Run ts-cloud host cleanup on a schedule, independent of deploys","","[Timer]",`OnCalendar=${t.schedule}`,"RandomizedDelaySec=1h","Persistent=true","","[Install]","WantedBy=timers.target"],"TS_CLOUD_HOST_CLEANUP_TMR_EOF","644","TS_CLOUD_HC_UNITS_CHANGED"),' if [ "$TS_CLOUD_HC_UNITS_CHANGED" -eq 1 ]; then systemctl daemon-reload >/dev/null 2>&1 || true; fi'," systemctl enable --now ts-cloud-host-cleanup.timer >/dev/null 2>&1 || true","fi"]}function Kn(e){let t=Fs(e);if(!vr(e).enabled)return t;return[...t,'if [ -x /usr/local/bin/ts-cloud-host-cleanup.sh ]; then /usr/local/bin/ts-cloud-host-cleanup.sh < /dev/null || echo "[ts-cloud] warning: host cleanup exited $?" >&2; fi']}var oc=/\.(?:[cm]?[jt]sx?)$/;function Wn(e,t,r){let n=t==="bun"?"/usr/local/bin/bun":t==="deno"?"/usr/local/bin/deno":"/usr/local/bin/node",s=e.trim();if(/^(?:bun|node|deno)\s+/.test(s))return`${n} ${s.replace(/^(?:bun|node|deno)\s+/,"")}`;let[i="",...a]=s.split(/\s+/);if(i&&!oc.test(i)){let c=i.startsWith("/")?i:r&&i.startsWith("./")?`${r.replace(/\/+$/,"")}/${i.replace(/^\.\//,"")}`:void 0;if(c)return[c,...a].join(" ")}return`${n} ${s}`}function io(e){return["TS_CLOUD_MEM_MB=$(awk '/^MemTotal:/{print int($2/1024)}' /proc/meminfo)","TS_CLOUD_HIGH_MB=$((TS_CLOUD_MEM_MB / 8))",'[ "$TS_CLOUD_HIGH_MB" -lt 512 ] && TS_CLOUD_HIGH_MB=512','[ "$TS_CLOUD_HIGH_MB" -gt 4096 ] && TS_CLOUD_HIGH_MB=4096',`mkdir -p /etc/systemd/system/${e}.d`,`printf '# Written by ts-cloud: memoryHigh resolved from this box.
276
+ `)}function Ka(e){return`"${e.replace(/\\/g,"\\\\").replace(/"/g,"\\\"").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\t/g,"\\t")}"`}var Qt=["storage",".env"],Pt=4;function be(e){return`${e.replace(/\/+$/,"")}/.ts-cloud`}function Va(e){return`${be(e)}/deploy-history.log`}function Wa(e,t){return`${be(e)}/deploys/${t}.log`}function Ws(e){return`${be(e)}/shared-paths`}function Zt(e){return`${e.shared}/stx-images`}function Ft(e,t){let r=e.replace(/\/+$/,"");return{base:r,releases:`${r}/releases`,shared:`${r}/shared`,current:`${r}/current`,release:`${r}/releases/${t}`}}function Ys(e){let t=e.split("/").pop()||e;if(t===".env"||t.startsWith(".env."))return!0;return/\.[a-z0-9]+$/i.test(t)}function on(e){return typeof e==="string"?e:e.path}function Xs(e){let t=new Map;for(let r of e)t.set(on(r),r);return[...t.values()]}function Js(e,t){if(typeof t==="string")return{path:t,target:`${e.shared}/${t}`,seed:!0};return{path:t.path,target:t.target??`${e.shared}/${t.path}`,seed:t.seed!==!1}}function qa(e){return["ts_cloud_adopt_shared() {",' TS_CLOUD_SP="$1"',' TS_CLOUD_DST="$2"',` TS_CLOUD_SRC="${e.current}/$TS_CLOUD_SP"`,' if [ -e "$TS_CLOUD_DST" ]; then return 0; fi',' if [ -L "$TS_CLOUD_SRC" ] || [ ! -e "$TS_CLOUD_SRC" ]; then return 0; fi',' mkdir -p "$(dirname "$TS_CLOUD_DST")"',' cp -a "$TS_CLOUD_SRC" "$TS_CLOUD_DST"'," for TS_CLOUD_SIDECAR in -wal -shm; do",' if [ -f "$TS_CLOUD_SRC$TS_CLOUD_SIDECAR" ]; then cp -a "$TS_CLOUD_SRC$TS_CLOUD_SIDECAR" "$TS_CLOUD_DST$TS_CLOUD_SIDECAR"; fi'," done",' echo "[ts-cloud] adopted $TS_CLOUD_SP from the live release into shared/ — it now survives deploys"'," return 0","}"]}function Et(e,t=Qt){let r=[`mkdir -p ${e.releases} ${e.shared} ${Zt(e)}`,...qa(e)],n=t.map((i)=>Js(e,i));for(let{path:i,target:a,seed:c}of n){if(!c)continue;if(r.push(`ts_cloud_adopt_shared '${i}' '${a}'`),Ys(i))r.push(`mkdir -p "$(dirname ${a})"`,`touch ${a}`);else if(i==="storage")r.push(`mkdir -p ${a}/app/public`,`mkdir -p ${a}/framework/cache/data`,`mkdir -p ${a}/framework/sessions`,`mkdir -p ${a}/framework/testing`,`mkdir -p ${a}/framework/views`,`mkdir -p ${a}/logs`);else r.push(`mkdir -p ${a}`)}let s=Ws(e.base);return r.push(`mkdir -p ${be(e.base)}`,`cat > ${s} <<'TS_CLOUD_SHARED_PATHS_EOF'`,...n.map((i)=>`${i.path} ${i.target}`),"TS_CLOUD_SHARED_PATHS_EOF"),r}function an(e,t=900){let r=`${be(e.base)}/deploy.lock`;return[`mkdir -p "$(dirname ${r})"`,`exec 9>${r}`,`flock -w ${t} 9 || { echo "[ts-cloud] another deploy of this site has held the lock for ${t}s — refusing to race it" >&2; exit 1; }`]}function cn(e){let t=`${e.release}.incoming`;return[`rm -rf ${t}`,`mkdir -p ${t}`,...Ya(e),'if [ "$TS_CLOUD_IS_LIVE" = "yes" ]; then',` TS_CLOUD_STAGED=${t}`,"else",` rm -rf ${e.release}`,` mkdir -p ${e.release}`,` rmdir ${t}`,` TS_CLOUD_STAGED=${e.release}`,"fi"]}function ln(e){let t=`TS_CLOUD_TRAP_LIVE="$(readlink -f ${e.current} 2>/dev/null || true)"`,r=`TS_CLOUD_TRAP_TARGET="$(readlink -f ${e.release} 2>/dev/null || echo ${e.release})"`;return`trap '${`if [ $? -ne 0 ]; then ${t}; ${r}; [ "$TS_CLOUD_TRAP_LIVE" = "$TS_CLOUD_TRAP_TARGET" ] || rm -rf ${e.release}; fi`}' EXIT`}function Ya(e){return[`TS_CLOUD_LIVE_PATH="$(readlink -f ${e.current} 2>/dev/null || true)"`,`TS_CLOUD_RELEASE_PATH="$(readlink -f ${e.release} 2>/dev/null || echo ${e.release})"`,'if [ -n "$TS_CLOUD_LIVE_PATH" ] && [ "$TS_CLOUD_LIVE_PATH" = "$TS_CLOUD_RELEASE_PATH" ]; then TS_CLOUD_IS_LIVE=yes; else TS_CLOUD_IS_LIVE=no; fi']}function un(e){let t=`${e.release}.incoming`;return[`if [ "$TS_CLOUD_STAGED" = "${t}" ]; then`,` rm -rf ${e.release}.previous`,` mv ${e.release} ${e.release}.previous`,` mv ${t} ${e.release}`,` rm -rf ${e.release}.previous`,"fi"]}function Xa(e,t){return[`if [ -f ${e} ] && [ ! -L ${e} ] && [ -s ${e} ] && [ ! -s ${t} ]; then`,` cp -a ${e} ${t}`,` echo "[ts-cloud] seeded shared/ from the release's own copy of the file"`,"fi"]}function er(e,t=Qt){let r=[];for(let n of t){let{path:s,target:i,seed:a}=Js(e,n),c=`${e.release}/${s}`,l=[`rm -rf ${c}`,`mkdir -p "$(dirname ${c})"`,`ln -sfn ${i} ${c}`];if(a){if(s!==".env"&&Ys(s))r.push(...Xa(c,i));r.push(...l);continue}r.push(`if [ -e ${i} ]; then`,...l.map((m)=>` ${m}`),"fi")}return r}function Ja(e,t){let r=Ws(e.base);return[`if [ -f ${r} ]; then`,' while IFS="\t" read -r TS_CLOUD_SP TS_CLOUD_TGT; do',' [ -n "$TS_CLOUD_SP" ] || continue',` [ -n "$TS_CLOUD_TGT" ] || TS_CLOUD_TGT="${e.shared}/$TS_CLOUD_SP"`,' [ -e "$TS_CLOUD_TGT" ] || continue',` rm -rf ${t}/"$TS_CLOUD_SP"`,` mkdir -p "$(dirname ${t}/"$TS_CLOUD_SP")"`,` ln -sfn "$TS_CLOUD_TGT" ${t}/"$TS_CLOUD_SP"`,` done < ${r}`,"fi"]}function st(e){return[`ln -sfn ${e.release} ${e.current}.tmp`,`mv -Tf ${e.current}.tmp ${e.current}`]}function pn(e){return`${be(e)}/previous-release`}function dn(e,t="TS_CLOUD_PREV_CURRENT"){let r=pn(e.base);return[`if [ -n "\${${t}:-}" ] && [ -d "$${t}" ] && [ "$(readlink -f "$${t}")" != "$(readlink -f ${e.release})" ]; then mkdir -p ${be(e.base)}; basename "$${t}" > ${r}; fi`]}function Zs(e,t){let r=pn(e.base);return[`TS_CLOUD_CURRENT=$(readlink -f ${e.current} 2>/dev/null || true)`,...t?[`TS_CLOUD_RB_TARGET=${e.releases}/${t}`]:["TS_CLOUD_RB_TARGET=",`if [ -f ${r} ]; then TS_CLOUD_REC="${e.releases}/$(cat ${r})"; if [ -d "$TS_CLOUD_REC" ] && [ "$(readlink -f "$TS_CLOUD_REC")" != "$TS_CLOUD_CURRENT" ]; then TS_CLOUD_RB_TARGET="$TS_CLOUD_REC"; fi; fi`,`[ -n "$TS_CLOUD_RB_TARGET" ] || TS_CLOUD_RB_TARGET=$(ls -1dt ${e.releases}/*/ 2>/dev/null | sed 's#/$##' | while read -r r; do [ "$(readlink -f "$r")" != "$TS_CLOUD_CURRENT" ] && { echo "$r"; break; }; done)`],'[ -n "$TS_CLOUD_RB_TARGET" ] && [ -d "$TS_CLOUD_RB_TARGET" ] || { echo "no release to roll back to (looked for ${TS_CLOUD_RB_TARGET:-a previous release})" >&2; exit 1; }','[ "$(readlink -f "$TS_CLOUD_RB_TARGET")" != "$TS_CLOUD_CURRENT" ] || { echo "$(basename "$TS_CLOUD_RB_TARGET") is already the live release" >&2; exit 1; }','TS_CLOUD_RB_ID=$(basename "$TS_CLOUD_RB_TARGET")']}function ug(e,t={}){return[...Zs(e,t.to),'echo "would roll back from $(basename "${TS_CLOUD_CURRENT:-none}") to $TS_CLOUD_RB_ID (dry run: nothing changed)"']}function gg(e,t={}){let r=Math.max(1,t.gateSeconds??5),n=t.systemdDir??"/etc/systemd/system",s=[`ln -sfn "$TS_CLOUD_RB_TARGET" ${e.current}.tmp`,`mv -Tf ${e.current}.tmp ${e.current}`,`rm -f ${pn(e.base)}`,'echo "rolled back to $TS_CLOUD_RB_ID"'],i=[...Zs(e,t.to),...Ja(e,'"$TS_CLOUD_RB_TARGET"')];if(!t.unitBase)return[...i,...s];let a=t.unitBase,c=`${a}@\${TS_CLOUD_RB_ID}.service`;return[...i,`if [ -f ${n}/${a}@.service ]; then`,` systemctl start "${c}"`,` for TS_CLOUD_I in $(seq 1 ${r}); do sleep 1; systemctl is-active --quiet "${c}" || { echo "the release rolled back to, $TS_CLOUD_RB_ID, did not stay up; the current release keeps serving" >&2; journalctl -u "${c}" -n 30 --no-pager >&2 || true; systemctl stop "${c}" 2>/dev/null || true; exit 1; }; done`,...s.map((l)=>` ${l}`),` systemctl enable "${c}" 2>/dev/null || true`,` systemctl list-units --plain --no-legend --type=service "${a}@*.service" 2>/dev/null | awk '{print $1}' | { grep -v "^${a}@\${TS_CLOUD_RB_ID}.service$" || true; } | while read -r TS_CLOUD_U; do systemctl stop "$TS_CLOUD_U" 2>/dev/null || true; systemctl disable "$TS_CLOUD_U" 2>/dev/null || true; done`,`elif [ -f ${n}/${a}.service ]; then`,...s.map((l)=>` ${l}`),` systemctl restart ${a}.service`,"else",...s.map((l)=>` ${l}`),"fi"]}function ot(e,t=4){let r=Math.max(1,t);return[`TS_CLOUD_CURRENT=$(readlink -f ${e.current} 2>/dev/null || true)`,`ls -1dt ${e.releases}/*/ 2>/dev/null | sed 's#/$##' | tail -n +${r+1} | while read -r TS_CLOUD_OLD; do`,' [ "$(readlink -f "$TS_CLOUD_OLD")" = "$TS_CLOUD_CURRENT" ] || rm -rf "$TS_CLOUD_OLD"',"done"]}function Qa(e){return`${be(e)}/owner`}function mn(e,t){let r=Qa(e);return[`if [ -f "${r}" ]; then`,` TS_CLOUD_OWNER=$(cat "${r}")`,` if [ "$TS_CLOUD_OWNER" != "${t}" ]; then`,` echo "[ts-cloud] REFUSING deploy: ${e} belongs to project '$TS_CLOUD_OWNER', not '${t}'. Another project on this box derives the same site key — give one of them a distinct site name/domain, or remove ${r} to transfer ownership." >&2`," exit 1"," fi","else",` mkdir -p "$(dirname "${r}")"`,` printf '%s\\n' "${t}" > "${r}"`,"fi"]}function tr(e,t){let r=be(e),n=Wa(e,t.releaseId),s=Va(e),i=Math.max(1,t.keepLogs??20),a=t.commit||"",c=t.branch||"";return[`mkdir -p ${r}/deploys`,`exec > >(tee -a ${n}) 2>&1`,`echo "[ts-cloud] deploy ${t.releaseId} commit=${a} branch=${c} starting $(date -u +%Y-%m-%dT%H:%M:%SZ)"`,"ts_cloud_record_deploy() {"," TS_CLOUD_RC=$?",' if [ "$TS_CLOUD_RC" -eq 0 ]; then TS_CLOUD_ST=success; else TS_CLOUD_ST=failed; fi',` printf '%s\\t%s\\t%s\\t%s\\trc=%s\\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${t.releaseId}" "${a}" "$TS_CLOUD_ST" "$TS_CLOUD_RC" >> ${s}`,"}","trap ts_cloud_record_deploy EXIT",`ls -1t ${r}/deploys/*.log 2>/dev/null | tail -n +${i+1} | while read -r TS_CLOUD_OLDLOG; do rm -f "$TS_CLOUD_OLDLOG"; done`]}var Za=new Set(["sqlite","sqlite3"]),ec=new Set([":memory:","memory"]);function tc(e){let t=e?.DB_CONNECTION?.trim().toLowerCase();return t!=null&&Za.has(t)}function rc(e){let t=e.trim().replace(/^\.\//,"");if(t===""||t.startsWith("/")||t.startsWith("~"))return;if(t.split("/").includes(".."))return;return t.replace(/\/+$/,"")}function fn(e,t=[]){if(!tc(e))return{};let r=e?.DB_DATABASE_PATH?.trim()||e?.DB_DATABASE?.trim();if(r&&ec.has(r.toLowerCase()))return{};if(!r)return{warning:"DB_CONNECTION is sqlite but neither DB_DATABASE_PATH nor DB_DATABASE names a file, so ts-cloud cannot tell where the database lives. If it is written inside the release directory, the next deploy discards it. Set DB_DATABASE_PATH, or list the file in the site's `sharedPaths`."};let n=rc(r);if(!n)return{};if(t.some((s)=>on(s)===n))return{};return{path:n}}function rr(e,t=[]){let{path:r}=fn(e,t);return r?[r]:[]}var Er="/var/ts-cloud/artifacts",sh="/usr/local/bin/ts-cloud-host-cleanup.sh",oh="ts-cloud-host-cleanup";var pg=Object.freeze({stagingMaxAgeMinutes:60,artifactMaxAgeDays:2,artifactUploadMaxAgeMinutes:60,bunCacheMaxAgeDays:7,journalMaxAgeDays:14,journalMaxSizeMb:512,containerImageMaxAgeHours:168}),mg=Object.freeze({artifactMaxAgeDays:0,bunCacheMaxAgeDays:1,journalMaxAgeDays:3,journalMaxSizeMb:256,containerImageMaxAgeHours:24}),ah=50,ch=85,dh="daily";function eo(e){return`'${e.replace(/'/g,`'"'"'`)}'`}function Be(e,t,r=0){if(!Number.isInteger(t)||t<r)throw Error(`hostCleanup: ${e} must be a whole number >= ${r} (got ${t})`)}function to(e,t){for(let[r,n]of Object.entries(t))Be(`${e}.${r}`,n);Be(`${e}.stagingMaxAgeMinutes`,t.stagingMaxAgeMinutes,15),Be(`${e}.artifactUploadMaxAgeMinutes`,t.artifactUploadMaxAgeMinutes,15),Be(`${e}.journalMaxSizeMb`,t.journalMaxSizeMb,1)}function nc(e,t){let r=`hostCleanup.paths[${t}]`,n=e.path?.replace(/\/+$/,"")??"";if(!n.startsWith("/")||n===""||/[\s'"`$\\]/.test(n)||n.split("/").includes(".."))throw Error(`${r}.path must be an absolute directory with no spaces, quotes or '..' (got ${JSON.stringify(e.path)})`);if(n==="/var/www"||n.startsWith("/var/www/"))throw Error(`${r}.path must not be under /var/www: releases are pruned by keepReleases`);if(!e.pattern||/^[*?]+$/.test(e.pattern)||e.pattern.includes("/")||/[\n'"`]/.test(e.pattern))throw Error(`${r}.pattern must be a file-name glob naming something, not a bare wildcard (got ${JSON.stringify(e.pattern)})`);if(Be(`${r}.maxAgeDays`,e.maxAgeDays),e.type!==void 0&&e.type!=="file"&&e.type!=="directory")throw Error(`${r}.type must be 'file' or 'directory'`)}function vr(e){let t=typeof e==="object"&&e!==null?e:{},r=e!==!1,n={...pg,...ro(t.retention)};to("hostCleanup.retention",n);let s=null;if(t.pressure!==!1){let c=typeof t.pressure==="object"?t.pressure:{},l=c.lowWaterPercent??50,m=c.highWaterPercent??85;if(Be("hostCleanup.pressure.lowWaterPercent",l),Be("hostCleanup.pressure.highWaterPercent",m,1),m>100||l>=m)throw Error(`hostCleanup.pressure: need 0 <= lowWaterPercent < highWaterPercent <= 100 (got ${l} and ${m})`);let S={...mg,...ro(c.escalated)},b=Object.fromEntries(Object.keys(n).map((y)=>[y,Math.min(n[y],S[y]??n[y])]));to("hostCleanup.pressure.escalated",b),s={lowWaterPercent:l,highWaterPercent:m,escalated:b}}let i=t.schedule??"daily";if(!/^[\w*:,./~ -]+$/.test(i))throw Error(`hostCleanup.schedule must be a systemd OnCalendar expression (got ${JSON.stringify(i)})`);let a=t.paths??[];return a.forEach(nc),{enabled:r,timer:r&&t.timer!==!1,schedule:i,retention:n,pressure:s,paths:a}}function ro(e){return Object.fromEntries(Object.entries(e??{}).filter(([,t])=>t!==void 0))}var Rt={stagingMaxAgeMinutes:"TS_CLOUD_HC_STAGING_MIN",artifactMaxAgeDays:"TS_CLOUD_HC_ARTIFACT_DAYS",artifactUploadMaxAgeMinutes:"TS_CLOUD_HC_UPLOAD_MIN",bunCacheMaxAgeDays:"TS_CLOUD_HC_BUN_DAYS",journalMaxAgeDays:"TS_CLOUD_HC_JOURNAL_DAYS",journalMaxSizeMb:"TS_CLOUD_HC_JOURNAL_MB",containerImageMaxAgeHours:"TS_CLOUD_HC_IMAGE_HOURS"};function gn(e){return Object.keys(Rt).map((t)=>`${Rt[t]}=${e[t]}`).join(" ")}var Fe=(e)=>`"$${Rt[e]}"`;function no(e){return[`find /var/ts-cloud/staging -xdev -maxdepth 1 -type f -mmin +${Fe("stagingMaxAgeMinutes")} -delete 2>/dev/null || true`,`find /tmp -xdev -maxdepth 1 -type f -name "*-release.tar.gz" -mmin +${Fe("stagingMaxAgeMinutes")} -delete 2>/dev/null || true`,`find /var/ts-cloud/artifacts -xdev -maxdepth 1 -type f -name "*.tar.gz" -mtime +${Fe("artifactMaxAgeDays")} -delete 2>/dev/null || true`,`find /var/ts-cloud/artifacts -xdev -maxdepth 1 -type f -name ".*.tmp" -mmin +${Fe("artifactUploadMaxAgeMinutes")} -delete 2>/dev/null || true`,...e.map(sc),`journalctl --vacuum-time=${Fe("journalMaxAgeDays")}d --vacuum-size=${Fe("journalMaxSizeMb")}M >/dev/null 2>&1 || true`]}function sc(e){let t=eo(e.path.replace(/\/+$/,"")),r=eo(e.pattern);return e.type==="directory"?`find ${t} -xdev -mindepth 1 -maxdepth 1 -type d -name ${r} -mtime +${e.maxAgeDays} -exec rm -rf -- {} + 2>/dev/null || true`:`find ${t} -xdev -mindepth 1 -maxdepth 1 -type f -name ${r} -mtime +${e.maxAgeDays} -delete 2>/dev/null || true`}function so(){return[`find /root/.bun/install/cache -xdev -type f -mtime +${Fe("bunCacheMaxAgeDays")} -delete 2>/dev/null || true`,"find /root/.bun/install/cache -xdev -depth -type d -empty -delete 2>/dev/null || true",`if command -v docker >/dev/null 2>&1; then docker image prune --all --force --filter "until=\${${Rt.containerImageMaxAgeHours}}h" >/dev/null 2>&1 || true; fi`,`if command -v podman >/dev/null 2>&1; then podman image prune --all --force --filter "until=\${${Rt.containerImageMaxAgeHours}}h" >/dev/null 2>&1 || true; fi`,"if command -v apt-get >/dev/null 2>&1; then apt-get clean >/dev/null 2>&1 || true; fi"]}function oo(e){return[`${e}="$(df -P / 2>/dev/null | awk 'NR==2 {sub(/%/, "", $5); print $5}' || true)"`,`case "$${e}" in ''|*[!0-9]*) ${e}=-1 ;; esac`]}function zn(e){let{retention:t,pressure:r,paths:n}=vr(e),s='echo "[ts-cloud] host cleanup (disk before): $(df -h / | tail -1)"',i='echo "[ts-cloud] host cleanup (disk after): $(df -h / | tail -1)"';if(!r)return['echo "[ts-cloud] host cleanup (disk before): $(df -h / | tail -1)"',gn(t),...no(n),...so(),'echo "[ts-cloud] host cleanup (disk after): $(df -h / | tail -1)"'];let{lowWaterPercent:a,highWaterPercent:c,escalated:l}=r;return['echo "[ts-cloud] host cleanup (disk before): $(df -h / | tail -1)"',...oo("TS_CLOUD_HC_BEFORE"),"TS_CLOUD_HC_TIER=normal",gn(t),`if [ "$TS_CLOUD_HC_BEFORE" -ge 0 ] && [ "$TS_CLOUD_HC_BEFORE" -lt ${a} ]; then`," TS_CLOUD_HC_TIER=light",` echo "[ts-cloud] host cleanup: / at \${TS_CLOUD_HC_BEFORE}% (under ${a}%), skipping cache and image pruning"`,`elif [ "$TS_CLOUD_HC_BEFORE" -ge ${c} ]; then`," TS_CLOUD_HC_TIER=pressure",` ${gn(l)}`,` echo "[ts-cloud] warning: / at \${TS_CLOUD_HC_BEFORE}% (at or above ${c}%), host cleanup is using shortened retention windows" >&2`,"fi",...no(n),'if [ "$TS_CLOUD_HC_TIER" != light ]; then',...so().map((m)=>` ${m}`),"fi",'echo "[ts-cloud] host cleanup (disk after): $(df -h / | tail -1)"',...oo("TS_CLOUD_HC_AFTER"),`echo "[ts-cloud] host-cleanup {\\"event\\":\\"host-cleanup\\",\\"tier\\":\\"$TS_CLOUD_HC_TIER\\",\\"diskBeforePercent\\":$TS_CLOUD_HC_BEFORE,\\"diskAfterPercent\\":$TS_CLOUD_HC_AFTER,\\"lowWaterPercent\\":${a},\\"highWaterPercent\\":${c}}"`,"mkdir -p /var/lib/ts-cloud 2>/dev/null || true",'TS_CLOUD_HC_PREV="$(cat /var/lib/ts-cloud/host-cleanup.alert 2>/dev/null || echo ok)"',`if [ "$TS_CLOUD_HC_AFTER" -ge ${c} ]; then`,` echo "[ts-cloud] warning: / still at \${TS_CLOUD_HC_AFTER}% after host cleanup (high-water ${c}%); something outside ts-cloud's retention rules is filling the disk" >&2`,` if [ "$TS_CLOUD_HC_PREV" != alert ] && [ -x /usr/local/bin/ts-cloud-notify ]; then /usr/local/bin/ts-cloud-notify "⚠️ $(hostname): / at \${TS_CLOUD_HC_AFTER}% after host cleanup (high-water ${c}%)" >/dev/null 2>&1 || true; fi`," echo alert > /var/lib/ts-cloud/host-cleanup.alert 2>/dev/null || true","else",` if [ "$TS_CLOUD_HC_PREV" = alert ] && [ -x /usr/local/bin/ts-cloud-notify ]; then /usr/local/bin/ts-cloud-notify "✅ $(hostname): / back under ${c}% after host cleanup (\${TS_CLOUD_HC_AFTER}%)" >/dev/null 2>&1 || true; fi`," echo ok > /var/lib/ts-cloud/host-cleanup.alert 2>/dev/null || true","fi"]}function fg(e){return["#!/usr/bin/env bash","# Managed by ts-cloud: rewritten on every deploy. Configure compute.hostCleanup instead of editing.","set -uo pipefail","TS_CLOUD_HC_LOCK=/run/lock/ts-cloud-host-cleanup.lock","[ -d /run/lock ] || TS_CLOUD_HC_LOCK=/tmp/ts-cloud-host-cleanup.lock",'if command -v flock >/dev/null 2>&1 && exec 9>"$TS_CLOUD_HC_LOCK"; then',' if ! flock -n 9; then echo "[ts-cloud] host cleanup already running; skipping"; exit 0; fi',"fi",...zn(e),"exit 0"]}function hn(e,t,r,n,s){return[`cat > ${e}.ts-cloud-new <<'${r}' || true`,...t,r,`if [ -s ${e}.ts-cloud-new ] && ! cmp -s ${e}.ts-cloud-new ${e}; then chmod ${n} ${e}.ts-cloud-new && mv -f ${e}.ts-cloud-new ${e}${s?` && ${s}=1`:""} || true; else rm -f ${e}.ts-cloud-new; fi`]}function Fs(e){let t=vr(e),r="/etc/systemd/system/ts-cloud-host-cleanup",n=["if [ -f /etc/systemd/system/ts-cloud-host-cleanup.timer ]; then"," systemctl disable --now ts-cloud-host-cleanup.timer >/dev/null 2>&1 || true"," rm -f /etc/systemd/system/ts-cloud-host-cleanup.timer /etc/systemd/system/ts-cloud-host-cleanup.service"," systemctl daemon-reload >/dev/null 2>&1 || true","fi"];if(!t.enabled)return[...n,"rm -f /usr/local/bin/ts-cloud-host-cleanup.sh"];let s=hn("/usr/local/bin/ts-cloud-host-cleanup.sh",fg(e),"TS_CLOUD_HOST_CLEANUP_EOF","755");if(!t.timer)return[...s,...n];return[...s,"TS_CLOUD_HC_UNITS_CHANGED=0","if command -v systemctl >/dev/null 2>&1 && [ -d /etc/systemd/system ]; then",...hn("/etc/systemd/system/ts-cloud-host-cleanup.service",["[Unit]","Description=ts-cloud host cleanup (bounded disk retention)","After=local-fs.target","","[Service]","Type=oneshot","ExecStart=/usr/local/bin/ts-cloud-host-cleanup.sh","Nice=19","IOSchedulingClass=idle"],"TS_CLOUD_HOST_CLEANUP_SVC_EOF","644","TS_CLOUD_HC_UNITS_CHANGED"),...hn("/etc/systemd/system/ts-cloud-host-cleanup.timer",["[Unit]","Description=Run ts-cloud host cleanup on a schedule, independent of deploys","","[Timer]",`OnCalendar=${t.schedule}`,"RandomizedDelaySec=1h","Persistent=true","","[Install]","WantedBy=timers.target"],"TS_CLOUD_HOST_CLEANUP_TMR_EOF","644","TS_CLOUD_HC_UNITS_CHANGED"),' if [ "$TS_CLOUD_HC_UNITS_CHANGED" -eq 1 ]; then systemctl daemon-reload >/dev/null 2>&1 || true; fi'," systemctl enable --now ts-cloud-host-cleanup.timer >/dev/null 2>&1 || true","fi"]}function Kn(e){let t=Fs(e);if(!vr(e).enabled)return t;return[...t,'if [ -x /usr/local/bin/ts-cloud-host-cleanup.sh ]; then /usr/local/bin/ts-cloud-host-cleanup.sh < /dev/null || echo "[ts-cloud] warning: host cleanup exited $?" >&2; fi']}var oc=/\.(?:[cm]?[jt]sx?)$/;function Wn(e,t,r){let n=t==="bun"?"/usr/local/bin/bun":t==="deno"?"/usr/local/bin/deno":"/usr/local/bin/node",s=e.trim();if(/^(?:bun|node|deno)\s+/.test(s))return`${n} ${s.replace(/^(?:bun|node|deno)\s+/,"")}`;let[i="",...a]=s.split(/\s+/);if(i&&!oc.test(i)){let c=i.startsWith("/")?i:r&&i.startsWith("./")?`${r.replace(/\/+$/,"")}/${i.replace(/^\.\//,"")}`:void 0;if(c)return[c,...a].join(" ")}return`${n} ${s}`}function io(e){return["TS_CLOUD_MEM_MB=$(awk '/^MemTotal:/{print int($2/1024)}' /proc/meminfo)","TS_CLOUD_HIGH_MB=$((TS_CLOUD_MEM_MB / 8))",'[ "$TS_CLOUD_HIGH_MB" -lt 512 ] && TS_CLOUD_HIGH_MB=512','[ "$TS_CLOUD_HIGH_MB" -gt 4096 ] && TS_CLOUD_HIGH_MB=4096',`mkdir -p /etc/systemd/system/${e}.d`,`printf '# Written by ts-cloud: memoryHigh resolved from this box.
277
277
  # An eighth of %sMB RAM. Set memoryHigh in config to override.
278
278
  [Service]
279
279
  MemoryAccounting=true
@@ -400,7 +400,7 @@ ${s}`);if(/status:\s*(?:done|degraded)/.test(s))return}catch(s){if(s instanceof
400
400
  `}function Cl(e){return[`instance-id: ${le(`ts-cloud-${e.hostname}`)}`,`local-hostname: ${le(e.hostname)}`].join(`
401
401
  `)+`
402
402
  `}function Pl(e,t){return[`Copy user-data, network-config and meta-data onto the boot partition of the flashed card (the volume named '${Lg[t]}'), replacing the files already there.`,"Eject the card, put it in the board and power it on.","The first boot takes a few minutes: cloud-init creates the user, waits for the clock to sync, then runs the ts-cloud bootstrap (packages, runtime, gateway).",`Then, from this machine: cloud ssh:preflight ${e.hostname}.local --user ${e.user}`,"When the preflight is clean, cloud deploy adopts the host and ships the sites."].join(`
403
- `)}function Pc(e,t,r){bl(e);let n={scriptPath:_g,permissions:"0700",before:Tl(e,r.os)};return{files:{"user-data":Y(t,n),"network-config":wl(e),"meta-data":Cl(e)},instructions:Pl(e,r.os)}}function El(e){return e.location?.name??e.datacenter?.location.name??"unknown"}function gt(e){return typeof e==="number"&&Number.isFinite(e)?e:0}function Rl(e,t){if(e.architecture&&t.architecture&&e.architecture!==t.architecture)throw Error(`Cannot resize ${e.name} (${e.architecture}) to ${t.name} (${t.architecture}) in place.`);let r=[];if(gt(t.cores)<gt(e.cores))r.push("CPU");if(gt(t.memory)<gt(e.memory))r.push("memory");if(gt(t.disk)<gt(e.disk))r.push("disk");if(r.length>0)throw Error(`Refusing an in-place downgrade from ${e.name} to ${t.name}: ${r.join(", ")} would shrink.`)}async function $g(e,t,r,n=!0){let[s,i]=await Promise.all([e.getServer(t),e.getServerType(r)]);if(!i)throw Error(`Hetzner server type ${r} was not found.`);let a=s.server_type,c=a.name===i.name;if(!c)Rl(a,i);let l=El(s),m=i.locations?.find((S)=>S.name===l);return{serverId:t,serverName:s.name,location:l,currentType:a,targetType:i,capacityAvailable:m?.available!==!1,alreadyComplete:c,upgradeDisk:n}}function Mg(e){if(e instanceof cr&&e.code==="resource_unavailable")return!0;if(e instanceof lr){if(e.action.error?.code==="resource_unavailable")return!0;if(/no fitting vhost|placement|free resources/i.test(e.action.error?.message??""))return!0}return/resource_unavailable|no fitting vhost|error during placement|free resources/i.test(e instanceof Error?e.message:String(e))}async function Ce(e,t,r){await e?.onPhase?.(t,r)}async function Cn(e,t,r){await e.waitForAction(t.id,{pollIntervalMs:r.actionPollIntervalMs,maxWaitMs:r.actionTimeoutMs})}var pr=async({client:e,hooks:t,actionPollIntervalMs:r,actionTimeoutMs:n,serverPollIntervalMs:s,serverTimeoutMs:i},a,c,l)=>{let m=await e.getServer(a.serverId);if(m.status!=="running"){await Ce(t,l?"recovering":"powering-on",{serverId:a.serverId});let b=await e.powerOnServer(a.serverId);await Cn(e,b,{actionPollIntervalMs:r,actionTimeoutMs:n}),m=await e.waitForServerRunning(a.serverId,{pollIntervalMs:s,maxWaitMs:i})}await Ce(t,"verifying",{serverId:a.serverId,recovered:l});let S=await t?.afterBoot?.(m,a.targetType,c,{recovered:l});return{server:m,verification:S}};async function dh(e){let{client:t,hooks:r}=e,n=e.upgradeDisk??!0;await Ce(r,"planning",{serverId:e.serverId,targetType:e.targetType});let s=await $g(t,e.serverId,e.targetType,n),i=await t.getServer(s.serverId);if(s.alreadyComplete){let l=await pr(e,s,e.manifest,!1);return await Ce(r,"complete",{alreadyComplete:!0}),{status:"already-complete",plan:s,verification:l.verification}}if(!s.capacityAvailable&&!e.attemptUnavailable)return await Ce(r,"waiting-capacity",{location:s.location,targetType:s.targetType.name}),{status:"waiting-capacity",plan:s};let a=e.manifest;if(i.status==="off"&&!e.resumeFromOff){let l=await pr(e,s,void 0,!0);return{status:"recovered",plan:s,server:l.server,verification:l.verification,retryable:!0,error:"Server was already off without a resumable resize checkpoint; powered it back on."}}if(i.status==="running"){await Ce(r,"preflight",{serverId:s.serverId}),a=await r?.preflight?.(i,s.targetType),await Ce(r,"shutting-down",{serverId:s.serverId});let l=await t.shutdownServer(s.serverId);await Cn(t,l,e),i=await t.waitForServerStatus(s.serverId,"off",{pollIntervalMs:e.serverPollIntervalMs,maxWaitMs:e.serverTimeoutMs})}try{await Ce(r,"resizing",{serverId:s.serverId,from:s.currentType.name,to:s.targetType.name,upgradeDisk:n});let l=await t.changeServerType(s.serverId,s.targetType.name,n);await Cn(t,l,e)}catch(l){let m=await pr(e,s,a,!0);return{status:"recovered",plan:s,server:m.server,manifest:a,verification:m.verification,retryable:Mg(l),error:l instanceof Error?l.message:String(l)}}let c=await pr(e,s,a,!1);if(c.verification&&!c.verification.ok)throw Error(`Resize completed but verification failed: ${(c.verification.failures??[]).join("; ")}`);return await Ce(r,"complete",{serverId:s.serverId,targetType:s.targetType.name}),{status:"completed",plan:s,server:c.server,manifest:a,verification:c.verification}}var xl=String.raw`
403
+ `)}function Pc(e,t,r){bl(e);let n={scriptPath:_g,permissions:"0700",before:Tl(e,r.os)};return{files:{"user-data":Y(t,n),"network-config":wl(e),"meta-data":Cl(e)},instructions:Pl(e,r.os)}}function El(e){return e.location?.name??e.datacenter?.location.name??"unknown"}function gt(e){return typeof e==="number"&&Number.isFinite(e)?e:0}function Rl(e,t){if(e.architecture&&t.architecture&&e.architecture!==t.architecture)throw Error(`Cannot resize ${e.name} (${e.architecture}) to ${t.name} (${t.architecture}) in place.`);let r=[];if(gt(t.cores)<gt(e.cores))r.push("CPU");if(gt(t.memory)<gt(e.memory))r.push("memory");if(gt(t.disk)<gt(e.disk))r.push("disk");if(r.length>0)throw Error(`Refusing an in-place downgrade from ${e.name} to ${t.name}: ${r.join(", ")} would shrink.`)}async function $g(e,t,r,n=!0){let[s,i]=await Promise.all([e.getServer(t),e.getServerType(r)]);if(!i)throw Error(`Hetzner server type ${r} was not found.`);let a=s.server_type,c=a.name===i.name;if(!c)Rl(a,i);let l=El(s),m=i.locations?.find((S)=>S.name===l);return{serverId:t,serverName:s.name,location:l,currentType:a,targetType:i,capacityAvailable:m?.available!==!1,alreadyComplete:c,upgradeDisk:n}}function Mg(e){if(e instanceof cr&&e.code==="resource_unavailable")return!0;if(e instanceof lr){if(e.action.error?.code==="resource_unavailable")return!0;if(/no fitting vhost|placement|free resources/i.test(e.action.error?.message??""))return!0}return/resource_unavailable|no fitting vhost|error during placement|free resources/i.test(e instanceof Error?e.message:String(e))}async function Ce(e,t,r){await e?.onPhase?.(t,r)}async function Cn(e,t,r){await e.waitForAction(t.id,{pollIntervalMs:r.actionPollIntervalMs,maxWaitMs:r.actionTimeoutMs})}var pr=async({client:e,hooks:t,actionPollIntervalMs:r,actionTimeoutMs:n,serverPollIntervalMs:s,serverTimeoutMs:i},a,c,l)=>{let m=await e.getServer(a.serverId);if(m.status!=="running"){await Ce(t,l?"recovering":"powering-on",{serverId:a.serverId});let b=await e.powerOnServer(a.serverId);await Cn(e,b,{actionPollIntervalMs:r,actionTimeoutMs:n}),m=await e.waitForServerRunning(a.serverId,{pollIntervalMs:s,maxWaitMs:i})}await Ce(t,"verifying",{serverId:a.serverId,recovered:l});let S=await t?.afterBoot?.(m,a.targetType,c,{recovered:l});return{server:m,verification:S}};async function lh(e){let{client:t,hooks:r}=e,n=e.upgradeDisk??!0;await Ce(r,"planning",{serverId:e.serverId,targetType:e.targetType});let s=await $g(t,e.serverId,e.targetType,n),i=await t.getServer(s.serverId);if(s.alreadyComplete){let l=await pr(e,s,e.manifest,!1);return await Ce(r,"complete",{alreadyComplete:!0}),{status:"already-complete",plan:s,verification:l.verification}}if(!s.capacityAvailable&&!e.attemptUnavailable)return await Ce(r,"waiting-capacity",{location:s.location,targetType:s.targetType.name}),{status:"waiting-capacity",plan:s};let a=e.manifest;if(i.status==="off"&&!e.resumeFromOff){let l=await pr(e,s,void 0,!0);return{status:"recovered",plan:s,server:l.server,verification:l.verification,retryable:!0,error:"Server was already off without a resumable resize checkpoint; powered it back on."}}if(i.status==="running"){await Ce(r,"preflight",{serverId:s.serverId}),a=await r?.preflight?.(i,s.targetType),await Ce(r,"shutting-down",{serverId:s.serverId});let l=await t.shutdownServer(s.serverId);await Cn(t,l,e),i=await t.waitForServerStatus(s.serverId,"off",{pollIntervalMs:e.serverPollIntervalMs,maxWaitMs:e.serverTimeoutMs})}try{await Ce(r,"resizing",{serverId:s.serverId,from:s.currentType.name,to:s.targetType.name,upgradeDisk:n});let l=await t.changeServerType(s.serverId,s.targetType.name,n);await Cn(t,l,e)}catch(l){let m=await pr(e,s,a,!0);return{status:"recovered",plan:s,server:m.server,manifest:a,verification:m.verification,retryable:Mg(l),error:l instanceof Error?l.message:String(l)}}let c=await pr(e,s,a,!1);if(c.verification&&!c.verification.ok)throw Error(`Resize completed but verification failed: ${(c.verification.failures??[]).join("; ")}`);return await Ce(r,"complete",{serverId:s.serverId,targetType:s.targetType.name}),{status:"completed",plan:s,server:c.server,manifest:a,verification:c.verification}}var xl=String.raw`
404
404
  const { readdir, readFile, readlink, stat } = await import('node:fs/promises')
405
405
 
406
406
  const text = (command) => {
@@ -492,7 +492,7 @@ console.log(JSON.stringify({
492
492
  persistentData: persistentData.sort(),
493
493
  dataCatalog: dataCatalog.sort(),
494
494
  }))
495
- `;function Ol(e){return`printf %s ${Buffer.from(e).toString("base64")} | base64 -d | bun -`}async function Dl(e){let t=await ge(e.host,Ol(xl),e);return JSON.parse(t)}async function Il(e,t){let r="";for(let n=1;n<=3;n++){let s=Bun.spawn(["curl","--location","--silent","--show-error","--output","/dev/null","--max-time",String(Math.max(1,Math.ceil(t/1000))),"--write-out","%{http_code}",`https://${e}/`],{stdout:"pipe",stderr:"pipe"}),[i,a,c]=await Promise.all([new Response(s.stdout).text(),new Response(s.stderr).text(),s.exited]),l=Number.parseInt(i.trim(),10);if(c===0&&l>0)return{domain:e,ok:l<500,status:l};if(r=a.trim()||`curl exited ${c}`,n<3)await Bun.sleep(500*n)}return{domain:e,ok:!1,error:r}}async function Al(e,t){let r=[],n=0,s=Array.from({length:Math.min(4,e.length)},async()=>{for(;;){let i=n++,a=e[i];if(!a)return;r[i]=await Il(a,t)}});return await Promise.all(s),r}async function Nc(e){let t=await Dl(e),r=await Al(t.routeDomains,e.routeTimeoutMs??15000);return{...t,routeProbes:r}}async function lh(e){let t=await Nc(e),r=t.routeProbes.filter((n)=>!n.ok);if(t.failedUnits.length>0)throw Error(`Preflight found failed services: ${t.failedUnits.join(", ")}`);if(r.length>0)throw Error(`Preflight found unhealthy routes: ${r.map((n)=>n.domain).join(", ")}`);return await ge(e.host,String.raw`systemctl list-units --type=service --state=running --no-legend --plain | awk '{print $1}' | while read -r unit; do if systemctl cat "$unit" 2>/dev/null | grep -Eq '/var/www|/usr/local/bin/rpx|/opt/ts-cloud'; then systemctl enable "$unit" >/dev/null 2>&1 || true; fi; done; systemctl daemon-reload; sync`,e),t}function ht(e,t){let r=new Set(t);return e.filter((n)=>!r.has(n))}function Ll(e){return Math.floor((e.memory??0)*1024*1024*1024)}async function Ul(e){await ge(e.host,String.raw`set -e
495
+ `;function Ol(e){return`printf %s ${Buffer.from(e).toString("base64")} | base64 -d | bun -`}async function Dl(e){let t=await ge(e.host,Ol(xl),e);return JSON.parse(t)}async function Il(e,t){let r="";for(let n=1;n<=3;n++){let s=Bun.spawn(["curl","--location","--silent","--show-error","--output","/dev/null","--max-time",String(Math.max(1,Math.ceil(t/1000))),"--write-out","%{http_code}",`https://${e}/`],{stdout:"pipe",stderr:"pipe"}),[i,a,c]=await Promise.all([new Response(s.stdout).text(),new Response(s.stderr).text(),s.exited]),l=Number.parseInt(i.trim(),10);if(c===0&&l>0)return{domain:e,ok:l<500,status:l};if(r=a.trim()||`curl exited ${c}`,n<3)await Bun.sleep(500*n)}return{domain:e,ok:!1,error:r}}async function Al(e,t){let r=[],n=0,s=Array.from({length:Math.min(4,e.length)},async()=>{for(;;){let i=n++,a=e[i];if(!a)return;r[i]=await Il(a,t)}});return await Promise.all(s),r}async function Nc(e){let t=await Dl(e),r=await Al(t.routeDomains,e.routeTimeoutMs??15000);return{...t,routeProbes:r}}async function uh(e){let t=await Nc(e),r=t.routeProbes.filter((n)=>!n.ok);if(t.failedUnits.length>0)throw Error(`Preflight found failed services: ${t.failedUnits.join(", ")}`);if(r.length>0)throw Error(`Preflight found unhealthy routes: ${r.map((n)=>n.domain).join(", ")}`);return await ge(e.host,String.raw`systemctl list-units --type=service --state=running --no-legend --plain | awk '{print $1}' | while read -r unit; do if systemctl cat "$unit" 2>/dev/null | grep -Eq '/var/www|/usr/local/bin/rpx|/opt/ts-cloud'; then systemctl enable "$unit" >/dev/null 2>&1 || true; fi; done; systemctl daemon-reload; sync`,e),t}function ht(e,t){let r=new Set(t);return e.filter((n)=>!r.has(n))}function Ll(e){return Math.floor((e.memory??0)*1024*1024*1024)}async function Ul(e){await ge(e.host,String.raw`set -e
496
496
  root_source="$(findmnt -n -o SOURCE /)"
497
497
  root_fs="$(findmnt -n -o FSTYPE /)"
498
498
  parent="$(lsblk -n -o PKNAME "$root_source" | head -1)"
@@ -505,7 +505,7 @@ case "$root_fs" in
505
505
  xfs) xfs_growfs / ;;
506
506
  *) echo "Unsupported root filesystem: $root_fs" >&2; exit 1 ;;
507
507
  esac`,e)}async function Hl(e,t){let r=Buffer.from(t.join(`
508
- `)).toString("base64");await ge(e.host,`printf %s ${r} | base64 -d | while read -r unit; do [ -z "$unit" ] || systemctl is-active --quiet "$unit" || systemctl start "$unit"; done`,e)}async function uh(e,t,r,n={recovered:!1}){if(await qn(e.host,{...e,timeoutMs:e.sshTimeoutMs??600000}),!n.recovered)await Ul(e);if(r)await Hl(e,r.runningServices);let s=await Nc(e),i=[],a=r?ht(r.runningServices,s.runningServices):[],c=r?ht(r.releaseLinks,s.releaseLinks):[],l=r?ht(r.routeFragments,s.routeFragments):[],m=r?ht(r.routeIds,s.routeIds):[],S=r?.persistentData.map((E)=>E.split("=")[0])??[],b=s.persistentData.map((E)=>E.split("=")[0]),y=ht(S,b),f=r?ht(r.dataCatalog,s.dataCatalog):[],T=s.routeProbes.filter((E)=>!E.ok),D=n.recovered&&r?r.cpuCores:t.cores??0,R=n.recovered&&r?r.memoryBytes:Ll(t),C=s.cpuCores>=D,P=s.memoryBytes>=R*0.9,w=n.recovered||s.rootDiskBytes>=(t.disk??0)*1e9*0.95;if(s.failedUnits.length>0)i.push(`Failed services: ${s.failedUnits.join(", ")}`);if(a.length>0)i.push(`Services did not return: ${a.join(", ")}`);if(c.length>0)i.push(`Release links changed: ${c.join(", ")}`);if(l.length>0)i.push(`Route manifests changed: ${l.join(", ")}`);if(m.length>0)i.push(`Routes disappeared: ${m.join(", ")}`);if(y.length>0)i.push(`Persistent data paths disappeared: ${y.join(", ")}`);if(f.length>0)i.push(`Databases or volumes disappeared: ${f.join(", ")}`);if(T.length>0)i.push(`Unhealthy routes: ${T.map((E)=>E.domain).join(", ")}`);if(!C)i.push(`Expected at least ${D} CPU cores, found ${s.cpuCores}`);if(!P)i.push(`Expected at least ${R} bytes of RAM`);if(!w)i.push(`Expected approximately ${t.disk??0} GB root disk`);return{ok:i.length===0,checks:{cpuCores:s.cpuCores,memoryBytes:s.memoryBytes,rootDiskBytes:s.rootDiskBytes,rootFilesystemBytes:s.rootFilesystemBytes,runningServices:s.runningServices.length,releaseLinks:s.releaseLinks.length,routeFragments:s.routeFragments.length,routes:s.routeIds.length,healthyRoutes:s.routeProbes.filter((E)=>E.ok).length,persistentDataPaths:s.persistentData.length,databasesAndVolumes:s.dataCatalog.length,failedUnits:s.failedUnits.length},failures:i}}function Po(e){return e.infrastructure?.compute??{}}function Fg(e){let t=Po(e),r=t.monitoring;return{firewallPorts:[...new Set([22,80,443,...t.firewall?.allowedPorts??[]])].filter((s)=>Number.isInteger(s)&&s>=1&&s<=65535).sort((s,i)=>s-i),monitoring:r!==!1&&(typeof r!=="object"||r.enabled!==!1),autoUpdates:t.autoUpdates!==!1,swapGb:Math.max(0,Math.floor(t.swapGb??2)),sshPasswordAuthentication:!1,sshMaxStartups:"100:30:200",journalMaxUse:"256M",journalRetention:"14day"}}function Nl(e){return["install -d -m 0755 /etc/ssh/sshd_config.d /etc/systemd/journald.conf.d /etc/sysctl.d /etc/fail2ban/jail.d","cat > /etc/ssh/sshd_config.d/99-ts-cloud-hardening.conf <<'TS_CLOUD_SSH_EOF'","# Managed by ts-cloud host optimization.","PasswordAuthentication no","KbdInteractiveAuthentication no","PermitRootLogin prohibit-password","MaxAuthTries 4","LoginGraceTime 30",`MaxStartups ${e.sshMaxStartups}`,"TS_CLOUD_SSH_EOF","sshd -t","systemctl reload ssh","cat > /etc/systemd/journald.conf.d/99-ts-cloud-retention.conf <<'TS_CLOUD_JOURNAL_EOF'","[Journal]",`SystemMaxUse=${e.journalMaxUse}`,`MaxRetentionSec=${e.journalRetention}`,"Compress=yes","TS_CLOUD_JOURNAL_EOF","systemctl restart systemd-journald","journalctl --vacuum-size=256M --vacuum-time=14d >/dev/null","cat > /etc/sysctl.d/99-ts-cloud-production.conf <<'TS_CLOUD_SYSCTL_EOF'","# Managed by ts-cloud host optimization.","vm.swappiness=10","vm.dirty_background_ratio=5","vm.dirty_ratio=15","net.core.somaxconn=4096","net.ipv4.tcp_max_syn_backlog=4096","net.ipv4.tcp_fin_timeout=30","net.ipv4.tcp_keepalive_time=300","net.ipv4.tcp_keepalive_intvl=30","net.ipv4.tcp_keepalive_probes=5","fs.inotify.max_user_watches=524288","TS_CLOUD_SYSCTL_EOF","sysctl --system >/dev/null","export DEBIAN_FRONTEND=noninteractive","command -v fail2ban-client >/dev/null 2>&1 || apt-get install -y fail2ban","cat > /etc/fail2ban/jail.d/99-ts-cloud-sshd.conf <<'TS_CLOUD_FAIL2BAN_EOF'","[sshd]","enabled = true","backend = systemd","bantime = 1h","findtime = 10m","maxretry = 5","TS_CLOUD_FAIL2BAN_EOF","systemctl enable --now fail2ban","systemctl restart fail2ban"]}function kl(e){if(e===0)return[];return["if ! swapon --show=NAME --noheadings 2>/dev/null | grep -qx '/swapfile'; then",` if [ ! -f /swapfile ]; then fallocate -l ${e}G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=${e*1024} status=none; fi`," chmod 600 /swapfile"," mkswap /swapfile >/dev/null"," swapon /swapfile","fi","grep -qE '^/swapfile\\s' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab"]}function Ug(e){let t=Po(e),r=Fg(e),n=t.monitoring??!0,s=t.firewall??{enabled:!0};return["set -euo pipefail",...se(t)?["systemctl disable --now nginx.service bun-gateway.service ts-cloud-nginx.service 2>/dev/null || true","systemctl reset-failed nginx.service bun-gateway.service ts-cloud-nginx.service 2>/dev/null || true"]:[],...kl(r.swapGb),...Oe(r.autoUpdates),...Ie(n),...Nl(r),...qt(s),"systemctl daemon-reload","systemctl reset-failed"]}var Ml=String.raw`
508
+ `)).toString("base64");await ge(e.host,`printf %s ${r} | base64 -d | while read -r unit; do [ -z "$unit" ] || systemctl is-active --quiet "$unit" || systemctl start "$unit"; done`,e)}async function gh(e,t,r,n={recovered:!1}){if(await qn(e.host,{...e,timeoutMs:e.sshTimeoutMs??600000}),!n.recovered)await Ul(e);if(r)await Hl(e,r.runningServices);let s=await Nc(e),i=[],a=r?ht(r.runningServices,s.runningServices):[],c=r?ht(r.releaseLinks,s.releaseLinks):[],l=r?ht(r.routeFragments,s.routeFragments):[],m=r?ht(r.routeIds,s.routeIds):[],S=r?.persistentData.map((E)=>E.split("=")[0])??[],b=s.persistentData.map((E)=>E.split("=")[0]),y=ht(S,b),f=r?ht(r.dataCatalog,s.dataCatalog):[],T=s.routeProbes.filter((E)=>!E.ok),D=n.recovered&&r?r.cpuCores:t.cores??0,R=n.recovered&&r?r.memoryBytes:Ll(t),C=s.cpuCores>=D,P=s.memoryBytes>=R*0.9,w=n.recovered||s.rootDiskBytes>=(t.disk??0)*1e9*0.95;if(s.failedUnits.length>0)i.push(`Failed services: ${s.failedUnits.join(", ")}`);if(a.length>0)i.push(`Services did not return: ${a.join(", ")}`);if(c.length>0)i.push(`Release links changed: ${c.join(", ")}`);if(l.length>0)i.push(`Route manifests changed: ${l.join(", ")}`);if(m.length>0)i.push(`Routes disappeared: ${m.join(", ")}`);if(y.length>0)i.push(`Persistent data paths disappeared: ${y.join(", ")}`);if(f.length>0)i.push(`Databases or volumes disappeared: ${f.join(", ")}`);if(T.length>0)i.push(`Unhealthy routes: ${T.map((E)=>E.domain).join(", ")}`);if(!C)i.push(`Expected at least ${D} CPU cores, found ${s.cpuCores}`);if(!P)i.push(`Expected at least ${R} bytes of RAM`);if(!w)i.push(`Expected approximately ${t.disk??0} GB root disk`);return{ok:i.length===0,checks:{cpuCores:s.cpuCores,memoryBytes:s.memoryBytes,rootDiskBytes:s.rootDiskBytes,rootFilesystemBytes:s.rootFilesystemBytes,runningServices:s.runningServices.length,releaseLinks:s.releaseLinks.length,routeFragments:s.routeFragments.length,routes:s.routeIds.length,healthyRoutes:s.routeProbes.filter((E)=>E.ok).length,persistentDataPaths:s.persistentData.length,databasesAndVolumes:s.dataCatalog.length,failedUnits:s.failedUnits.length},failures:i}}function Po(e){return e.infrastructure?.compute??{}}function Fg(e){let t=Po(e),r=t.monitoring;return{firewallPorts:[...new Set([22,80,443,...t.firewall?.allowedPorts??[]])].filter((s)=>Number.isInteger(s)&&s>=1&&s<=65535).sort((s,i)=>s-i),monitoring:r!==!1&&(typeof r!=="object"||r.enabled!==!1),autoUpdates:t.autoUpdates!==!1,swapGb:Math.max(0,Math.floor(t.swapGb??2)),sshPasswordAuthentication:!1,sshMaxStartups:"100:30:200",journalMaxUse:"256M",journalRetention:"14day"}}function Nl(e){return["install -d -m 0755 /etc/ssh/sshd_config.d /etc/systemd/journald.conf.d /etc/sysctl.d /etc/fail2ban/jail.d","cat > /etc/ssh/sshd_config.d/99-ts-cloud-hardening.conf <<'TS_CLOUD_SSH_EOF'","# Managed by ts-cloud host optimization.","PasswordAuthentication no","KbdInteractiveAuthentication no","PermitRootLogin prohibit-password","MaxAuthTries 4","LoginGraceTime 30",`MaxStartups ${e.sshMaxStartups}`,"TS_CLOUD_SSH_EOF","sshd -t","systemctl reload ssh","cat > /etc/systemd/journald.conf.d/99-ts-cloud-retention.conf <<'TS_CLOUD_JOURNAL_EOF'","[Journal]",`SystemMaxUse=${e.journalMaxUse}`,`MaxRetentionSec=${e.journalRetention}`,"Compress=yes","TS_CLOUD_JOURNAL_EOF","systemctl restart systemd-journald","journalctl --vacuum-size=256M --vacuum-time=14d >/dev/null","cat > /etc/sysctl.d/99-ts-cloud-production.conf <<'TS_CLOUD_SYSCTL_EOF'","# Managed by ts-cloud host optimization.","vm.swappiness=10","vm.dirty_background_ratio=5","vm.dirty_ratio=15","net.core.somaxconn=4096","net.ipv4.tcp_max_syn_backlog=4096","net.ipv4.tcp_fin_timeout=30","net.ipv4.tcp_keepalive_time=300","net.ipv4.tcp_keepalive_intvl=30","net.ipv4.tcp_keepalive_probes=5","fs.inotify.max_user_watches=524288","TS_CLOUD_SYSCTL_EOF","sysctl --system >/dev/null","export DEBIAN_FRONTEND=noninteractive","command -v fail2ban-client >/dev/null 2>&1 || apt-get install -y fail2ban","cat > /etc/fail2ban/jail.d/99-ts-cloud-sshd.conf <<'TS_CLOUD_FAIL2BAN_EOF'","[sshd]","enabled = true","backend = systemd","bantime = 1h","findtime = 10m","maxretry = 5","TS_CLOUD_FAIL2BAN_EOF","systemctl enable --now fail2ban","systemctl restart fail2ban"]}function kl(e){if(e===0)return[];return["if ! swapon --show=NAME --noheadings 2>/dev/null | grep -qx '/swapfile'; then",` if [ ! -f /swapfile ]; then fallocate -l ${e}G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=${e*1024} status=none; fi`," chmod 600 /swapfile"," mkswap /swapfile >/dev/null"," swapon /swapfile","fi","grep -qE '^/swapfile\\s' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab"]}function Ug(e){let t=Po(e),r=Fg(e),n=t.monitoring??!0,s=t.firewall??{enabled:!0};return["set -euo pipefail",...se(t)?["systemctl disable --now nginx.service bun-gateway.service ts-cloud-nginx.service 2>/dev/null || true","systemctl reset-failed nginx.service bun-gateway.service ts-cloud-nginx.service 2>/dev/null || true"]:[],...kl(r.swapGb),...Oe(r.autoUpdates),...Ie(n),...Nl(r),...qt(s),"systemctl daemon-reload","systemctl reset-failed"]}var Ml=String.raw`
509
509
  const text = (command) => {
510
510
  const result = Bun.spawnSync(['bash', '-lc', command], { stdout: 'pipe', stderr: 'pipe' })
511
511
  return result.stdout.toString().trim()
@@ -536,14 +536,14 @@ console.log(JSON.stringify({
536
536
  failedUnits: lines("systemctl --failed --type=service --no-legend --plain | awk '{print $1}'"),
537
537
  publicTcpPorts: [...new Set(publicTcpPorts)].sort((a, b) => a - b),
538
538
  }))
539
- `;function Fl(e){return`printf %s ${Buffer.from(e).toString("base64")} | base64 -d | bun -`}async function gh(e,t){let r=Ug(e).join(`
540
- `),n=`printf %s ${Buffer.from(r).toString("base64")} | base64 -d | bash`;await ge(t.host,n,t)}async function ph(e){let t=await ge(e.host,Fl(Ml),e);return JSON.parse(t)}function mh(e,t){let r=[],n=new Set(t.firewallPorts),s=e.firewallPorts.filter((a)=>!n.has(a)),i=t.firewallPorts.filter((a)=>!e.firewallPorts.includes(a));if(!t.firewallActive)r.push("UFW is not active");if(s.length>0)r.push(`UFW is missing ports: ${s.join(", ")}`);if(i.length>0)r.push(`UFW has unexpected ports: ${i.join(", ")}`);if(t.passwordAuthentication)r.push("SSH password authentication remains enabled");if(t.rootPasswordLogin)r.push("SSH root password login remains enabled");if(!t.fail2banActive)r.push("fail2ban is not active");if(e.autoUpdates&&!t.unattendedUpgradesActive)r.push("unattended upgrades are not active");if(e.monitoring&&!t.metricsTimerActive)r.push("metrics timer is not active");if(e.monitoring&&!t.metricsSnapshotFresh)r.push("metrics snapshot is stale");if(e.swapGb>0&&t.swapBytes<e.swapGb*1073741824*0.9)r.push("configured swap is missing");if(t.failedUnits.length>0)r.push(`failed services: ${t.failedUnits.join(", ")}`);return r}import{mkdir as dr,readFile as Bl,rename as Eo,rmdir as zl,stat as jl,unlink as Gl,writeFile as Ro}from"node:fs/promises";import{join as mr}from"node:path";function kc(e){return mr(ct(),`${e}-resize.json`)}function Qg(e){return mr(ct(),`${e}-resize.lock`)}async function fh(e){try{return JSON.parse(await Bl(kc(e),"utf8"))}catch{return null}}async function hh(e){let t=kc(e.stackName);await dr(ct(),{recursive:!0});let r=`${t}.${process.pid}.tmp`;await Ro(r,`${JSON.stringify(e,null,2)}
541
- `,"utf8"),await Eo(r,t)}async function Ah(e,t=1800000){let r=Qg(e);await dr(ct(),{recursive:!0});try{await dr(r)}catch(n){if(Date.now()-(await jl(r)).mtimeMs<=t)throw Error(`Another resize process holds ${r}.`);await Eo(r,`${r}.stale-${Date.now()}`),await dr(r)}return await Ro(mr(r,"owner.json"),`${JSON.stringify({pid:process.pid,acquiredAt:new Date().toISOString()},null,2)}
539
+ `;function Fl(e){return`printf %s ${Buffer.from(e).toString("base64")} | base64 -d | bun -`}async function ph(e,t){let r=Ug(e).join(`
540
+ `),n=`printf %s ${Buffer.from(r).toString("base64")} | base64 -d | bash`;await ge(t.host,n,t)}async function mh(e){let t=await ge(e.host,Fl(Ml),e);return JSON.parse(t)}function fh(e,t){let r=[],n=new Set(t.firewallPorts),s=e.firewallPorts.filter((a)=>!n.has(a)),i=t.firewallPorts.filter((a)=>!e.firewallPorts.includes(a));if(!t.firewallActive)r.push("UFW is not active");if(s.length>0)r.push(`UFW is missing ports: ${s.join(", ")}`);if(i.length>0)r.push(`UFW has unexpected ports: ${i.join(", ")}`);if(t.passwordAuthentication)r.push("SSH password authentication remains enabled");if(t.rootPasswordLogin)r.push("SSH root password login remains enabled");if(!t.fail2banActive)r.push("fail2ban is not active");if(e.autoUpdates&&!t.unattendedUpgradesActive)r.push("unattended upgrades are not active");if(e.monitoring&&!t.metricsTimerActive)r.push("metrics timer is not active");if(e.monitoring&&!t.metricsSnapshotFresh)r.push("metrics snapshot is stale");if(e.swapGb>0&&t.swapBytes<e.swapGb*1073741824*0.9)r.push("configured swap is missing");if(t.failedUnits.length>0)r.push(`failed services: ${t.failedUnits.join(", ")}`);return r}import{mkdir as dr,readFile as Bl,rename as Eo,rmdir as zl,stat as jl,unlink as Gl,writeFile as Ro}from"node:fs/promises";import{join as mr}from"node:path";function kc(e){return mr(ct(),`${e}-resize.json`)}function Qg(e){return mr(ct(),`${e}-resize.lock`)}async function hh(e){try{return JSON.parse(await Bl(kc(e),"utf8"))}catch{return null}}async function Ah(e){let t=kc(e.stackName);await dr(ct(),{recursive:!0});let r=`${t}.${process.pid}.tmp`;await Ro(r,`${JSON.stringify(e,null,2)}
541
+ `,"utf8"),await Eo(r,t)}async function yh(e,t=1800000){let r=Qg(e);await dr(ct(),{recursive:!0});try{await dr(r)}catch(n){if(Date.now()-(await jl(r)).mtimeMs<=t)throw Error(`Another resize process holds ${r}.`);await Eo(r,`${r}.stale-${Date.now()}`),await dr(r)}return await Ro(mr(r,"owner.json"),`${JSON.stringify({pid:process.pid,acquiredAt:new Date().toISOString()},null,2)}
542
542
  `,"utf8"),async()=>{await Gl(mr(r,"owner.json")).catch(()=>{}),await zl(r).catch(()=>{})}}async function ci(e,t){let r=Qe(t.publicKey),n=(await e.listSshKeys()).find((i)=>Qe(i.public_key)===r);if(n)return{id:n.id,name:n.name,created:!1};let s=await e.createSshKey({name:t.name,publicKey:t.publicKey.trim(),labels:t.labels});return{id:s.id,name:s.name,created:!0}}async function di(e,t){let r=(await e.listFirewalls()).find((s)=>s.name===t.name);if(r)return await e.setFirewallRules(r.id,t.rules),{id:r.id,name:r.name,created:!1};let{firewall:n}=await e.createFirewall({name:t.name,rules:t.rules,labels:t.labels});return{id:n.id,name:n.name,created:!0}}async function li(e,t){let{waitForRunning:r=!0,...n}=t,s=(await e.listServers()).find((a)=>a.name===t.name),i=!s;if(!s)s=(await e.createServer(n)).server;if(r)s=await e.waitForServerRunning(s.id);return{server:s,created:i}}function ui(e){let t=e.public_net.ipv4?.ip;if(!t)throw Error(`Hetzner server "${e.name}" (#${e.id}) has no public IPv4`);return t}var Bc="/aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id";function xc(e){let t=e.sshPublicKey.trim(),r=["#cloud-config","disable_root: false","ssh_authorized_keys:",` - ${t}`];if(e.bootstrapScript){let s=e.bootstrapScript.split(`
543
543
  `).map((i)=>` ${i}`).join(`
544
544
  `);r.push("write_files:"," - path: /var/lib/cloud/box-bootstrap.sh"," permissions: '0755'"," owner: root:root"," content: |",s)}if(r.push("runcmd:",` - mkdir -p /root/.ssh && grep -qF "${t}" /root/.ssh/authorized_keys 2>/dev/null || echo "${t}" >> /root/.ssh/authorized_keys`),e.bootstrapScript)r.push(" - [ bash, /var/lib/cloud/box-bootstrap.sh ]");return`${r.join(`
545
545
  `)}
546
- `}function Kl(e=[]){let t=[{direction:"in",protocol:"tcp",port:"22",source_ips:["0.0.0.0/0","::/0"],description:"SSH"}];for(let r of e)t.push({direction:"in",protocol:r.protocol,...r.protocol==="icmp"?{}:{port:String(r.port)},source_ips:["0.0.0.0/0","::/0"]});return t}class Oc{client;provider="hetzner";constructor(e){this.client=e}async ensureBox(e){let t=await ci(this.client,{name:`${e.name}-key`,publicKey:e.sshPublicKey}),r=await di(this.client,{name:`${e.name}-fw`,rules:Kl(e.ports)}),{server:n,created:s}=await li(this.client,{name:e.name,serverType:e.size,image:e.image??"ubuntu-24.04",location:e.location,sshKeys:[t.id],firewalls:[{firewall:r.id}],userData:xc(e),labels:e.labels});return{provider:"hetzner",id:String(n.id),name:n.name,publicIp:ui(n),created:s}}async destroyBox(e){let t=[],r=(await this.client.listServers()).find((s)=>s.name===e);if(r){let s=await this.client.deleteServer(r.id);await this.client.waitForAction(s.id).catch(()=>{}),t.push(`server ${e} (#${r.id})`)}let n=(await this.client.listFirewalls()).find((s)=>s.name===`${e}-fw`);if(n)for(let s=0;s<10;s++)try{await this.client.deleteFirewall(n.id),t.push(`firewall ${e}-fw (#${n.id})`);break}catch{if(s===9)break;await new Promise((i)=>setTimeout(i,3000))}return{destroyed:t}}}function Vl(e=[]){let t=[{IpProtocol:"tcp",FromPort:22,ToPort:22,IpRanges:[{CidrIp:"0.0.0.0/0",Description:"SSH"}]}];for(let r of e)t.push(r.protocol==="icmp"?{IpProtocol:"icmp",FromPort:-1,ToPort:-1,IpRanges:[{CidrIp:"0.0.0.0/0"}]}:{IpProtocol:r.protocol,FromPort:r.port,ToPort:r.port,IpRanges:[{CidrIp:"0.0.0.0/0"}]});return t}class _c{provider="aws";ec2;ssm;constructor(e){this.ec2=e.ec2,this.ssm=e.ssm}async findInstance(e,t){return(await this.ec2.describeInstances({Filters:[{Name:"tag:Name",Values:[e]},{Name:"instance-state-name",Values:t}]})).Reservations?.flatMap((n)=>n.Instances??[])[0]}async waitForPublicIp(e,t=300000){let r=Date.now();while(Date.now()-r<t){let n=await this.ec2.getInstance(e);if(n?.State?.Name==="running"&&n.PublicIpAddress)return n;await new Promise((s)=>setTimeout(s,5000))}throw Error(`EC2 instance ${e} did not reach running with a public IP within ${t}ms`)}async ensureSecurityGroup(e){let t=`${e.name}-sg`,n=(await this.ec2.describeSecurityGroups({Filters:[{Name:"group-name",Values:[t]}]})).SecurityGroups?.[0]?.GroupId;if(n)return n;let s=await this.ec2.createSecurityGroup({GroupName:t,Description:`box ${e.name} (managed by ts-cloud box-provision)`});if(!s.GroupId)throw Error(`could not create security group ${t}`);return await this.ec2.authorizeSecurityGroupIngress({GroupId:s.GroupId,IpPermissions:Vl(e.ports)}),s.GroupId}async resolveImage(e){if(e.image?.startsWith("ami-"))return e.image;let r=(await this.ssm.getParameter({Name:Bc})).Parameter?.Value;if(!r)throw Error(`could not resolve the Ubuntu 24.04 AMI via SSM (${Bc})`);return r}async ensureBox(e){let t=await this.findInstance(e.name,["pending","running"]);if(t?.InstanceId){let l=await this.waitForPublicIp(t.InstanceId);return{provider:"aws",id:l.InstanceId,name:e.name,publicIp:l.PublicIpAddress,created:!1}}let[r,n]=await Promise.all([this.ensureSecurityGroup(e),this.resolveImage(e)]),s=[{Key:"Name",Value:e.name},...Object.entries(e.labels??{}).map(([l,m])=>({Key:l,Value:m}))],a=(await this.ec2.runInstances({ImageId:n,InstanceType:e.size,MinCount:1,MaxCount:1,SecurityGroupIds:[r],UserData:Buffer.from(xc(e),"utf8").toString("base64"),TagSpecifications:[{ResourceType:"instance",Tags:s}]})).Instances?.[0]?.InstanceId;if(!a)throw Error(`RunInstances returned no instance for box ${e.name}`);let c=await this.waitForPublicIp(a);return{provider:"aws",id:a,name:e.name,publicIp:c.PublicIpAddress,created:!0}}async destroyBox(e){let t=[],r=await this.findInstance(e,["pending","running","stopping","stopped"]);if(r?.InstanceId){await this.ec2.terminateInstances([r.InstanceId]),t.push(`instance ${e} (${r.InstanceId})`);let i=Date.now();while(Date.now()-i<300000){let a=await this.ec2.getInstance(r.InstanceId);if(!a||a.State?.Name==="terminated")break;await new Promise((c)=>setTimeout(c,5000))}}let s=(await this.ec2.describeSecurityGroups({Filters:[{Name:"group-name",Values:[`${e}-sg`]}]})).SecurityGroups?.[0]?.GroupId;if(s)for(let i=0;i<10;i++)try{await this.ec2.deleteSecurityGroup(s),t.push(`security group ${e}-sg (${s})`);break}catch{if(i===9)break;await new Promise((a)=>setTimeout(a,3000))}return{destroyed:t}}}function yh(e){if(e.provider==="hetzner"){if(!e.hetzner)throw Error('createBoxProvisioner: options.hetzner (a HetznerClient) is required for provider "hetzner"');return new Oc(e.hetzner)}if(!e.aws)throw Error('createBoxProvisioner: options.aws ({ ec2, ssm }) is required for provider "aws"');return new _c(e.aws)}import{copyFileSync as Ru}from"node:fs";import{tmpdir as xu}from"node:os";import{join as Ou}from"node:path";var Wl=`${K}/project`;function xo(e,t){let r=t.ownerSlug.trim(),n=t.selfSlug?.trim(),s={total:e.length,owner:[],self:[],others:new Map,unmanaged:[]};for(let i of e){let a=i.labels?.[Wl]?.trim();if(!a){s.unmanaged.push(i.name);continue}if(a===r){s.owner.push(i.name);continue}if(n&&a===n){s.self.push(i.name);continue}let c=s.others.get(a);if(c)c.push(i.name);else s.others.set(a,[i.name])}return s}function Pn(e){let t=e.unmanaged.length;for(let r of e.others.values())t+=r.length;return t}function Oo(e,t){let r=Pn(e),n=[];if(n.push(`Attaching to '${t.ownerSlug}' shares one provider project, so this deploy's credential can write to all ${e.total} server(s) it can see.`),r===0)return n.push("Nothing outside the two projects being joined is reachable with it."),n;n.push(`${r} of them belong to neither project:`);for(let s of[...e.others.keys()].sort())n.push(` ${s}: ${[...e.others.get(s)].sort().join(", ")}`);if(e.unmanaged.length>0)n.push(` not managed by ts-cloud: ${[...e.unmanaged].sort().join(", ")}`);return n.push("A compromised CI run or a mistargeted teardown in this project now reaches those. Keep the app in its own provider project instead if that is not acceptable, which rules out attaching."),n}import{execSync as Io}from"node:child_process";import{randomBytes as Yl}from"node:crypto";import{chmodSync as Xl,copyFileSync as Jl,existsSync as qe,mkdirSync as Ao,readFileSync as Lo,statSync as Ql,writeFileSync as Rn}from"node:fs";import{tmpdir as Zl}from"node:os";import{basename as eu,dirname as On,isAbsolute as Uo,join as J,resolve as tu}from"node:path";import{fileURLToPath as Ho}from"node:url";function En(e){if(!e||typeof e!=="object")return e;return Object.fromEntries(Object.keys(e).map((t)=>[t,""]))}function ql(e){let t=JSON.parse(JSON.stringify(e??{}));if(t.hetzner)delete t.hetzner.apiToken;delete t.aws,delete t.stateDir;for(let r of Object.values(t.sites??{})){if(r?.env)r.env=En(r.env);if(r?.environment)r.environment=En(r.environment)}for(let r of Object.values(t.environments??{}))if(r?.variables)r.variables=En(r.variables);for(let r of Object.values(t.infrastructure?.databases??{}))if(r?.password)r.password="";if(t.infrastructure?.appDatabase?.password)t.infrastructure.appDatabase.password="";if(t.infrastructure?.compute?.database?.password)t.infrastructure.compute.database.password="";if(t.infrastructure?.compute?.managedServices?.vitess?.password)t.infrastructure.compute.managedServices.vitess.password="";return t}function Do(e){let t=ql(e);return["// Generated by ts-cloud for the management dashboard — do not edit.","//","// This is the project's resolved cloud config, inlined so it needs no","// imports on the box. Credentials are deliberately absent.",`export default ${JSON.stringify(t,null,2)}`,""].join(`
546
+ `}function Kl(e=[]){let t=[{direction:"in",protocol:"tcp",port:"22",source_ips:["0.0.0.0/0","::/0"],description:"SSH"}];for(let r of e)t.push({direction:"in",protocol:r.protocol,...r.protocol==="icmp"?{}:{port:String(r.port)},source_ips:["0.0.0.0/0","::/0"]});return t}class Oc{client;provider="hetzner";constructor(e){this.client=e}async ensureBox(e){let t=await ci(this.client,{name:`${e.name}-key`,publicKey:e.sshPublicKey}),r=await di(this.client,{name:`${e.name}-fw`,rules:Kl(e.ports)}),{server:n,created:s}=await li(this.client,{name:e.name,serverType:e.size,image:e.image??"ubuntu-24.04",location:e.location,sshKeys:[t.id],firewalls:[{firewall:r.id}],userData:xc(e),labels:e.labels});return{provider:"hetzner",id:String(n.id),name:n.name,publicIp:ui(n),created:s}}async destroyBox(e){let t=[],r=(await this.client.listServers()).find((s)=>s.name===e);if(r){let s=await this.client.deleteServer(r.id);await this.client.waitForAction(s.id).catch(()=>{}),t.push(`server ${e} (#${r.id})`)}let n=(await this.client.listFirewalls()).find((s)=>s.name===`${e}-fw`);if(n)for(let s=0;s<10;s++)try{await this.client.deleteFirewall(n.id),t.push(`firewall ${e}-fw (#${n.id})`);break}catch{if(s===9)break;await new Promise((i)=>setTimeout(i,3000))}return{destroyed:t}}}function Vl(e=[]){let t=[{IpProtocol:"tcp",FromPort:22,ToPort:22,IpRanges:[{CidrIp:"0.0.0.0/0",Description:"SSH"}]}];for(let r of e)t.push(r.protocol==="icmp"?{IpProtocol:"icmp",FromPort:-1,ToPort:-1,IpRanges:[{CidrIp:"0.0.0.0/0"}]}:{IpProtocol:r.protocol,FromPort:r.port,ToPort:r.port,IpRanges:[{CidrIp:"0.0.0.0/0"}]});return t}class _c{provider="aws";ec2;ssm;constructor(e){this.ec2=e.ec2,this.ssm=e.ssm}async findInstance(e,t){return(await this.ec2.describeInstances({Filters:[{Name:"tag:Name",Values:[e]},{Name:"instance-state-name",Values:t}]})).Reservations?.flatMap((n)=>n.Instances??[])[0]}async waitForPublicIp(e,t=300000){let r=Date.now();while(Date.now()-r<t){let n=await this.ec2.getInstance(e);if(n?.State?.Name==="running"&&n.PublicIpAddress)return n;await new Promise((s)=>setTimeout(s,5000))}throw Error(`EC2 instance ${e} did not reach running with a public IP within ${t}ms`)}async ensureSecurityGroup(e){let t=`${e.name}-sg`,n=(await this.ec2.describeSecurityGroups({Filters:[{Name:"group-name",Values:[t]}]})).SecurityGroups?.[0]?.GroupId;if(n)return n;let s=await this.ec2.createSecurityGroup({GroupName:t,Description:`box ${e.name} (managed by ts-cloud box-provision)`});if(!s.GroupId)throw Error(`could not create security group ${t}`);return await this.ec2.authorizeSecurityGroupIngress({GroupId:s.GroupId,IpPermissions:Vl(e.ports)}),s.GroupId}async resolveImage(e){if(e.image?.startsWith("ami-"))return e.image;let r=(await this.ssm.getParameter({Name:Bc})).Parameter?.Value;if(!r)throw Error(`could not resolve the Ubuntu 24.04 AMI via SSM (${Bc})`);return r}async ensureBox(e){let t=await this.findInstance(e.name,["pending","running"]);if(t?.InstanceId){let l=await this.waitForPublicIp(t.InstanceId);return{provider:"aws",id:l.InstanceId,name:e.name,publicIp:l.PublicIpAddress,created:!1}}let[r,n]=await Promise.all([this.ensureSecurityGroup(e),this.resolveImage(e)]),s=[{Key:"Name",Value:e.name},...Object.entries(e.labels??{}).map(([l,m])=>({Key:l,Value:m}))],a=(await this.ec2.runInstances({ImageId:n,InstanceType:e.size,MinCount:1,MaxCount:1,SecurityGroupIds:[r],UserData:Buffer.from(xc(e),"utf8").toString("base64"),TagSpecifications:[{ResourceType:"instance",Tags:s}]})).Instances?.[0]?.InstanceId;if(!a)throw Error(`RunInstances returned no instance for box ${e.name}`);let c=await this.waitForPublicIp(a);return{provider:"aws",id:a,name:e.name,publicIp:c.PublicIpAddress,created:!0}}async destroyBox(e){let t=[],r=await this.findInstance(e,["pending","running","stopping","stopped"]);if(r?.InstanceId){await this.ec2.terminateInstances([r.InstanceId]),t.push(`instance ${e} (${r.InstanceId})`);let i=Date.now();while(Date.now()-i<300000){let a=await this.ec2.getInstance(r.InstanceId);if(!a||a.State?.Name==="terminated")break;await new Promise((c)=>setTimeout(c,5000))}}let s=(await this.ec2.describeSecurityGroups({Filters:[{Name:"group-name",Values:[`${e}-sg`]}]})).SecurityGroups?.[0]?.GroupId;if(s)for(let i=0;i<10;i++)try{await this.ec2.deleteSecurityGroup(s),t.push(`security group ${e}-sg (${s})`);break}catch{if(i===9)break;await new Promise((a)=>setTimeout(a,3000))}return{destroyed:t}}}function Sh(e){if(e.provider==="hetzner"){if(!e.hetzner)throw Error('createBoxProvisioner: options.hetzner (a HetznerClient) is required for provider "hetzner"');return new Oc(e.hetzner)}if(!e.aws)throw Error('createBoxProvisioner: options.aws ({ ec2, ssm }) is required for provider "aws"');return new _c(e.aws)}import{copyFileSync as Ru}from"node:fs";import{tmpdir as xu}from"node:os";import{join as Ou}from"node:path";var Wl=`${K}/project`;function xo(e,t){let r=t.ownerSlug.trim(),n=t.selfSlug?.trim(),s={total:e.length,owner:[],self:[],others:new Map,unmanaged:[]};for(let i of e){let a=i.labels?.[Wl]?.trim();if(!a){s.unmanaged.push(i.name);continue}if(a===r){s.owner.push(i.name);continue}if(n&&a===n){s.self.push(i.name);continue}let c=s.others.get(a);if(c)c.push(i.name);else s.others.set(a,[i.name])}return s}function Pn(e){let t=e.unmanaged.length;for(let r of e.others.values())t+=r.length;return t}function Oo(e,t){let r=Pn(e),n=[];if(n.push(`Attaching to '${t.ownerSlug}' shares one provider project, so this deploy's credential can write to all ${e.total} server(s) it can see.`),r===0)return n.push("Nothing outside the two projects being joined is reachable with it."),n;n.push(`${r} of them belong to neither project:`);for(let s of[...e.others.keys()].sort())n.push(` ${s}: ${[...e.others.get(s)].sort().join(", ")}`);if(e.unmanaged.length>0)n.push(` not managed by ts-cloud: ${[...e.unmanaged].sort().join(", ")}`);return n.push("A compromised CI run or a mistargeted teardown in this project now reaches those. Keep the app in its own provider project instead if that is not acceptable, which rules out attaching."),n}import{execSync as Io}from"node:child_process";import{randomBytes as Yl}from"node:crypto";import{chmodSync as Xl,copyFileSync as Jl,existsSync as qe,mkdirSync as Ao,readFileSync as Lo,statSync as Ql,writeFileSync as Rn}from"node:fs";import{tmpdir as Zl}from"node:os";import{basename as eu,dirname as On,isAbsolute as Uo,join as J,resolve as tu}from"node:path";import{fileURLToPath as Ho}from"node:url";function En(e){if(!e||typeof e!=="object")return e;return Object.fromEntries(Object.keys(e).map((t)=>[t,""]))}function ql(e){let t=JSON.parse(JSON.stringify(e??{}));if(t.hetzner)delete t.hetzner.apiToken;delete t.aws,delete t.stateDir;for(let r of Object.values(t.sites??{})){if(r?.env)r.env=En(r.env);if(r?.environment)r.environment=En(r.environment)}for(let r of Object.values(t.environments??{}))if(r?.variables)r.variables=En(r.variables);for(let r of Object.values(t.infrastructure?.databases??{}))if(r?.password)r.password="";if(t.infrastructure?.appDatabase?.password)t.infrastructure.appDatabase.password="";if(t.infrastructure?.compute?.database?.password)t.infrastructure.compute.database.password="";if(t.infrastructure?.compute?.managedServices?.vitess?.password)t.infrastructure.compute.managedServices.vitess.password="";return t}function Do(e){let t=ql(e);return["// Generated by ts-cloud for the management dashboard — do not edit.","//","// This is the project's resolved cloud config, inlined so it needs no","// imports on the box. Credentials are deliberately absent.",`export default ${JSON.stringify(t,null,2)}`,""].join(`
547
547
  `)}var Lc="dashboard";function Gs(){return L("dashboard-credentials.json")}function ru(){return Yl(24).toString("base64url")}function $c(e,t,r){let n=process.env.TS_CLOUD_UI_PASSWORD?.trim();if(n)return{password:n,source:"env"};if(xn(process.env.TS_CLOUD_UI_PUBLIC))return{password:void 0,source:"public"};let s=Gs(),i=d(e,"dashboard-credentials.json");try{if(qe(i)){let c=JSON.parse(Lo(i,"utf8"));if(c?.password)return{password:c.password,source:"generated"}}}catch{}let a=ru();try{Ao(On(i),{recursive:!0}),Rn(i,`${JSON.stringify({username:t,password:a,generatedAt:new Date().toISOString()},null,2)}
548
548
  `),Xl(i,384),r.info(`Management dashboard: generated a password for '${t}' and saved it to ${s} (read it there — it is not printed). Set TS_CLOUD_UI_PASSWORD to pin your own, or TS_CLOUD_UI_PUBLIC=1 to serve without auth.`)}catch(c){r.warn(`Management dashboard: could not persist the generated password (${c?.message??c}). Using it for this deploy only — pass: ${a}
549
549
  This password is now in your deploy log. Set TS_CLOUD_UI_PASSWORD to a value of your own and redeploy once ${s} is writable.`)}return{password:a,source:"generated"}}var No={info:()=>{},warn:()=>{}};function xn(e){return e!=null&&e!==""&&e!=="0"&&e.toLowerCase()!=="false"}function nu(){return L("dashboard-release")}function ko(){let e=On(Ho(import.meta.url));for(let t of[J(e,"..","..","package.json"),J(e,"..","package.json")])try{let r=JSON.parse(Lo(t,"utf8"));if(r.name==="@stacksjs/ts-cloud"&&r.version)return`^${r.version}`}catch{}return"latest"}function su(){return process.env.TS_CLOUD_UI_VERSION?.trim()||ko()}function ou(e,t,r){if(!e?.project?.slug)return r.warn("Management dashboard: the cloud config has no project slug — skipping the live dashboard."),null;let n=d(t,"dashboard-release");try{Ao(n,{recursive:!0}),Rn(J(n,"cloud.config.ts"),Do(e));let s=su();if(s.startsWith("file:")){let a=s.slice(5),c=Uo(a)?a:tu(t,a);if(!qe(c)||!Ql(c).isFile())s=ko(),r.warn(`Management dashboard: TS_CLOUD_UI_VERSION points to a missing package file (${c}); using the deploying ts-cloud version ${s} instead.`);else{let l=eu(c),m=J(n,l);if(c!==m)Jl(c,m);s=`file:./${l}`}}let i={name:"ts-cloud-dashboard",private:!0,type:"module",dependencies:{"@stacksjs/ts-cloud":s}};return Rn(J(n,"package.json"),`${JSON.stringify(i,null,2)}
@@ -581,4 +581,4 @@ This password is now in your deploy log. Set TS_CLOUD_UI_PASSWORD to a value of
581
581
  changed = true
582
582
  }
583
583
  if (changed) writeFileSync('/run/ts-cloud-dashboard-routes-changed', '')
584
- `)}`,"if [ -e /run/ts-cloud-dashboard-routes-changed ]; then"," systemctl restart rpx-gateway.service"," rm -f /run/ts-cloud-dashboard-routes-changed","fi"]:[];return["set -euo pipefail",`TS_CLOUD_DASHBOARD_PREFIX=${$r(s)}`,`TS_CLOUD_DASHBOARD_SERVER_OWNER=${r?"1":"0"}`,`TS_CLOUD_DASHBOARD_DESIRED=${$r(` ${i.join(" ")} `)}`,"for TS_CLOUD_UNIT_FILE in /etc/systemd/system/*.service; do",' [ -e "$TS_CLOUD_UNIT_FILE" ] || continue',' TS_CLOUD_UNIT=$(basename "$TS_CLOUD_UNIT_FILE")',' case "$TS_CLOUD_UNIT" in'," *-dashboard-*.service)",' if [ "$TS_CLOUD_DASHBOARD_SERVER_OWNER" != 1 ]; then',' case "$TS_CLOUD_UNIT" in "$TS_CLOUD_DASHBOARD_PREFIX"*.service) ;; *) continue ;; esac'," fi",' case "$TS_CLOUD_DASHBOARD_DESIRED" in',' *" $TS_CLOUD_UNIT "*) ;;'," *)",' systemctl disable --now "$TS_CLOUD_UNIT" 2>/dev/null || true',' rm -f "$TS_CLOUD_UNIT_FILE"'," ;;"," esac"," ;;"," esac","done","systemctl daemon-reload","systemctl reset-failed",...a]}async function Au(e,t,r,n){let{config:s,environment:i}=t,a=s.project.slug,c=p(s,i),l=await e.findComputeTargets({slug:a,environment:i,role:"app",stackName:c});if(l.length===0)return!0;let m=await e.runRemoteDeploy({targets:l,commands:Iu(a,r,!s.cloud?.attachTo,r.map((S)=>s.sites?.[S]?.domain).filter((S)=>typeof S==="string")),comment:`ts-cloud reconcile management dashboard ${a}`,tags:{Project:a,Environment:i,Role:"app"}});if(!m.success)return n.error(`Management dashboard reconciliation failed: ${m.error||"unknown error"}`),!1;return!0}async function Lu(e,t,r){let{config:n}=t,s=n.cloud?.attachTo;if(!s||!e.listReachableResources)return;let i;try{i=await e.listReachableResources()}catch(l){r.warn(`Could not determine this credential's reach: ${l instanceof Error?l.message:String(l)}`);return}let a=xo(i,{ownerSlug:s,selfSlug:n.project.slug}),c=Pn(a)>0;for(let l of Oo(a,{ownerSlug:s,selfSlug:n.project.slug}))if(c)r.warn(l);else r.info(l)}async function Uu(e,t,r){let{config:n,environment:s}=t,i=n.cloud?.attachTo;if(!i)return!0;let a=Zo(n.infrastructure?.compute?.managedServices);if(a.length===0)return!0;let c=n.project.slug,l=p(n,s),m=await e.findComputeTargets({slug:c,environment:s,role:"app",stackName:l});if(m.length===0)return!0;let S=await e.runRemoteDeploy({targets:m,commands:mi(a),comment:`ts-cloud preflight managed services ${c}`,tags:{Project:c,Environment:s,Role:"app"}});if(!S.success)return r.warn(`Could not check '${i}' for the declared services: ${S.error||"unknown error"}`),!0;let b=new Set;for(let y of S.perInstance)for(let f of hi(y.output,a))b.add(f);if(b.size===0)return!0;return r.error(Si([...b],i,m[0]?.publicIp)),!1}async function Hu(e,t,r){let{config:n,environment:s}=t;if(!n.cloud?.attachTo)return!0;let i=n.infrastructure?.compute,a=g(n),c=Je(a,i?.managedServices??{});if(c.length===0)return!0;let l=n.project.slug,m=p(n,s),S=await e.findComputeTargets({slug:l,environment:s,role:"app",stackName:m});if(S.length===0)return!0;r.step(`Ensuring database '${a?.name}' + role exist on the shared box...`);let b=await e.runRemoteDeploy({targets:S,commands:c,comment:`ts-cloud ensure database ${l}/${a?.name}`,tags:{Project:l,Environment:s,Role:"app"}});if(!b.success)return r.error(`Ensuring database '${a?.name}' failed: ${b.error||"unknown error"}`),!1;return r.success(`Database '${a?.name}' is ready on ${b.instanceCount} target(s)`),!0}async function Mc(e){let{config:t,environment:r,driver:n,sha:s,runtime:i,tarballForSite:a,logger:c=Or}=e,l=t.project.slug,m=e.cwd??process.cwd(),S=e.managementDashboard!==!1,b=Xt(t);if(S)gi(t,{cwd:m,logger:{info:c.info,warn:c.warn}});let y=!S||b?[]:Dn(t);if(S&&e.rpxConfig&&e.rpxConfig!==t&&!Xt(e.rpxConfig))gi(e.rpxConfig,{cwd:m,logger:{info:()=>{},warn:()=>{}}});if(S&&!await Au(n,e,Dn(t),c))return!1;let f=null,T=new Map;if(y.length>0)if(f=Vs(t.sites?.[y[0]],{cwd:m,slug:l,sha:s,siteName:y[0],logger:{info:c.info,warn:c.warn}}),!f){c.warn("Management dashboard: no artifact available — skipping dashboard site(s) for this deploy.");for(let w of y)if(t.sites)delete t.sites[w]}else for(let w of y){if(w===y[0]){T.set(w,f);continue}try{let E=Ou(xu(),`${l}-${w}-${s}.tar.gz`);Ru(f,E),T.set(w,E)}catch{T.set(w,f)}}let D=t.sites||{},R=Object.entries(D).filter(([w,E])=>{if(!E)return!1;let H=u(E);if(H==="bucket")return c.warn(`Site '${w}' targets a bucket — skipping (handled by the static-site path, not compute).`),!1;if(H==="redirect")return!1;if(H==="proxy")return!1;return!0});if(R.length===0)return pi(e);if(await Lu(n,e,c),!await Uu(n,e,c))return!1;if(!await Hu(n,e,c))return!1;let C=(w)=>T.get(w)??a(w);for(let[w,E]of R){c.step(`Deploying site: ${w}`);let H=Br(E)?void 0:C(w),I=await qs(n,{config:t,environment:r,siteName:w,site:E,slug:l,sha:s,runtime:i,localTarballPath:H},c);if(!I.success){if(c.error(`Deploy of '${w}' failed: ${I.error||"unknown error"}`),I.perInstance)for(let A of I.perInstance)c.error(` ${A.instanceId}: ${A.status}${A.error?` — ${A.error}`:""}`);return!1}if(c.success(`Deployed ${l}/${w}@${s} to ${I.instanceCount} target(s)`),I.perInstance)for(let A of I.perInstance)c.info(` ✓ ${A.instanceId}: ${A.status}`)}if(!await pi(e))return!1;return!0}async function Nu(e,t,r){let n=bs(e);if(!n||n.tls==="off")return n;let s=await Ge(t,r),i=s?.provider==="ssh"?s.lanIp:void 0;return i?{...n,ip:i}:n}async function pi(e){let{config:t,environment:r,driver:n,logger:s=Or}=e,i=e.rpxConfig??t,a=i.infrastructure?.compute?.proxy??t.infrastructure?.compute?.proxy;if(a?.engine!=="rpx")return!0;let c=i.sites||{},l=t.project.slug,m=p(t,r),S=await n.findComputeTargets({slug:l,environment:r,role:"lb",stackName:m});if(S.length>0){let P=(await n.findComputeTargets({slug:l,environment:r,role:"app",stackName:m})).map((H)=>({privateIp:H.privateIp,publicIp:H.publicIp})),w=Ir(c,P,{proxy:a,slug:l});if(w.proxies.length===0)return s.warn("rpx gateway: no server sites with a domain to route — skipping gateway reload."),!0;s.step(`Reloading the load balancer's rpx gateway with ${w.proxies.length} route(s)...`);let E=await n.runRemoteDeploy({targets:S,commands:Ue({config:w,slug:l,preserveManagementDashboardRoutes:e.managementDashboard===!1}),comment:`ts-cloud rpx gateway reload ${l}`,tags:{Project:l,Environment:r,Role:"lb"}});if(!E.success)return s.error(`rpx gateway reload failed: ${E.error||"unknown error"}`),!1;return s.success(`rpx gateway reloaded on ${E.instanceCount} load balancer(s)`),!0}let b=await Nu(t,m,e.cwd),y=X(c,{proxy:a,slug:l,lan:b});if(y.proxies.length===0)return s.warn("rpx gateway: no server sites with a domain to route — skipping gateway reload."),!0;let f=await n.findComputeTargets({slug:l,environment:r,role:"app",stackName:m});if(f.length===0)return s.warn("rpx gateway: no compute targets found — skipping gateway reload."),!0;let T=Boolean(t.cloud?.attachTo);if(T&&(a.version||a.tlsxVersion))s.warn(`rpx gateway: ignoring proxy.version/tlsxVersion — this project attaches to '${t.cloud?.attachTo}' and the gateway belongs to that box's owner.`);s.step(`Reloading rpx gateway with ${y.proxies.length} route(s)...`);let D=Te({proxy:a,config:y,slug:l,tenant:T,profile:Ts(t),preserveManagementDashboardRoutes:e.managementDashboard===!1}),R=await n.runRemoteDeploy({targets:f,commands:D,comment:`ts-cloud rpx gateway reload ${l}`,tags:{Project:l,Environment:r,Role:"app"}});if(!R.success)return s.error(`rpx gateway reload failed: ${R.error||"unknown error"}`),!1;return s.success(`rpx gateway reloaded on ${R.instanceCount} target(s)`),!0}async function Fc(e){let{config:t,environment:r,driver:n,logger:s=Or}=e,i=e.rpxConfig??t,a=i.infrastructure?.compute?.proxy??t.infrastructure?.compute?.proxy;if(a?.engine!=="rpx"||!a.onDemandTls)return!0;let c=X(i.sites||{},{proxy:a,slug:t.project.slug});if(c.proxies.length===0)return!0;let l=nn(c);if(l.length===0)return!0;let m=t.project.slug,S=p(t,r),b="lb",y=await n.findComputeTargets({slug:m,environment:r,role:b,stackName:S});if(y.length===0)b="app",y=await n.findComputeTargets({slug:m,environment:r,role:b,stackName:S});if(y.length===0)return s.warn("rpx TLS: no gateway targets found - skipping certificate issuance."),!0;s.step("Issuing rpx TLS certificates after DNS reconciliation...");let f=l.map((D)=>`test -s ${Du(`${c.productionCerts.certsDir}/${D}.crt`)}`).join(" && "),T=await n.runRemoteDeploy({targets:y,commands:[`systemctl start ${rn(m)}`,f],comment:`ts-cloud rpx TLS issuance ${m}`,tags:{Project:m,Environment:r,Role:b}});if(!T.success)return s.error(`rpx TLS issuance failed: ${T.error||"unknown error"}`),!1;return s.success(`rpx TLS certificates reconciled on ${T.instanceCount} gateway target(s)`),!0}var Hg="658327ea-f89d-4fab-a63d-7e88639e58f6",Gg="4135ea2d-6df8-44a3-9df3-4b5a84be39ad",Vg="216adef6-5c7f-47e4-b989-5492eafa07d3",ts="origin",Ti="origin-failover";function wi(e,t){let r={TargetOriginId:ts,ViewerProtocolPolicy:"redirect-to-https",Compress:!0,CachePolicyId:t==="dynamic"?Gg:Hg,OriginRequestPolicyId:Vg,SmoothStreaming:!1,FieldLevelEncryptionId:"",TrustedSigners:{Enabled:!1,Quantity:0},TrustedKeyGroups:{Enabled:!1,Quantity:0},LambdaFunctionAssociations:{Quantity:0},FunctionAssociations:{Quantity:0},AllowedMethods:t==="dynamic"?{Quantity:7,Items:["GET","HEAD","OPTIONS","PUT","POST","PATCH","DELETE"],CachedMethods:{Quantity:2,Items:["GET","HEAD"]}}:{Quantity:3,Items:["GET","HEAD","OPTIONS"],CachedMethods:{Quantity:2,Items:["GET","HEAD"]}}};if(e!==null)r.PathPattern=e;return r}function Sh(e){let t=e.aliases;if(t.length===0)throw Error("buildCloudFrontOriginConfig: at least one alias is required");if(t.includes(e.originDomain))throw Error(`buildCloudFrontOriginConfig: originDomain ${e.originDomain} must not be one of the aliases (it would loop)`);if(e.originShield&&!e.originShieldRegion)throw Error("buildCloudFrontOriginConfig: originShieldRegion is required when originShield is enabled");let r=e.failoverOriginDomain;if(r!==void 0){if(t.includes(r))throw Error(`buildCloudFrontOriginConfig: failoverOriginDomain ${r} must not be one of the aliases (it would loop)`);if(r===e.originDomain)throw Error("buildCloudFrontOriginConfig: failoverOriginDomain must differ from originDomain")}else if(e.failoverStatusCodes)throw Error("buildCloudFrontOriginConfig: failoverStatusCodes needs failoverOriginDomain to fail over to");let n=e.originSecretHeader??"X-Origin-Verify",s=e.originSecret?{Quantity:1,Items:[{HeaderName:n,HeaderValue:e.originSecret}]}:{Quantity:0},i=[...e.behaviors??[]].sort((y,f)=>f.pathPattern.length-y.pathPattern.length),a=te(e,"buildCloudFrontOriginConfig"),c=(y,f,T,D={})=>({Id:y,DomainName:f,OriginPath:"",CustomHeaders:s,CustomOriginConfig:{HTTPPort:80,HTTPSPort:443,OriginProtocolPolicy:"https-only",OriginSslProtocols:{Quantity:1,Items:["TLSv1.2"]},OriginReadTimeout:30,OriginKeepaliveTimeout:5},ConnectionAttempts:D.ConnectionAttempts??3,ConnectionTimeout:D.ConnectionTimeout??10,OriginShield:T,OriginAccessControlId:""}),l=[c(ts,e.originDomain,e.originShield?{Enabled:!0,OriginShieldRegion:e.originShieldRegion}:{Enabled:!1},a)],m={Quantity:0},S=wi(null,"static");if(r!==void 0)l.push(c(Ti,r,{Enabled:!1})),m=Kr({primaryOriginId:ts,secondaryOriginId:Ti,statusCodes:e.failoverStatusCodes}),yt(S.AllowedMethods,"the default cache behavior"),S.TargetOriginId=m.Items[0].Id;let b={CallerReference:e.callerReference??e.originDomain,Comment:e.comment??`Origin-fronted distribution for ${t[0]} → ${e.originDomain}`,Enabled:!0,Aliases:{Quantity:t.length,Items:t},DefaultRootObject:"",Origins:{Quantity:l.length,Items:l},OriginGroups:m,DefaultCacheBehavior:S,CacheBehaviors:{Quantity:i.length,Items:i.map((y)=>wi(y.pathPattern,y.kind))},ViewerCertificate:{CloudFrontDefaultCertificate:!1,ACMCertificateArn:e.viewerCertificateArn,Certificate:e.viewerCertificateArn,CertificateSource:"acm",SSLSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"},PriceClass:e.priceClass??"PriceClass_All",HttpVersion:"http2and3",IsIPV6Enabled:!0,Restrictions:{GeoRestriction:{RestrictionType:"none",Quantity:0}},Logging:{Enabled:!1,IncludeCookies:!1,Bucket:"",Prefix:""},WebACLId:"",CustomErrorResponses:{Quantity:0}};return St(b,"buildCloudFrontOriginConfig"),b}export{sg,uc,Ee,gc,Ln,og,pc,$n,Lt,Ar,$t,ag,cg,rh,v,yr,$s,Mn,Mt,Sr,dg,Ms,Un,Qn,Hn,lg,mc,X,Ir,nh,Gn,fc,ve,Te,Ue,x,br,Vn,Cr,ge,hc,qn,Ac,jn,yc,Ft,ug,gg,Er,ih,sh,pg,mg,oh,ah,ch,vr,zn,fg,Fs,Kn,Wn,Jn,Xn,Yn,Sc,Ic,bc,Tr,Cc,Qe,Y,Ec,ct,He,Ge,dt,we,Us,vc,hg,Ag,Tc,yg,lt,Zn,wc,Sg,Ig,Rc,wr,Re,Dc,bg,Cg,Qs,ei,ut,Eg,vg,Tg,wg,Rg,Dg,Pg,Ng,kg,ti,ri,ni,ii,si,oi,ai,Bg,xg,Rr,O,Hs,Og,_g,Lg,Pc,$g,Mg,dh,Nc,lh,uh,Fg,Ug,gh,ph,mh,kc,Qg,fh,hh,Ah,ci,di,li,ui,Bc,xc,Oc,_c,yh,Lc,Gs,$c,Ut,gi,Vs,Dr,qs,Mc,pi,Fc,Hg,Gg,Vg,Sh};
584
+ `)}`,"if [ -e /run/ts-cloud-dashboard-routes-changed ]; then"," systemctl restart rpx-gateway.service"," rm -f /run/ts-cloud-dashboard-routes-changed","fi"]:[];return["set -euo pipefail",`TS_CLOUD_DASHBOARD_PREFIX=${$r(s)}`,`TS_CLOUD_DASHBOARD_SERVER_OWNER=${r?"1":"0"}`,`TS_CLOUD_DASHBOARD_DESIRED=${$r(` ${i.join(" ")} `)}`,"for TS_CLOUD_UNIT_FILE in /etc/systemd/system/*.service; do",' [ -e "$TS_CLOUD_UNIT_FILE" ] || continue',' TS_CLOUD_UNIT=$(basename "$TS_CLOUD_UNIT_FILE")',' case "$TS_CLOUD_UNIT" in'," *-dashboard-*.service)",' if [ "$TS_CLOUD_DASHBOARD_SERVER_OWNER" != 1 ]; then',' case "$TS_CLOUD_UNIT" in "$TS_CLOUD_DASHBOARD_PREFIX"*.service) ;; *) continue ;; esac'," fi",' case "$TS_CLOUD_DASHBOARD_DESIRED" in',' *" $TS_CLOUD_UNIT "*) ;;'," *)",' systemctl disable --now "$TS_CLOUD_UNIT" 2>/dev/null || true',' rm -f "$TS_CLOUD_UNIT_FILE"'," ;;"," esac"," ;;"," esac","done","systemctl daemon-reload","systemctl reset-failed",...a]}async function Au(e,t,r,n){let{config:s,environment:i}=t,a=s.project.slug,c=p(s,i),l=await e.findComputeTargets({slug:a,environment:i,role:"app",stackName:c});if(l.length===0)return!0;let m=await e.runRemoteDeploy({targets:l,commands:Iu(a,r,!s.cloud?.attachTo,r.map((S)=>s.sites?.[S]?.domain).filter((S)=>typeof S==="string")),comment:`ts-cloud reconcile management dashboard ${a}`,tags:{Project:a,Environment:i,Role:"app"}});if(!m.success)return n.error(`Management dashboard reconciliation failed: ${m.error||"unknown error"}`),!1;return!0}async function Lu(e,t,r){let{config:n}=t,s=n.cloud?.attachTo;if(!s||!e.listReachableResources)return;let i;try{i=await e.listReachableResources()}catch(l){r.warn(`Could not determine this credential's reach: ${l instanceof Error?l.message:String(l)}`);return}let a=xo(i,{ownerSlug:s,selfSlug:n.project.slug}),c=Pn(a)>0;for(let l of Oo(a,{ownerSlug:s,selfSlug:n.project.slug}))if(c)r.warn(l);else r.info(l)}async function Uu(e,t,r){let{config:n,environment:s}=t,i=n.cloud?.attachTo;if(!i)return!0;let a=Zo(n.infrastructure?.compute?.managedServices);if(a.length===0)return!0;let c=n.project.slug,l=p(n,s),m=await e.findComputeTargets({slug:c,environment:s,role:"app",stackName:l});if(m.length===0)return!0;let S=await e.runRemoteDeploy({targets:m,commands:mi(a),comment:`ts-cloud preflight managed services ${c}`,tags:{Project:c,Environment:s,Role:"app"}});if(!S.success)return r.warn(`Could not check '${i}' for the declared services: ${S.error||"unknown error"}`),!0;let b=new Set;for(let y of S.perInstance)for(let f of hi(y.output,a))b.add(f);if(b.size===0)return!0;return r.error(Si([...b],i,m[0]?.publicIp)),!1}async function Hu(e,t,r){let{config:n,environment:s}=t;if(!n.cloud?.attachTo)return!0;let i=n.infrastructure?.compute,a=g(n),c=Je(a,i?.managedServices??{});if(c.length===0)return!0;let l=n.project.slug,m=p(n,s),S=await e.findComputeTargets({slug:l,environment:s,role:"app",stackName:m});if(S.length===0)return!0;r.step(`Ensuring database '${a?.name}' + role exist on the shared box...`);let b=await e.runRemoteDeploy({targets:S,commands:c,comment:`ts-cloud ensure database ${l}/${a?.name}`,tags:{Project:l,Environment:s,Role:"app"}});if(!b.success)return r.error(`Ensuring database '${a?.name}' failed: ${b.error||"unknown error"}`),!1;return r.success(`Database '${a?.name}' is ready on ${b.instanceCount} target(s)`),!0}async function Mc(e){let{config:t,environment:r,driver:n,sha:s,runtime:i,tarballForSite:a,logger:c=Or}=e,l=t.project.slug,m=e.cwd??process.cwd(),S=e.managementDashboard!==!1,b=Xt(t);if(S)gi(t,{cwd:m,logger:{info:c.info,warn:c.warn}});let y=!S||b?[]:Dn(t);if(S&&e.rpxConfig&&e.rpxConfig!==t&&!Xt(e.rpxConfig))gi(e.rpxConfig,{cwd:m,logger:{info:()=>{},warn:()=>{}}});if(S&&!await Au(n,e,Dn(t),c))return!1;let f=null,T=new Map;if(y.length>0)if(f=Vs(t.sites?.[y[0]],{cwd:m,slug:l,sha:s,siteName:y[0],logger:{info:c.info,warn:c.warn}}),!f){c.warn("Management dashboard: no artifact available — skipping dashboard site(s) for this deploy.");for(let w of y)if(t.sites)delete t.sites[w]}else for(let w of y){if(w===y[0]){T.set(w,f);continue}try{let E=Ou(xu(),`${l}-${w}-${s}.tar.gz`);Ru(f,E),T.set(w,E)}catch{T.set(w,f)}}let D=t.sites||{},R=Object.entries(D).filter(([w,E])=>{if(!E)return!1;let H=u(E);if(H==="bucket")return c.warn(`Site '${w}' targets a bucket — skipping (handled by the static-site path, not compute).`),!1;if(H==="redirect")return!1;if(H==="proxy")return!1;return!0});if(R.length===0)return pi(e);if(await Lu(n,e,c),!await Uu(n,e,c))return!1;if(!await Hu(n,e,c))return!1;let C=(w)=>T.get(w)??a(w);for(let[w,E]of R){c.step(`Deploying site: ${w}`);let H=Br(E)?void 0:C(w),I=await qs(n,{config:t,environment:r,siteName:w,site:E,slug:l,sha:s,runtime:i,localTarballPath:H},c);if(!I.success){if(c.error(`Deploy of '${w}' failed: ${I.error||"unknown error"}`),I.perInstance)for(let A of I.perInstance)c.error(` ${A.instanceId}: ${A.status}${A.error?` — ${A.error}`:""}`);return!1}if(c.success(`Deployed ${l}/${w}@${s} to ${I.instanceCount} target(s)`),I.perInstance)for(let A of I.perInstance)c.info(` ✓ ${A.instanceId}: ${A.status}`)}if(!await pi(e))return!1;return!0}async function Nu(e,t,r){let n=bs(e);if(!n||n.tls==="off")return n;let s=await Ge(t,r),i=s?.provider==="ssh"?s.lanIp:void 0;return i?{...n,ip:i}:n}async function pi(e){let{config:t,environment:r,driver:n,logger:s=Or}=e,i=e.rpxConfig??t,a=i.infrastructure?.compute?.proxy??t.infrastructure?.compute?.proxy;if(a?.engine!=="rpx")return!0;let c=i.sites||{},l=t.project.slug,m=p(t,r),S=await n.findComputeTargets({slug:l,environment:r,role:"lb",stackName:m});if(S.length>0){let P=(await n.findComputeTargets({slug:l,environment:r,role:"app",stackName:m})).map((H)=>({privateIp:H.privateIp,publicIp:H.publicIp})),w=Ir(c,P,{proxy:a,slug:l});if(w.proxies.length===0)return s.warn("rpx gateway: no server sites with a domain to route — skipping gateway reload."),!0;s.step(`Reloading the load balancer's rpx gateway with ${w.proxies.length} route(s)...`);let E=await n.runRemoteDeploy({targets:S,commands:Ue({config:w,slug:l,preserveManagementDashboardRoutes:e.managementDashboard===!1}),comment:`ts-cloud rpx gateway reload ${l}`,tags:{Project:l,Environment:r,Role:"lb"}});if(!E.success)return s.error(`rpx gateway reload failed: ${E.error||"unknown error"}`),!1;return s.success(`rpx gateway reloaded on ${E.instanceCount} load balancer(s)`),!0}let b=await Nu(t,m,e.cwd),y=X(c,{proxy:a,slug:l,lan:b});if(y.proxies.length===0)return s.warn("rpx gateway: no server sites with a domain to route — skipping gateway reload."),!0;let f=await n.findComputeTargets({slug:l,environment:r,role:"app",stackName:m});if(f.length===0)return s.warn("rpx gateway: no compute targets found — skipping gateway reload."),!0;let T=Boolean(t.cloud?.attachTo);if(T&&(a.version||a.tlsxVersion))s.warn(`rpx gateway: ignoring proxy.version/tlsxVersion — this project attaches to '${t.cloud?.attachTo}' and the gateway belongs to that box's owner.`);s.step(`Reloading rpx gateway with ${y.proxies.length} route(s)...`);let D=Te({proxy:a,config:y,slug:l,tenant:T,profile:Ts(t),preserveManagementDashboardRoutes:e.managementDashboard===!1}),R=await n.runRemoteDeploy({targets:f,commands:D,comment:`ts-cloud rpx gateway reload ${l}`,tags:{Project:l,Environment:r,Role:"app"}});if(!R.success)return s.error(`rpx gateway reload failed: ${R.error||"unknown error"}`),!1;return s.success(`rpx gateway reloaded on ${R.instanceCount} target(s)`),!0}async function Fc(e){let{config:t,environment:r,driver:n,logger:s=Or}=e,i=e.rpxConfig??t,a=i.infrastructure?.compute?.proxy??t.infrastructure?.compute?.proxy;if(a?.engine!=="rpx"||!a.onDemandTls)return!0;let c=X(i.sites||{},{proxy:a,slug:t.project.slug});if(c.proxies.length===0)return!0;let l=nn(c);if(l.length===0)return!0;let m=t.project.slug,S=p(t,r),b="lb",y=await n.findComputeTargets({slug:m,environment:r,role:b,stackName:S});if(y.length===0)b="app",y=await n.findComputeTargets({slug:m,environment:r,role:b,stackName:S});if(y.length===0)return s.warn("rpx TLS: no gateway targets found - skipping certificate issuance."),!0;s.step("Issuing rpx TLS certificates after DNS reconciliation...");let f=l.map((D)=>`test -s ${Du(`${c.productionCerts.certsDir}/${D}.crt`)}`).join(" && "),T=await n.runRemoteDeploy({targets:y,commands:[`systemctl start ${rn(m)}`,f],comment:`ts-cloud rpx TLS issuance ${m}`,tags:{Project:m,Environment:r,Role:b}});if(!T.success)return s.error(`rpx TLS issuance failed: ${T.error||"unknown error"}`),!1;return s.success(`rpx TLS certificates reconciled on ${T.instanceCount} gateway target(s)`),!0}var Hg="658327ea-f89d-4fab-a63d-7e88639e58f6",Gg="4135ea2d-6df8-44a3-9df3-4b5a84be39ad",Vg="216adef6-5c7f-47e4-b989-5492eafa07d3",ts="origin",Ti="origin-failover";function wi(e,t){let r={TargetOriginId:ts,ViewerProtocolPolicy:"redirect-to-https",Compress:!0,CachePolicyId:t==="dynamic"?Gg:Hg,OriginRequestPolicyId:Vg,SmoothStreaming:!1,FieldLevelEncryptionId:"",TrustedSigners:{Enabled:!1,Quantity:0},TrustedKeyGroups:{Enabled:!1,Quantity:0},LambdaFunctionAssociations:{Quantity:0},FunctionAssociations:{Quantity:0},AllowedMethods:t==="dynamic"?{Quantity:7,Items:["GET","HEAD","OPTIONS","PUT","POST","PATCH","DELETE"],CachedMethods:{Quantity:2,Items:["GET","HEAD"]}}:{Quantity:3,Items:["GET","HEAD","OPTIONS"],CachedMethods:{Quantity:2,Items:["GET","HEAD"]}}};if(e!==null)r.PathPattern=e;return r}function Ih(e){let t=e.aliases;if(t.length===0)throw Error("buildCloudFrontOriginConfig: at least one alias is required");if(t.includes(e.originDomain))throw Error(`buildCloudFrontOriginConfig: originDomain ${e.originDomain} must not be one of the aliases (it would loop)`);if(e.originShield&&!e.originShieldRegion)throw Error("buildCloudFrontOriginConfig: originShieldRegion is required when originShield is enabled");let r=e.failoverOriginDomain;if(r!==void 0){if(t.includes(r))throw Error(`buildCloudFrontOriginConfig: failoverOriginDomain ${r} must not be one of the aliases (it would loop)`);if(r===e.originDomain)throw Error("buildCloudFrontOriginConfig: failoverOriginDomain must differ from originDomain")}else if(e.failoverStatusCodes)throw Error("buildCloudFrontOriginConfig: failoverStatusCodes needs failoverOriginDomain to fail over to");let n=e.originSecretHeader??"X-Origin-Verify",s=e.originSecret?{Quantity:1,Items:[{HeaderName:n,HeaderValue:e.originSecret}]}:{Quantity:0},i=[...e.behaviors??[]].sort((y,f)=>f.pathPattern.length-y.pathPattern.length),a=te(e,"buildCloudFrontOriginConfig"),c=(y,f,T,D={})=>({Id:y,DomainName:f,OriginPath:"",CustomHeaders:s,CustomOriginConfig:{HTTPPort:80,HTTPSPort:443,OriginProtocolPolicy:"https-only",OriginSslProtocols:{Quantity:1,Items:["TLSv1.2"]},OriginReadTimeout:30,OriginKeepaliveTimeout:5},ConnectionAttempts:D.ConnectionAttempts??3,ConnectionTimeout:D.ConnectionTimeout??10,OriginShield:T,OriginAccessControlId:""}),l=[c(ts,e.originDomain,e.originShield?{Enabled:!0,OriginShieldRegion:e.originShieldRegion}:{Enabled:!1},a)],m={Quantity:0},S=wi(null,"static");if(r!==void 0)l.push(c(Ti,r,{Enabled:!1})),m=Kr({primaryOriginId:ts,secondaryOriginId:Ti,statusCodes:e.failoverStatusCodes}),yt(S.AllowedMethods,"the default cache behavior"),S.TargetOriginId=m.Items[0].Id;let b={CallerReference:e.callerReference??e.originDomain,Comment:e.comment??`Origin-fronted distribution for ${t[0]} → ${e.originDomain}`,Enabled:!0,Aliases:{Quantity:t.length,Items:t},DefaultRootObject:"",Origins:{Quantity:l.length,Items:l},OriginGroups:m,DefaultCacheBehavior:S,CacheBehaviors:{Quantity:i.length,Items:i.map((y)=>wi(y.pathPattern,y.kind))},ViewerCertificate:{CloudFrontDefaultCertificate:!1,ACMCertificateArn:e.viewerCertificateArn,Certificate:e.viewerCertificateArn,CertificateSource:"acm",SSLSupportMethod:"sni-only",MinimumProtocolVersion:"TLSv1.2_2021"},PriceClass:e.priceClass??"PriceClass_All",HttpVersion:"http2and3",IsIPV6Enabled:!0,Restrictions:{GeoRestriction:{RestrictionType:"none",Quantity:0}},Logging:{Enabled:!1,IncludeCookies:!1,Bucket:"",Prefix:""},WebACLId:"",CustomErrorResponses:{Quantity:0}};return St(b,"buildCloudFrontOriginConfig"),b}export{sg,uc,Ee,gc,Ln,og,pc,$n,Lt,Ar,$t,ag,cg,nh,v,yr,$s,Mn,Mt,Sr,dg,Ms,Un,Qn,Hn,lg,mc,X,Ir,ih,Gn,fc,ve,Te,Ue,x,br,Vn,Cr,ge,hc,qn,Ac,jn,yc,Ft,ug,gg,Er,sh,oh,pg,mg,ah,ch,dh,vr,zn,fg,Fs,Kn,Wn,Jn,Xn,Yn,Sc,Ic,bc,Tr,Cc,Qe,Y,Ec,ct,He,Ge,dt,we,Us,vc,hg,Ag,Tc,yg,lt,Zn,wc,Sg,Ig,Rc,wr,Re,Dc,bg,Cg,Qs,ei,ut,Eg,vg,Tg,wg,Rg,Dg,Pg,Ng,kg,ti,ri,ni,ii,si,oi,ai,Bg,xg,Rr,O,Hs,Og,_g,Lg,Pc,$g,Mg,lh,Nc,uh,gh,Fg,Ug,ph,mh,fh,kc,Qg,hh,Ah,yh,ci,di,li,ui,Bc,xc,Oc,_c,Sh,Lc,Gs,$c,Ut,gi,Vs,Dr,qs,Mc,pi,Fc,Hg,Gg,Vg,Ih};
@@ -1,2 +1,2 @@
1
- import{n}from"./chunk-7gvnrx46.js";class Me{client;region;constructor(e="us-east-1",r){this.region=e,this.client=new n(void 0,{profile:r})}async createRepository(e){let r={repositoryName:e.repositoryName};if(e.imageTagMutability)r.imageTagMutability=e.imageTagMutability;if(e.imageScanningConfiguration)r.imageScanningConfiguration=e.imageScanningConfiguration;if(e.encryptionConfiguration)r.encryptionConfiguration=e.encryptionConfiguration;if(e.tags&&e.tags.length>0)r.tags=e.tags;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.CreateRepository","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{repository:t.repository?this.parseRepository(t.repository):void 0}}async describeRepositories(e){let r={};if(e?.repositoryNames&&e.repositoryNames.length>0)r.repositoryNames=e.repositoryNames;if(e?.registryId)r.registryId=e.registryId;if(e?.maxResults)r.maxResults=e.maxResults;if(e?.nextToken)r.nextToken=e.nextToken;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.DescribeRepositories","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{repositories:t.repositories?.map((i)=>this.parseRepository(i)),nextToken:t.nextToken}}async getAuthorizationToken(e){let r={};if(e&&e.length>0)r.registryIds=e;return{authorizationData:(await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)})).authorizationData?.map((i)=>({authorizationToken:i.authorizationToken,expiresAt:i.expiresAt,proxyEndpoint:i.proxyEndpoint}))}}async deleteRepository(e){let r={repositoryName:e.repositoryName};if(e.registryId)r.registryId=e.registryId;if(e.force!==void 0)r.force=e.force;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.DeleteRepository","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{repository:t.repository?this.parseRepository(t.repository):void 0}}async describeImages(e){let r={repositoryName:e.repositoryName};if(e.registryId)r.registryId=e.registryId;if(e.imageIds&&e.imageIds.length>0)r.imageIds=e.imageIds;if(e.filter)r.filter=e.filter;if(e.maxResults)r.maxResults=e.maxResults;if(e.nextToken)r.nextToken=e.nextToken;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.DescribeImages","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{imageDetails:t.imageDetails?.map((i)=>this.parseImageDetail(i)),nextToken:t.nextToken}}async batchDeleteImage(e){let r={repositoryName:e.repositoryName,imageIds:e.imageIds};if(e.registryId)r.registryId=e.registryId;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.BatchDeleteImage","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{imageIds:t.imageIds,failures:t.failures}}async putLifecyclePolicy(e){let r={repositoryName:e.repositoryName,lifecyclePolicyText:e.lifecyclePolicyText};if(e.registryId)r.registryId=e.registryId;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.PutLifecyclePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{registryId:t.registryId,repositoryName:t.repositoryName,lifecyclePolicyText:t.lifecyclePolicyText}}async getLifecyclePolicy(e){let r={repositoryName:e.repositoryName};if(e.registryId)r.registryId=e.registryId;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.GetLifecyclePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{registryId:t.registryId,repositoryName:t.repositoryName,lifecyclePolicyText:t.lifecyclePolicyText,lastEvaluatedAt:t.lastEvaluatedAt}}async setRepositoryPolicy(e){let r={repositoryName:e.repositoryName,policyText:e.policyText};if(e.registryId)r.registryId=e.registryId;if(e.force!==void 0)r.force=e.force;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.SetRepositoryPolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{registryId:t.registryId,repositoryName:t.repositoryName,policyText:t.policyText}}async getRepositoryPolicy(e){let r={repositoryName:e.repositoryName};if(e.registryId)r.registryId=e.registryId;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.GetRepositoryPolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{registryId:t.registryId,repositoryName:t.repositoryName,policyText:t.policyText}}async tagResource(e){let r={resourceArn:e.resourceArn,tags:e.tags};await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.TagResource","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)})}async listTagsForResource(e){let r={resourceArn:e};return{tags:(await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.ListTagsForResource","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)})).tags}}createLifecyclePolicyText(e){return JSON.stringify({rules:e.map((r)=>({rulePriority:r.rulePriority,description:r.description,selection:r.selection,action:r.action}))})}async getDockerLoginCommand(){let e=await this.getAuthorizationToken();if(!e.authorizationData?.[0])throw Error("Failed to get authorization token");let r=e.authorizationData[0],t=r.authorizationToken||"",i=r.proxyEndpoint||"";return`echo "${Buffer.from(t,"base64").toString("utf8").split(":")[1]}" | docker login --username AWS --password-stdin ${i}`}getRegistryUri(e){return`${e}.dkr.ecr.${this.region}.amazonaws.com`}parseRepository(e){return{repositoryArn:e.repositoryArn,registryId:e.registryId,repositoryName:e.repositoryName,repositoryUri:e.repositoryUri,createdAt:e.createdAt,imageTagMutability:e.imageTagMutability,imageScanningConfiguration:e.imageScanningConfiguration,encryptionConfiguration:e.encryptionConfiguration}}parseImageDetail(e){return{registryId:e.registryId,repositoryName:e.repositoryName,imageDigest:e.imageDigest,imageTags:e.imageTags,imageSizeInBytes:e.imageSizeInBytes,imagePushedAt:e.imagePushedAt,imageScanStatus:e.imageScanStatus,imageScanFindingsSummary:e.imageScanFindingsSummary}}}
1
+ import{n}from"./chunk-5np6w0g7.js";class Me{client;region;constructor(e="us-east-1",r){this.region=e,this.client=new n(void 0,{profile:r})}async createRepository(e){let r={repositoryName:e.repositoryName};if(e.imageTagMutability)r.imageTagMutability=e.imageTagMutability;if(e.imageScanningConfiguration)r.imageScanningConfiguration=e.imageScanningConfiguration;if(e.encryptionConfiguration)r.encryptionConfiguration=e.encryptionConfiguration;if(e.tags&&e.tags.length>0)r.tags=e.tags;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.CreateRepository","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{repository:t.repository?this.parseRepository(t.repository):void 0}}async describeRepositories(e){let r={};if(e?.repositoryNames&&e.repositoryNames.length>0)r.repositoryNames=e.repositoryNames;if(e?.registryId)r.registryId=e.registryId;if(e?.maxResults)r.maxResults=e.maxResults;if(e?.nextToken)r.nextToken=e.nextToken;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.DescribeRepositories","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{repositories:t.repositories?.map((i)=>this.parseRepository(i)),nextToken:t.nextToken}}async getAuthorizationToken(e){let r={};if(e&&e.length>0)r.registryIds=e;return{authorizationData:(await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.GetAuthorizationToken","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)})).authorizationData?.map((i)=>({authorizationToken:i.authorizationToken,expiresAt:i.expiresAt,proxyEndpoint:i.proxyEndpoint}))}}async deleteRepository(e){let r={repositoryName:e.repositoryName};if(e.registryId)r.registryId=e.registryId;if(e.force!==void 0)r.force=e.force;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.DeleteRepository","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{repository:t.repository?this.parseRepository(t.repository):void 0}}async describeImages(e){let r={repositoryName:e.repositoryName};if(e.registryId)r.registryId=e.registryId;if(e.imageIds&&e.imageIds.length>0)r.imageIds=e.imageIds;if(e.filter)r.filter=e.filter;if(e.maxResults)r.maxResults=e.maxResults;if(e.nextToken)r.nextToken=e.nextToken;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.DescribeImages","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{imageDetails:t.imageDetails?.map((i)=>this.parseImageDetail(i)),nextToken:t.nextToken}}async batchDeleteImage(e){let r={repositoryName:e.repositoryName,imageIds:e.imageIds};if(e.registryId)r.registryId=e.registryId;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.BatchDeleteImage","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{imageIds:t.imageIds,failures:t.failures}}async putLifecyclePolicy(e){let r={repositoryName:e.repositoryName,lifecyclePolicyText:e.lifecyclePolicyText};if(e.registryId)r.registryId=e.registryId;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.PutLifecyclePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{registryId:t.registryId,repositoryName:t.repositoryName,lifecyclePolicyText:t.lifecyclePolicyText}}async getLifecyclePolicy(e){let r={repositoryName:e.repositoryName};if(e.registryId)r.registryId=e.registryId;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.GetLifecyclePolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{registryId:t.registryId,repositoryName:t.repositoryName,lifecyclePolicyText:t.lifecyclePolicyText,lastEvaluatedAt:t.lastEvaluatedAt}}async setRepositoryPolicy(e){let r={repositoryName:e.repositoryName,policyText:e.policyText};if(e.registryId)r.registryId=e.registryId;if(e.force!==void 0)r.force=e.force;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.SetRepositoryPolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{registryId:t.registryId,repositoryName:t.repositoryName,policyText:t.policyText}}async getRepositoryPolicy(e){let r={repositoryName:e.repositoryName};if(e.registryId)r.registryId=e.registryId;let t=await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.GetRepositoryPolicy","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)});return{registryId:t.registryId,repositoryName:t.repositoryName,policyText:t.policyText}}async tagResource(e){let r={resourceArn:e.resourceArn,tags:e.tags};await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.TagResource","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)})}async listTagsForResource(e){let r={resourceArn:e};return{tags:(await this.client.request({service:"ecr",region:this.region,method:"POST",path:"/",headers:{"X-Amz-Target":"AmazonEC2ContainerRegistry_V20150921.ListTagsForResource","Content-Type":"application/x-amz-json-1.1"},body:JSON.stringify(r)})).tags}}createLifecyclePolicyText(e){return JSON.stringify({rules:e.map((r)=>({rulePriority:r.rulePriority,description:r.description,selection:r.selection,action:r.action}))})}async getDockerLoginCommand(){let e=await this.getAuthorizationToken();if(!e.authorizationData?.[0])throw Error("Failed to get authorization token");let r=e.authorizationData[0],t=r.authorizationToken||"",i=r.proxyEndpoint||"";return`echo "${Buffer.from(t,"base64").toString("utf8").split(":")[1]}" | docker login --username AWS --password-stdin ${i}`}getRegistryUri(e){return`${e}.dkr.ecr.${this.region}.amazonaws.com`}parseRepository(e){return{repositoryArn:e.repositoryArn,registryId:e.registryId,repositoryName:e.repositoryName,repositoryUri:e.repositoryUri,createdAt:e.createdAt,imageTagMutability:e.imageTagMutability,imageScanningConfiguration:e.imageScanningConfiguration,encryptionConfiguration:e.encryptionConfiguration}}parseImageDetail(e){return{registryId:e.registryId,repositoryName:e.repositoryName,imageDigest:e.imageDigest,imageTags:e.imageTags,imageSizeInBytes:e.imageSizeInBytes,imagePushedAt:e.imagePushedAt,imageScanStatus:e.imageScanStatus,imageScanFindingsSummary:e.imageScanFindingsSummary}}}
2
2
  export{Me};