@stacksjs/ts-cloud 0.11.3 → 0.11.4

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 (56) hide show
  1. package/dist/bin/cli.js +2 -2
  2. package/dist/ui/404.html +1 -1
  3. package/dist/ui/accept-invitation.html +1 -1
  4. package/dist/ui/access-denied.html +1 -1
  5. package/dist/ui/account/automation.html +5 -5
  6. package/dist/ui/account/security.html +2 -2
  7. package/dist/ui/applications/compose.html +5 -5
  8. package/dist/ui/applications/new.html +2 -2
  9. package/dist/ui/data/backups.html +5 -5
  10. package/dist/ui/data/services.html +6 -6
  11. package/dist/ui/data/volumes.html +6 -6
  12. package/dist/ui/index.html +6 -6
  13. package/dist/ui/infrastructure/topology.html +5 -5
  14. package/dist/ui/integrations.html +2 -2
  15. package/dist/ui/operations/alerts.html +6 -6
  16. package/dist/ui/operations/configuration.html +6 -6
  17. package/dist/ui/operations/jobs.html +5 -5
  18. package/dist/ui/operations/maintenance.html +6 -6
  19. package/dist/ui/operations/observability.html +6 -6
  20. package/dist/ui/operations/previews.html +6 -6
  21. package/dist/ui/operations/queue.html +5 -5
  22. package/dist/ui/operations/regions.html +6 -6
  23. package/dist/ui/operations/releases.html +6 -6
  24. package/dist/ui/operations/spend.html +6 -6
  25. package/dist/ui/operations/workloads.html +6 -6
  26. package/dist/ui/security.html +2 -2
  27. package/dist/ui/server/actions.html +6 -6
  28. package/dist/ui/server/activity.html +1 -1
  29. package/dist/ui/server/capacity.html +5 -5
  30. package/dist/ui/server/database.html +5 -5
  31. package/dist/ui/server/deployments.html +5 -5
  32. package/dist/ui/server/diagnostics.html +1 -1
  33. package/dist/ui/server/firewall.html +5 -5
  34. package/dist/ui/server/fleet.html +5 -5
  35. package/dist/ui/server/logs.html +5 -5
  36. package/dist/ui/server/metrics.html +5 -5
  37. package/dist/ui/server/security.html +1 -1
  38. package/dist/ui/server/services.html +1 -1
  39. package/dist/ui/server/sites.html +6 -6
  40. package/dist/ui/server/ssh-keys.html +6 -6
  41. package/dist/ui/server/team.html +6 -6
  42. package/dist/ui/server/terminal.html +2 -2
  43. package/dist/ui/serverless/alarms.html +6 -6
  44. package/dist/ui/serverless/assets.html +2 -2
  45. package/dist/ui/serverless/cost.html +2 -2
  46. package/dist/ui/serverless/data.html +6 -6
  47. package/dist/ui/serverless/deployments.html +2 -2
  48. package/dist/ui/serverless/firewall.html +1 -1
  49. package/dist/ui/serverless/functions.html +6 -6
  50. package/dist/ui/serverless/logs.html +6 -6
  51. package/dist/ui/serverless/metrics.html +2 -2
  52. package/dist/ui/serverless/queues.html +5 -5
  53. package/dist/ui/serverless/secrets.html +5 -5
  54. package/dist/ui/serverless/traces.html +5 -5
  55. package/dist/ui/serverless.html +5 -5
  56. package/package.json +3 -3
package/dist/bin/cli.js CHANGED
@@ -28,7 +28,7 @@ Received ${t}, cleaning up...`),r)try{await r()}catch(n){console.error("Error du
28
28
  ✗ Command "${r}" not found.
29
29
  `)),this.enableDidYouMean){let e=[];for(let n of this.commands){if(n.displayName)e.push(n.displayName);if(n.aliasNames)e.push(...n.aliasNames)}let t=Cs(r,e);if(t.length>0)console.log(tn.yellow("Did you mean one of these?")),t.forEach((n)=>console.log(` ${tn.dim("•")} ${this.name} ${n}`)),console.log("")}console.log(tn.dim("Run"),`${this.name} --help`,tn.dim("to see all available commands")),ut.exit(1)}async parse(r=bs,e={}){let{run:t=!0,exitOnError:n=!1}=e;if(n)try{return await this.parse(r,{run:t})}catch(l){throw this.handleUsageError(l),l}if(this.rawArgs=r,!this.name)this.name=r[1]?Id(r[1]):"cli";let i=!0,o=r.slice(2),s=o[0];if(s&&!s.startsWith("-")){for(let l of this.commands)if(l.isMatched(s)){let d=this.mri(o,l);i=!1;let u={...d,args:d.args.slice(1)};this.setParsedInfo(u,l,s),this.emit(`command:${s}`,l);break}}if(i){for(let l of this.commands)if(l.name===""){i=!1;let d=this.mri(o,l);this.setParsedInfo(d,l),this.emit("command:!",l);break}}if(i){let l=this.mri(r.slice(2));this.setParsedInfo(l)}if(this.options.verbose)this.isVerbose=!0;if(this.options.quiet)this.isQuiet=!0;if(this.options.debug)this.isDebug=!0;if(this.options.noInteraction)this.isNoInteraction=!0;if(this.options.env)this.environment=String(this.options.env);if(this.options.dryRun)this.isDryRun=!0;if(this.options.force)this.isForce=!0;if(this.options.noEmoji!==void 0)this.useEmoji=!this.options.noEmoji;if(this.options.theme)this.theme=String(this.options.theme);if(this.options.noCache!==void 0)this.isNoCache=Boolean(this.options.noCache);if(this.options.help&&this.showHelpOnExit)this.outputHelp(),t=!1,this.unsetMatchedCommand();if(this.options.version&&this.showVersionOnExit&&this.matchedCommandName==null)this.outputVersion(),t=!1,this.unsetMatchedCommand();let c={args:this.args,options:this.options};if(t)await this.runMatchedCommand();if(!this.matchedCommand&&this.args[0]){if(this.emit("command:*"),!(this.listenerCount("command:*")>0))this.showCommandNotFound(this.args[0])}return c}mri(r,e){let t=[...this.globalCommand.options,...e?e.options:[]],n=Cd(t),i=[],o=r.indexOf("--");if(o>-1)i=r.slice(o+1),r=r.slice(0,o);let s=Sd(r,n),c={_:s._};for(let f of Object.keys(s))if(f!=="_")c[$s(f)]=s[f];let l=c._,d={"--":i},u=e&&e.config.ignoreOptionDefaultValue?e.config.ignoreOptionDefaultValue:this.globalCommand.config.ignoreOptionDefaultValue,m=Object.create(null);for(let f of t){if(!u&&f.config.default!==void 0)for(let y of f.names)d[y]=f.config.default;if(Array.isArray(f.config.type)){if(m[f.name]===void 0)m[f.name]={shouldTransform:!0,transformFunction:f.config.type[0]}}}for(let f of Object.keys(c))if(f!=="_"){let y=f.split(".");kd(d,y,c[f]),Rd(d,m)}return{args:l,options:d}}async run(r=bs){return this.parse(r,{run:!0,exitOnError:!0})}handleUsageError(r){if(!(!!r&&typeof r==="object"&&r.name==="ClappError"&&r.isUsageError!==!1))return;let n=r,i=n.message??"command-line error",o=this.name?`${this.name}: `:"",s=/--help/.test(i)?"":`
30
30
  Run \`${this.name??"cli"} --help\` for usage.`;ut.stderr.write(`${o}${i}${s}
31
- `),ut.exit(n.exitCode??2)}async runMatchedCommand(){let{args:r,options:e,matchedCommand:t}=this;if(!t||!t.commandAction)return;t.checkUnknownOptions(),t.checkOptionValue(),t.checkRequiredArgs();let n=[];t.args.forEach((c,l)=>{if(c.variadic)n.push(r.slice(l));else n.push(r[l])}),n.push(e);let i={command:t,args:n,options:e};for(let c of t.beforeHooks)await c(i);let o,s=async()=>{let c=t.commandAction.apply(this,n);if(c instanceof Promise)o=await c;else o=c;return o};if(t.middleware.length>0){let c=0,l=async()=>{if(c<t.middleware.length){let d=t.middleware[c++];await d({...i,next:l})}else await s()};await l()}else await s();for(let c of t.afterHooks)await c(i);return o}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return ut.removeAllListeners("SIGINT"),ut.removeAllListeners("SIGTERM"),this.signalHandlersSet=!1,this}destroy(){this.removeSignalHandlers(),this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.matchedCommand=void 0,this.matchedCommandName=void 0,this.removeAllListeners()}}class ks{cache;enabled;cleanupInterval=null;hits=0;misses=0;constructor(){this.cache=new Map,this.enabled=!0,this.startCleanupInterval()}startCleanupInterval(){if(this.cleanupInterval)return;this.cleanupInterval=setInterval(()=>{this.cleanup()},30000),this.cleanupInterval.unref()}stopCleanup(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null}isEnabled(){return this.enabled}get(r){if(!this.enabled){this.misses++;return}let e=this.cache.get(r);if(!e){this.misses++;return}if(Date.now()-e.timestamp>e.ttl){this.cache.delete(r),this.misses++;return}return this.hits++,e.value}set(r,e,t=5000){if(!this.enabled)return;this.cache.set(r,{value:e,timestamp:Date.now(),ttl:t})}has(r){if(!this.enabled)return!1;let e=this.cache.get(r);if(!e)return!1;if(Date.now()-e.timestamp>e.ttl)return this.cache.delete(r),!1;return!0}delete(r){this.cache.delete(r)}clear(){this.cache.clear()}disable(){this.enabled=!1,this.clear()}enable(){this.enabled=!0}stats(){return{size:this.cache.size,enabled:this.enabled,hits:this.hits,misses:this.misses}}resetStats(){this.hits=0,this.misses=0}keys(){return Array.from(this.cache.keys())}cleanup(){let r=Date.now();for(let[e,t]of this.cache.entries())if(r-t.timestamp>t.ttl)this.cache.delete(e)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var Wf=new ks;function Ld(){if("FORCE_COLOR"in nn.env)return nn.env.FORCE_COLOR!=="0";if("NO_COLOR"in nn.env||nn.env.TERM==="dumb")return!1;if(nn.platform==="win32")return!0;return ws.isatty(1)&&ws.isatty(2)}var Rs=Ld();function te(r,e){if(!Rs)return(t)=>t;return(t)=>r+t+e}var jd=te("\x1B[0m","\x1B[0m"),Fd=te("\x1B[31m","\x1B[39m"),Md=te("\x1B[32m","\x1B[39m"),Bd=te("\x1B[33m","\x1B[39m"),_d=te("\x1B[34m","\x1B[39m"),zd=te("\x1B[35m","\x1B[39m"),Ud=te("\x1B[36m","\x1B[39m"),Vd=te("\x1B[37m","\x1B[39m"),Wd=te("\x1B[90m","\x1B[39m"),qd=te("\x1B[1m","\x1B[22m"),Gd=te("\x1B[3m","\x1B[23m"),Kd=te("\x1B[4m","\x1B[24m"),Hd=te("\x1B[2m","\x1B[22m"),Jd=te("\x1B[7m","\x1B[27m"),Yd=te("\x1B[8m","\x1B[28m"),Zd=te("\x1B[9m","\x1B[29m"),Qd=te("\x1B[41m","\x1B[49m"),Xd=te("\x1B[42m","\x1B[49m"),eu=te("\x1B[43m","\x1B[49m"),tu=te("\x1B[44m","\x1B[49m"),nu=te("\x1B[45m","\x1B[49m"),ru=te("\x1B[46m","\x1B[49m"),iu=te("\x1B[47m","\x1B[49m"),ou=Rs,su={reset:jd,red:Fd,green:Md,yellow:Bd,blue:_d,magenta:zd,cyan:Ud,white:Vd,gray:Wd,bold:qd,italic:Gd,underline:Kd,dim:Hd,inverse:Jd,hidden:Yd,strikethrough:Zd,bgRed:Qd,bgGreen:Xd,bgYellow:eu,bgBlue:tu,bgMagenta:nu,bgCyan:ru,bgWhite:iu,isColorSupported:ou},au=su,lu=["up","down","left","right","space","enter","cancel"],Yf={actions:new Set(lu),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};var Zf=cu.platform.startsWith("win"),Qf=Symbol("clapp:cancel");var du=Pd();var ee=(r,e)=>du?r:e,Xf=ee("◆","*"),eg=ee("■","x"),tg=ee("▲","x"),ng=ee("◇","o"),rg=ee("┌","T"),uu=ee("│","|"),ig=ee("└","—"),og=ee("●",">"),sg=ee("○"," "),ag=ee("◻","[•]"),cg=ee("◼","[+]"),lg=ee("◻","[ ]"),dg=ee("▪","•"),ug=ee("─","-"),mg=ee("╮","+"),pg=ee("├","+"),fg=ee("╯","+"),gg=ee("●","•"),hg=ee("◆","*"),yg=ee("▲","!"),vg=ee("■","x");var bg={light:ee("─","-"),heavy:ee("━","="),block:ee("█","#")};function mu(){return`${au.gray(uu)} `}var wg=mu();var Ir="0.11.3";import{existsSync as Pr}from"node:fs";import{mkdir as fu,writeFile as Is}from"node:fs/promises";function Er(r){r.command("init","Initialize a new ts-cloud project").option("--mode <mode>","Deployment mode: server or serverless").option("--name <name>","Project name").option("--region <region>","AWS Region").action(async(e)=>{if(w("Initializing ts-cloud Project"),Pr("cloud.config.ts")){if(!await O("cloud.config.ts already exists. Overwrite?",!1)){a("Initialization cancelled");return}}let t=e?.name||await Z("Project name","my-app"),n=e?.mode||await xr("Select deployment mode",["serverless","server"]),i=e?.region||await xr("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),o=new A("Creating configuration file...");o.start();let s=`import { defineConfig } from '@ts-cloud/core'
31
+ `),ut.exit(n.exitCode??2)}async runMatchedCommand(){let{args:r,options:e,matchedCommand:t}=this;if(!t||!t.commandAction)return;t.checkUnknownOptions(),t.checkOptionValue(),t.checkRequiredArgs();let n=[];t.args.forEach((c,l)=>{if(c.variadic)n.push(r.slice(l));else n.push(r[l])}),n.push(e);let i={command:t,args:n,options:e};for(let c of t.beforeHooks)await c(i);let o,s=async()=>{let c=t.commandAction.apply(this,n);if(c instanceof Promise)o=await c;else o=c;return o};if(t.middleware.length>0){let c=0,l=async()=>{if(c<t.middleware.length){let d=t.middleware[c++];await d({...i,next:l})}else await s()};await l()}else await s();for(let c of t.afterHooks)await c(i);return o}removeSignalHandlers(){if(!this.signalHandlersSet)return this;return ut.removeAllListeners("SIGINT"),ut.removeAllListeners("SIGTERM"),this.signalHandlersSet=!1,this}destroy(){this.removeSignalHandlers(),this.commands=[],this.rawArgs=[],this.args=[],this.options={},this.matchedCommand=void 0,this.matchedCommandName=void 0,this.removeAllListeners()}}class ks{cache;enabled;cleanupInterval=null;hits=0;misses=0;constructor(){this.cache=new Map,this.enabled=!0,this.startCleanupInterval()}startCleanupInterval(){if(this.cleanupInterval)return;this.cleanupInterval=setInterval(()=>{this.cleanup()},30000),this.cleanupInterval.unref()}stopCleanup(){if(this.cleanupInterval)clearInterval(this.cleanupInterval),this.cleanupInterval=null}isEnabled(){return this.enabled}get(r){if(!this.enabled){this.misses++;return}let e=this.cache.get(r);if(!e){this.misses++;return}if(Date.now()-e.timestamp>e.ttl){this.cache.delete(r),this.misses++;return}return this.hits++,e.value}set(r,e,t=5000){if(!this.enabled)return;this.cache.set(r,{value:e,timestamp:Date.now(),ttl:t})}has(r){if(!this.enabled)return!1;let e=this.cache.get(r);if(!e)return!1;if(Date.now()-e.timestamp>e.ttl)return this.cache.delete(r),!1;return!0}delete(r){this.cache.delete(r)}clear(){this.cache.clear()}disable(){this.enabled=!1,this.clear()}enable(){this.enabled=!0}stats(){return{size:this.cache.size,enabled:this.enabled,hits:this.hits,misses:this.misses}}resetStats(){this.hits=0,this.misses=0}keys(){return Array.from(this.cache.keys())}cleanup(){let r=Date.now();for(let[e,t]of this.cache.entries())if(r-t.timestamp>t.ttl)this.cache.delete(e)}destroy(){this.stopCleanup(),this.clear(),this.resetStats()}}var Wf=new ks;function Ld(){if("FORCE_COLOR"in nn.env)return nn.env.FORCE_COLOR!=="0";if("NO_COLOR"in nn.env||nn.env.TERM==="dumb")return!1;if(nn.platform==="win32")return!0;return ws.isatty(1)&&ws.isatty(2)}var Rs=Ld();function te(r,e){if(!Rs)return(t)=>t;return(t)=>r+t+e}var jd=te("\x1B[0m","\x1B[0m"),Fd=te("\x1B[31m","\x1B[39m"),Md=te("\x1B[32m","\x1B[39m"),Bd=te("\x1B[33m","\x1B[39m"),_d=te("\x1B[34m","\x1B[39m"),zd=te("\x1B[35m","\x1B[39m"),Ud=te("\x1B[36m","\x1B[39m"),Vd=te("\x1B[37m","\x1B[39m"),Wd=te("\x1B[90m","\x1B[39m"),qd=te("\x1B[1m","\x1B[22m"),Gd=te("\x1B[3m","\x1B[23m"),Kd=te("\x1B[4m","\x1B[24m"),Hd=te("\x1B[2m","\x1B[22m"),Jd=te("\x1B[7m","\x1B[27m"),Yd=te("\x1B[8m","\x1B[28m"),Zd=te("\x1B[9m","\x1B[29m"),Qd=te("\x1B[41m","\x1B[49m"),Xd=te("\x1B[42m","\x1B[49m"),eu=te("\x1B[43m","\x1B[49m"),tu=te("\x1B[44m","\x1B[49m"),nu=te("\x1B[45m","\x1B[49m"),ru=te("\x1B[46m","\x1B[49m"),iu=te("\x1B[47m","\x1B[49m"),ou=Rs,su={reset:jd,red:Fd,green:Md,yellow:Bd,blue:_d,magenta:zd,cyan:Ud,white:Vd,gray:Wd,bold:qd,italic:Gd,underline:Kd,dim:Hd,inverse:Jd,hidden:Yd,strikethrough:Zd,bgRed:Qd,bgGreen:Xd,bgYellow:eu,bgBlue:tu,bgMagenta:nu,bgCyan:ru,bgWhite:iu,isColorSupported:ou},au=su,lu=["up","down","left","right","space","enter","cancel"],Yf={actions:new Set(lu),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"}};var Zf=cu.platform.startsWith("win"),Qf=Symbol("clapp:cancel");var du=Pd();var ee=(r,e)=>du?r:e,Xf=ee("◆","*"),eg=ee("■","x"),tg=ee("▲","x"),ng=ee("◇","o"),rg=ee("┌","T"),uu=ee("│","|"),ig=ee("└","—"),og=ee("●",">"),sg=ee("○"," "),ag=ee("◻","[•]"),cg=ee("◼","[+]"),lg=ee("◻","[ ]"),dg=ee("▪","•"),ug=ee("─","-"),mg=ee("╮","+"),pg=ee("├","+"),fg=ee("╯","+"),gg=ee("●","•"),hg=ee("◆","*"),yg=ee("▲","!"),vg=ee("■","x");var bg={light:ee("─","-"),heavy:ee("━","="),block:ee("█","#")};function mu(){return`${au.gray(uu)} `}var wg=mu();var Ir="0.11.4";import{existsSync as Pr}from"node:fs";import{mkdir as fu,writeFile as Is}from"node:fs/promises";function Er(r){r.command("init","Initialize a new ts-cloud project").option("--mode <mode>","Deployment mode: server or serverless").option("--name <name>","Project name").option("--region <region>","AWS Region").action(async(e)=>{if(w("Initializing ts-cloud Project"),Pr("cloud.config.ts")){if(!await O("cloud.config.ts already exists. Overwrite?",!1)){a("Initialization cancelled");return}}let t=e?.name||await Z("Project name","my-app"),n=e?.mode||await xr("Select deployment mode",["serverless","server"]),i=e?.region||await xr("Select AWS region",["us-east-1","us-west-2","eu-west-1","eu-central-1","ap-southeast-1"]),o=new A("Creating configuration file...");o.start();let s=`import { defineConfig } from '@ts-cloud/core'
32
32
 
33
33
  export default defineConfig({
34
34
  project: {
@@ -315,7 +315,7 @@ Resources to create: ${k}`);let T={};for(let E of Object.values(d.Resources||{})
315
315
  No changes detected - infrastructure is up to date`);return}if(a(`
316
316
  Changes detected:
317
317
  `),b.length>0){h(`Resources to add (${b.length}):`);for(let C of b){let k=y[C].Type;a(` + ${C} (${k})`)}console.log()}if(I.length>0){p(`Resources to remove (${I.length}):`);for(let C of I){let k=f[C].Type;a(` - ${C} (${k})`)}console.log()}if(v.length>0){R(`Resources to modify (${v.length}):`);for(let C of v){let k=y[C].Type;a(` ~ ${C} (${k})`)}console.log()}a("Summary:"),a(` - Add: ${b.length}`),a(` - Remove: ${I.length}`),a(` - Modify: ${v.length}`),a("\nRun `cloud deploy` to apply these changes")}catch(t){p(`Diff failed: ${t.message}`)}})}function gt(r,e=0){let t=Number(r);return Number.isFinite(t)?t:e}function qr(r){return gt(r)*1024}function Gu(r){let e=r?.date?.trim(),t=r?.time?.trim();if(!e||!t)return null;let n=new Date(`${e}T${t}Z`);return Number.isFinite(n.getTime())?n.toISOString():null}function Ku(r,e,t){let n;try{n=JSON.parse(r)}catch{return[]}let i=(n?.sysstat?.hosts??[]).flatMap((s)=>s.statistics??[]),o=[];for(let s of i){let c=Gu(s.timestamp);if(!c)continue;let l=new Date(c).getTime();if(l<e.getTime()||l>t.getTime())continue;let d=s["cpu-load"]?.find((m)=>m.cpu==="all")??s["cpu-load"]?.[0],u=gt(d?.idle,100);o.push({timestamp:c,cpuUsedPercent:Math.max(0,Math.min(100,100-u)),cpuUserPercent:gt(d?.user),cpuSystemPercent:gt(d?.system),cpuIowaitPercent:gt(d?.iowait),memoryUsedPercent:gt(s.memory?.["memused-percent"]),memoryUsedBytes:qr(s.memory?.memused),memoryAvailableBytes:qr(s.memory?.avail),swapUsedPercent:gt(s.memory?.["swpused-percent"]),swapUsedBytes:qr(s.memory?.swpused)})}return o.sort((s,c)=>s.timestamp.localeCompare(c.timestamp))}function Hu(r){return Object.fromEntries(Object.entries(r.time_series??{}).map(([e,t])=>[e,(t.values??[]).map(([n,i])=>({timestamp:new Date(Number(n)*1000).toISOString(),value:gt(i,Number.NaN)})).filter((n)=>Number.isFinite(n.value))]))}function ga(r){return r.length?r.reduce((e,t)=>e+t,0)/r.length:null}function Gr(r){return r.length?Math.max(...r):null}function Ju(r){return r.length?Math.min(...r):null}async function ha(r){let[e,t]=await Promise.all([r.client.getServerMetrics(r.serverId,{types:["cpu","disk","network"],start:r.from,end:r.to,step:r.step}),Kr(r.remote.host,"LC_ALL=C sadf -j -- -u -r -S",r.remote).catch(()=>"")]),n=Ku(t,r.from,r.to),i=Hu(e),s=(i.cpu??[]).map((m)=>Math.max(0,Math.min(100,m.value/Math.max(1,r.cores)))),c=n.map((m)=>m.cpuUsedPercent),l=s.length?s:c,d=n.map((m)=>m.memoryUsedPercent),u=n.map((m)=>m.swapUsedPercent);return{from:r.from.toISOString(),to:r.to.toISOString(),cores:r.cores,host:n,provider:i,summary:{cpuAveragePercent:ga(l),cpuPeakPercent:Gr(l),memoryAveragePercent:ga(d),memoryPeakPercent:Gr(d),memoryMinimumAvailableBytes:Ju(n.map((m)=>m.memoryAvailableBytes)),swapPeakPercent:Gr(u)}}}function Yu(r){if(r.length===0)throw Error("A server name cannot be empty.");if(r.length>253)throw Error(`'${r}' is longer than the 253 characters a hostname allows.`);for(let e of r.split(".")){if(e.length===0)throw Error(`'${r}' has an empty label — a hostname cannot contain '..'.`);if(e.length>63)throw Error(`'${e}' is longer than the 63 characters a hostname label allows.`);if(!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(e))throw Error(`'${e}' is not a valid hostname label. Use letters, digits and hyphens, not starting or ending with a hyphen.`)}}async function ya(r,e,t){if(Yu(e),r===e)throw Error(`'${r}' is already named that.`);if((await t.takenNames()).some((o)=>o===e&&o!==r))throw Error(`'${e}' is already taken. Server names have to be unique within a provider project.`);let i=[];if(t.providerName&&t.renameProvider){let{providerName:o,renameProvider:s}=t;i.push({id:"provider",title:"Rename the server at the provider",change:{from:r,to:e},satisfied:async()=>await o()===e,apply:()=>s(e)})}if(t.stateName&&t.writeStateName){let{stateName:o,writeStateName:s}=t;i.push({id:"state-pin",title:"Update the recorded name in the local driver state",change:{from:r,to:e},satisfied:async()=>{let c=await o();return c===void 0||c===e},apply:()=>s(e)})}if(t.remoteHostname&&t.setRemoteHostname){let{remoteHostname:o,setRemoteHostname:s}=t;i.push({id:"hostname",title:"Set the hostname on the box",change:{from:r,to:e},satisfied:async()=>await o()===e,apply:()=>s(e)})}return i.push({id:"inventory",title:"Rename the fleet inventory record",change:{from:r,to:e},satisfied:async()=>t.inventoryName()===e,apply:async()=>{await t.renameInventory(e)}}),{operation:"server:rename",target:r,steps:i}}function va(r){let e=`'${r.replace(/'/g,`'"'"'`)}'`;return["set -eu",'TS_CLOUD_OLD="$(hostname)"',`hostnamectl set-hostname ${e} 2>/dev/null || { echo ${e} > /etc/hostname && hostname ${e}; }`,'if grep -q "127.0.1.1" /etc/hosts; then',` sed -i "s/^127\\.0\\.1\\.1.*/127.0.1.1\\t${r}/" /etc/hosts`,"else",` printf '127.0.1.1\\t%s\\n' ${e} >> /etc/hosts`,"fi",'printf "%s -> %s\\n" "$TS_CLOUD_OLD" "$(hostname)"'].join(`
318
- `)}async function Zu(){let r=await P(),e=q(process.cwd(),r),t=new et(e.store),n=new Ta(t,[new Un("aws"),new Un("hetzner"),new Un("ssh")]);return{controlPlane:e,store:t,service:n}}async function Ue(r){let e=await Zu();try{return await r(e)}finally{e.controlPlane.store.close()}}var yt=(r,e)=>{let t=r.store.list(r.controlPlane.project.id).find((n)=>n.id===e||n.name===e);if(!t)throw Error(`Server ${e} was not found.`);return t},fe=(r)=>{p(r instanceof Error?r.message:String(r)),process.exitCode=1};function Zr(r,e){let t=Number.parseInt(r??"",10);return Number.isFinite(t)&&t>=0?t:e}function Qu(r="3h"){let e=r.match(/^(\d+)(m|h)$/);if(!e)throw Error("--range must use minutes or hours, for example 30m or 3h.");let t=Number(e[1])*(e[2]==="h"?3600000:60000);if(t<300000||t>86400000)throw Error("--range must be between 5 minutes and 24 hours.");return t}function cn(r,e="%"){return r==null?"unavailable":`${r.toFixed(1)}${e}`}async function Xu(r,e){let t=await P();if(t.cloud?.provider!=="hetzner")throw Error("server:monitoring currently requires the Hetzner provider.");let n=e.env??process.env.CLOUD_ENV??"production",i=Ve(t,n),o=await Rt(i);if(!o?.serverId)throw Error(`No pinned Hetzner server was found for ${i}.`);if(![o.serverName,String(o.serverId),o.stackName].includes(r))throw Error(`Refusing to read ${r}: project state is pinned to ${o.serverName}.`);let s=ln(t),c=new Vt({apiToken:Wt(s.apiToken,t)}),l=await c.getServer(o.serverId),d=o.publicIp??l.public_net.ipv4?.ip;if(!d)throw Error(`Server ${l.name} has no public IPv4 address.`);let u=new Date,m=new Date(u.getTime()-Qu(e.range)),f=Math.min(3600,Math.max(60,Zr(e.step,60))),y=await ha({client:c,serverId:l.id,cores:Math.max(1,Number(l.server_type.cores)||1),from:m,to:u,step:f,remote:{host:d,user:o.sshUser??s.sshUser,identityFile:s.sshPrivateKeyPath,connectTimeoutSec:15}});if(e.json){console.log(JSON.stringify({schemaVersion:1,server:l.name,result:y},null,2));return}x(["Server","Window","CPU average / peak","RAM average / peak","Minimum available","Swap peak"],[[l.name,`${y.from} to ${y.to}`,`${cn(y.summary.cpuAveragePercent)} / ${cn(y.summary.cpuPeakPercent)}`,`${cn(y.summary.memoryAveragePercent)} / ${cn(y.summary.memoryPeakPercent)}`,y.summary.memoryMinimumAvailableBytes==null?"unavailable":`${(y.summary.memoryMinimumAvailableBytes/1073741824).toFixed(2)} GB`,cn(y.summary.swapPeakPercent)]])}function Na(r){let e=r.targetType.prices?.find((t)=>t.location===r.location);return{serverId:r.serverId,serverName:r.serverName,location:r.location,from:r.currentType.name,to:r.targetType.name,capacityAvailable:r.capacityAvailable,alreadyComplete:r.alreadyComplete,upgradeDisk:r.upgradeDisk,target:{cores:r.targetType.cores,memoryGb:r.targetType.memory,diskGb:r.targetType.disk,architecture:r.targetType.architecture,cpuType:r.targetType.cpu_type},price:e?{hourlyNet:e.price_hourly.net,hourlyGross:e.price_hourly.gross,monthlyNet:e.price_monthly.net,monthlyGross:e.price_monthly.gross}:void 0}}function em(r){let e=Na(r);x(["Server","Location","Resize","Capacity","CPU / RAM / disk","Monthly"],[[`${e.serverName} (${e.serverId})`,e.location,`${e.from} -> ${e.to}`,e.capacityAvailable?"available":"waiting",`${e.target.cores??0} / ${e.target.memoryGb??0} GB / ${e.target.diskGb??0} GB`,e.price?.monthlyGross??e.price?.monthlyNet??"not reported"]])}async function tm(r,e,t){let n=await P();if(n.cloud?.provider!=="hetzner")throw Error("server:resize currently requires the Hetzner provider.");let i=t.env??process.env.CLOUD_ENV??"production",o=Ve(n,i),s=await Rt(o),c=ln(n),l=new Vt({apiToken:Wt(c.apiToken,n)}),u=s&&(r===s.serverName||r===String(s.serverId)||r===s.stackName)?await l.getServer(s.serverId):(await l.listServers()).find((E)=>E.name===r||String(E.id)===r);if(!u)throw Error(`Hetzner server ${r} was not found.`);if(s?.serverId&&s.serverId!==u.id)throw Error(`Refusing to resize ${r}: project state is pinned to server ${s.serverId}.`);let m=wa(e),f=t.upgradeDisk!==!1,y=await Jr(l,u.id,m,f);if(t.json)console.log(JSON.stringify({schemaVersion:1,plan:Na(y)},null,2));else em(y);if(!t.apply){if(!t.json)a(`Preview only. Re-run with --apply --confirm ${u.name} to execute.`);return}if(t.confirm!==u.name)throw Error(`Resize requires --confirm ${u.name}.`);let S=s?.publicIp??u.public_net.ipv4?.ip;if(!S)throw Error(`Server ${u.name} has no public IPv4 address for verification.`);let g={host:S,user:s?.sshUser??c.sshUser,identityFile:c.sshPrivateKeyPath,connectTimeoutSec:15,sshTimeoutMs:600000,routeTimeoutMs:20000},b=await Ea(o),I=new Date().toISOString(),v={schemaVersion:1,operationId:b?.targetType===m&&b.serverId===u.id?b.operationId:crypto.randomUUID(),stackName:o,serverId:u.id,serverName:u.name,sourceType:y.currentType.name,targetType:m,upgradeDisk:f,phase:"planning",status:"running",attempts:b?.targetType===m?b.attempts:0,startedAt:b?.targetType===m?b.startedAt:I,updatedAt:I,manifest:b?.targetType===m?b.manifest:void 0};await ht(v);let C=Zr(t.pollSeconds,900)*1000,k=Zr(t.maxWaitMinutes,0)*60000,T=Date.now();for(;;){let E=await Jr(l,u.id,m,f);if(!E.capacityAvailable&&!E.alreadyComplete){if(v={...v,phase:"waiting-capacity",status:"waiting-capacity",updatedAt:new Date().toISOString()},await ht(v),!t.wait){if(!t.json)a(`${m} has no capacity in ${E.location}. The server remains running and unchanged. Use --wait to poll safely.`);return}if(k>0&&Date.now()-T>=k)throw Error(`Timed out waiting for ${m} capacity in ${E.location}.`);if(!t.json)a(`Waiting ${Math.round(C/1000)} seconds for ${m} capacity...`);await Bun.sleep(C);continue}let D=await Da(o);try{let N=await(async()=>{try{return v={...v,status:"running",attempts:v.attempts+1,updatedAt:new Date().toISOString()},await ht(v),await $a({client:l,serverId:u.id,targetType:m,upgradeDisk:f,resumeFromOff:v.phase==="shutting-down"||v.phase==="resizing"||v.phase==="powering-on"||v.phase==="verifying",manifest:v.manifest,hooks:{preflight:async()=>{let _=await Ca(g);return v={...v,manifest:_,updatedAt:new Date().toISOString()},await ht(v),_},afterBoot:async(_,B,V,K)=>Aa(g,B,V,K),onPhase:async(_,B)=>{if(v={...v,phase:_,status:_==="waiting-capacity"?"waiting-capacity":_==="complete"?"completed":"running",updatedAt:new Date().toISOString()},await ht(v),!t.json)a(`${_}${B?` ${JSON.stringify(B)}`:""}`)}}})}finally{await D()}})();if(v={...v,status:N.status==="completed"||N.status==="already-complete"?"completed":N.status==="recovered"?"recovered":"waiting-capacity",lastError:N.status==="recovered"?N.error:void 0,updatedAt:new Date().toISOString()},await ht(v),t.json)console.log(JSON.stringify({schemaVersion:1,result:N},null,2));else if(N.status==="completed"||N.status==="already-complete")h(`${u.name} is on ${m} and passed full post-boot verification.`);else if(N.status==="recovered"&&N.retryable&&t.wait){a(`Capacity race recovered safely. Waiting ${Math.round(C/1000)} seconds before retry.`),await Bun.sleep(C);continue}else if(N.status==="recovered")throw Error(`Resize did not complete and the original server was recovered: ${N.error}`);return}catch(N){throw v={...v,status:"failed",lastError:N instanceof Error?N.message:String(N),updatedAt:new Date().toISOString()},await ht(v),N}}}async function nm(r,e){let t=await P();if(t.cloud?.provider!=="hetzner")throw Error("server:optimize currently requires the Hetzner provider.");let n=e.env??process.env.CLOUD_ENV??"production",i=Ve(t,n),o=await Rt(i);if(!o?.serverId)throw Error(`No pinned Hetzner server was found for ${i}.`);if(![o.serverName,String(o.serverId),o.stackName].includes(r))throw Error(`Refusing to optimize ${r}: project state is pinned to ${o.serverName}.`);let s=ln(t),c=new Vt({apiToken:Wt(s.apiToken,t)}),l=await c.getServer(o.serverId),d=o.publicIp??l.public_net.ipv4?.ip;if(!d)throw Error(`Server ${l.name} has no public IPv4 address.`);let u=Ra(t);if(e.json)console.log(JSON.stringify({schemaVersion:1,server:l.name,plan:u},null,2));else x(["Server","Firewall ports","Monitoring","Auto updates","Swap","SSH passwords"],[[l.name,u.firewallPorts.join(","),u.monitoring?"on":"off",u.autoUpdates?"on":"off",`${u.swapGb} GB`,"disabled"]]);if(!e.apply){if(!e.json)a(`Preview only. Re-run with --apply --confirm ${l.name} to execute.`);return}if(e.confirm!==l.name)throw Error(`Optimization requires --confirm ${l.name}.`);let m={host:d,user:o.sshUser??s.sshUser,identityFile:s.sshPrivateKeyPath,connectTimeoutSec:15},f=await Yr(m),y=f.routeProbes.filter((V)=>!V.ok),S=t.infrastructure?.compute,g=Hr(S)?new Set(["nginx.service","bun-gateway.service","ts-cloud-nginx.service"]):new Set,b=f.failedUnits.filter((V)=>!g.has(V));if(b.length>0)throw Error(`Preflight found failed services: ${b.join(", ")}`);if(y.length>0)throw Error(`Preflight found unhealthy routes: ${y.map((V)=>V.domain).join(", ")}`);let I=Hr(S)?[]:Object.values(t.sites??{}).flatMap((V)=>typeof V?.port==="number"&&![80,443].includes(V.port)?[V.port]:[]),v=ba({allowSsh:S?.allowSsh!==!1,sitePorts:I,allowedPorts:S?.firewall?.allowedPorts}),C=(o.firewallId?(await c.listFirewalls()).find((V)=>V.id===o.firewallId):void 0)??(await c.listFirewalls()).find((V)=>V.name===`${t.project.slug}-${n}-app-fw`);if(!C)throw Error(`Managed firewall for ${l.name} was not found.`);let k=await c.setFirewallRules(C.id,v);await Promise.all(k.map((V)=>c.waitForAction(V.id))),await Ia(t,m);let[T,E]=await Promise.all([Yr(m),Pa(m)]),D=xa(u,E),N=ka(f,T),_=T.routeProbes.filter((V)=>!V.ok);if(N.stoppedServices.length>0)D.push(`services stopped: ${N.stoppedServices.join(", ")}`);if(N.changedRouteFragments.length>0)D.push(`route fragments changed: ${N.changedRouteFragments.join(", ")}`);if(N.missingRouteIds.length>0)D.push(`routes disappeared: ${N.missingRouteIds.join(", ")}`);if(N.changedReleaseLinks.length>0)D.push(`release links changed: ${N.changedReleaseLinks.join(", ")}`);if(N.missingData.length>0)D.push(`databases or volumes disappeared: ${N.missingData.join(", ")}`);if(_.length>0)D.push(`unhealthy routes: ${_.map((V)=>V.domain).join(", ")}`);if(D.length>0)throw Error(`Host optimization verification failed: ${D.join("; ")}`);let B={serverId:l.id,serverName:l.name,services:T.runningServices.length,routes:T.routeIds.length,healthyDomains:T.routeProbes.length,releaseLinks:T.releaseLinks.length,databasesAndVolumes:T.dataCatalog.length,report:E};if(e.json)console.log(JSON.stringify({schemaVersion:1,result:B},null,2));else h(`${l.name} passed full host, rpx route, service, release, and data verification.`)}async function rm(r,e,t){let n=await P(),i=t.env??"production",o=Ve(n,i);await Ue(async(s)=>{let c=yt(s,r),l=s.store.list(s.controlPlane.project.id,!0),d=c.provider==="hetzner"&&c.providerId?(()=>{try{let C=ln(n);return new Vt({apiToken:Wt(C.apiToken,n)})}catch{return null}})():null,u=c.providerId?Number(c.providerId):Number.NaN,m=await Rt(o),f=m!=null&&m.serverId===u,y=new Mn,S=c.trustState==="pinned",g={takenNames:async()=>{let C=l.map((k)=>k.name);if(d)C.push(...(await d.listServers()).map((k)=>k.name));return C},inventoryName:()=>s.store.get(c.id)?.name??c.name,renameInventory:(C)=>{s.store.update(c.id,{name:C})},...d&&Number.isFinite(u)?{providerName:async()=>(await d.getServer(u)).name,renameProvider:async(C)=>{await d.renameServer(u,C)}}:{},...f?{stateName:async()=>(await Rt(o))?.serverName,writeStateName:async(C)=>{let k=await Rt(o);if(k)await Sa(o,{...k,serverName:C})}}:{},...S?{remoteHostname:async()=>{let C=await y.exec(c,"hostname");return C.code===0?C.stdout.trim():void 0},setRemoteHostname:async(C)=>{let k=await y.exec(c,va(C));if(k.code!==0)throw Error(k.stderr.trim()||`hostname exited ${k.code}`)}}:{}},b=await ya(c.name,e,g),I=await On(b);if(!t.apply){let C=[d?"":"provider record (no provider id or no API token)",f?"":"local state pin (none points at this server)",S?"":"box hostname (host key is not pinned — run server:validate first)"].filter(Boolean);if(t.json){console.log(JSON.stringify({schemaVersion:1,operation:b.operation,target:b.target,rename:{from:c.name,to:e},notCovered:C,steps:I.map((k)=>({id:k.step.id,title:k.step.title,state:k.state,change:k.step.change,reason:k.reason}))},null,2));return}for(let k of Ln(b,I))console.log(k);for(let k of C)console.log(` not covered: ${k}`);console.log(" Re-run with --apply to perform it.");return}let v=await jn(b,I,{log:(C)=>a(C),audit:(C)=>s.controlPlane.store.appendEvent({projectId:s.controlPlane.project.id,resourceId:c.resourceId,type:`${C.operation}.${C.step}.${C.state}`,level:C.state==="failed"?"error":"info",payload:{target:C.target,renameTo:e,...C.error?{error:C.error}:{}}})});if(t.json)console.log(JSON.stringify({schemaVersion:1,outcome:v},null,2));if(!v.success){let C=v.steps.find((k)=>k.state==="failed");throw Error(`${b.operation} stopped at '${C?.title}': ${C?.error}. Earlier steps stayed applied; re-run the same command to continue from here.`)}if(!t.json)h(`Renamed ${c.name} to ${e}.`)})}function Qr(r){r.command("capabilities [server]","Show provider and target operation support").option("--json","Print structured JSON").action(async(e,t)=>{try{await Ue(async(n)=>{let i=e?[yt(n,e)]:n.store.list(n.controlPlane.project.id),o=i.flatMap((s)=>Object.entries(s.capabilities).map(([c,l])=>({server:s.name,provider:s.provider,action:c,...l})));if(t.json)console.log(JSON.stringify({schemaVersion:1,targets:o},null,2));else x(["Server","Provider","Action","Support","Explanation"],o.map((s)=>[s.server,s.provider,s.action,s.supported?"supported":"unsupported",s.reason??"—"]))})}catch(n){fe(n)}}),r.command("server:resize <name> <type>","Safely resize Hetzner compute with full host verification").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Execute the reviewed resize plan").option("--confirm <name>","Exact server name required for mutation").option("--upgrade-disk","Permanently expand the root disk to the target size",{default:!0}).option("--wait","Poll capacity without taking the server offline until placement is available").option("--poll-seconds <seconds>","Capacity retry interval",{default:"900"}).option("--max-wait-minutes <minutes>","Stop waiting after this many minutes; 0 waits indefinitely",{default:"0"}).option("--json","Print structured JSON").action(async(e,t,n)=>{try{await tm(e,t,n)}catch(i){fe(i)}}),r.command("server:optimize <name>","Reconcile and verify a production Hetzner host").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Apply the reviewed host optimization plan").option("--confirm <name>","Exact server name required for mutation").option("--json","Print structured JSON").action(async(e,t)=>{try{await nm(e,t)}catch(n){fe(n)}}),r.command("server:monitoring <name>","Read historical Hetzner CPU, RAM, swap, disk, and network metrics").option("--env <environment>","Deployment environment",{default:"production"}).option("--range <duration>","Historical window from 5m to 24h",{default:"3h"}).option("--step <seconds>","Hetzner provider sample interval",{default:"60"}).option("--json","Print structured JSON").action(async(e,t)=>{try{await Xu(e,t)}catch(n){fe(n)}}),r.command("server:list","List fleet servers").option("--json","Print structured JSON").action(async(e)=>{try{await Ue(async(t)=>{let n=t.store.list(t.controlPlane.project.id);if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Provider ID","Provider / region","Status / trust","Roles","CPU / memory","Heartbeat"],n.map((i)=>[i.name,i.providerId??"—",`${i.provider} / ${i.region??"external"}`,`${i.status} / ${i.trustState}`,i.roles.join(","),`${i.capacity.cpu??0} / ${i.capacity.memoryBytes??0}`,i.heartbeatAt??"never"]))})}catch(t){fe(t)}}),r.command("server:create <name>","Enroll a provisioned or existing server").option("--provider <provider>","aws, hetzner, or ssh",{default:"ssh"}).option("--provider-id <id>","Stable provider identity").option("--endpoint <host>","SSH hostname or IP").option("--user <user>","Non-root SSH user",{default:"deploy"}).option("--credential-ref <ref>","Secret reference",{default:"secret://fleet/agent"}).option("--region <region>","Provider region").option("--roles <roles>","Comma-separated roles",{default:"application"}).option("--labels <labels>","Comma-separated key=value labels").action(async(e,t)=>{try{await Ue(async(n)=>{let i=t.provider;if(!["aws","hetzner","ssh"].includes(i)||!t.endpoint)throw Error("A supported --provider and --endpoint are required; provisioning and enrollment are separate operations.");let o=n.service.enroll({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,provider:i,providerId:t.providerId,endpoint:t.endpoint,sshUser:t.user??"deploy",credentialRef:t.credentialRef??"secret://fleet/agent",region:t.region,roles:String(t.roles??"application").split(","),labels:Object.fromEntries(String(t.labels??"").split(",").map((s)=>s.split("=")).filter((s)=>s[0]&&s[1]))});h(`Enrolled ${o.name} as ${o.id}; no remote mutation was performed.`)})}catch(n){fe(n)}}),r.command("server:validate <name>","Pin trust and validate a server").option("--accept-host-key <fingerprint>","Accept reviewed rotation").option("--json","Print JSON").action(async(e,t)=>{try{await Ue(async(n)=>{let i=yt(n,e);if(i=await n.service.test(i.id),i.trustState==="rotation_pending"){if(!t.acceptHostKey)throw Error(`Host key changed to ${i.pendingHostKey}; review it first.`);i=n.service.reviewHostKey(i.id,t.acceptHostKey)}let o=await n.service.validate(i.id);if(t.json)console.log(JSON.stringify(o.validation,null,2));else x(["Severity","Code","Finding","Remediation"],(o.validation?.findings??[]).map((s)=>[s.severity,s.code,s.message,s.remediation??"—"]));if(!o.validation?.valid)process.exitCode=1})}catch(n){fe(n)}}),r.command("server:bootstrap <name>","Preview or queue bootstrap").option("--apply","Apply reviewed plan").action(async(e,t)=>{try{await Ue(async(n)=>{let i=n.service.bootstrap(yt(n,e).id,!!t.apply);if(i.preview)x(["Step"],i.steps.map((o)=>[o]));else h(`Bootstrap queued: ${i.operation?.id}`)})}catch(n){fe(n)}}),r.command("server:rename <name> <new-name>","Rename a server in place, without recreating it").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Perform the reviewed rename plan").option("--json","Print structured JSON").action(async(e,t,n)=>{try{await rm(e,t,n)}catch(i){fe(i)}}),r.command("server:drain <name>","Drain without terminating").option("--complete","Mark movement complete").action(async(e,t)=>{try{await Ue(async(n)=>h(n.service.drain(yt(n,e).id,!!t.complete).status))}catch(n){fe(n)}}),r.command("server:uncordon <name>","Return a server to scheduling").action(async(e)=>{try{await Ue(async(t)=>{t.service.uncordon(yt(t,e).id),h(`Uncordoned ${e}.`)})}catch(t){fe(t)}}),r.command("server:archive <name>","Archive inventory without termination").option("--confirm <name>","Exact name").action(async(e,t)=>{try{await Ue(async(n)=>{n.service.archive(yt(n,e).id,t.confirm??""),h(`Archived ${e}; provider infrastructure was not terminated.`)})}catch(n){fe(n)}}),r.command("server:ssh <name>","Open strict SSH using the enrolled endpoint").action(async(e)=>{try{await Ue(async(t)=>{let n=yt(t,e);if(n.trustState!=="pinned")throw Error("Validate and pin the server host key first.");let i=Bun.spawn(["ssh","-p",String(n.sshPort),"-o","StrictHostKeyChecking=yes",`${n.sshUser}@${n.endpoint}`],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}),o=await i.exited;if(o)process.exitCode=o})}catch(t){fe(t)}});for(let[e,t]of[["server:logs <name>","Use the runtime log service"],["server:deploy <name>","Use immutable release deployment"],["server:reboot <name>","Reboot provider compute"],["server:destroy <name>","Terminate provider compute"],["server:recipe <name> <recipe>","Apply a server recipe"],["server:firewall:add <name> <rule>","Add firewall rule"],["server:firewall:list <name>","List firewall rules"],["server:firewall:remove <name> <rule>","Remove firewall rule"],["server:ssl:install <domain>","Install TLS"],["server:ssl:renew <domain>","Renew TLS"],["server:snapshot <name>","Create provider snapshot"],["server:snapshot:restore <name> <snapshot-id>","Restore provider snapshot"],["server:update <name>","Update OS packages"],["server:secure <name>","Apply OS hardening"]])r.command(e,t).action(async(...n)=>be(e.split(" ")[0],{target:String(n[0]??""),nextAction:"Use the dashboard capability view or the corresponding runtime, release, firewall, backup, or maintenance service."}))}async function im(){let r=await P(),e=q(process.cwd(),r),t=new La(e.store),n=new ja(t,new et(e.store));return{controlPlane:e,store:t,service:n}}async function dn(r){let e=await im();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}var Vn=(r)=>Object.fromEntries(String(r??"").split(",").map((e)=>e.split("=")).filter((e)=>e[0]&&e[1])),Oa=(r)=>Object.fromEntries(Object.entries(Vn(r)).map(([e,t])=>[e,Number(t)]));function Xr(r){r.command("pool:list","List capacity pools and reservations").option("--json","Print structured JSON").action(async(e)=>dn((t)=>{let n=t.store.listPools(t.controlPlane.project.id);if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Purpose","Backend","Region / arch","Status","Capacity","Concurrency"],n.map((i)=>[i.name,i.purpose,i.backend,`${i.region??"any"} / ${i.architecture??"any"}`,i.status,JSON.stringify(i.capacity),String(i.concurrency)]))})),r.command("pool:create <name>","Create a capacity pool").option("--purpose <purpose>","application, build, worker, monitoring, or backup",{default:"application"}).option("--backend <backend>","server, ecs, or asg",{default:"server"}).option("--capacity <values>","cpu=4,memoryBytes=8589934592,diskBytes=...").option("--region <region>","Required region").option("--arch <architecture>","Required architecture").option("--labels <labels>","Pool key=value labels").option("--selector <labels>","Required server labels").option("--max <count>","Maximum workloads",{default:"10"}).option("--concurrency <count>","Build concurrency",{default:"1"}).action(async(e,t)=>dn((n)=>{let{purpose:i,backend:o}=t;if(!["application","build","worker","monitoring","backup"].includes(i)||!["server","ecs","asg"].includes(o))throw Error("Unsupported pool purpose or backend.");let s=n.store.createPool({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,purpose:i,backend:o,region:t.region,architecture:t.arch,labels:Vn(t.labels),requiredServerLabels:Vn(t.selector),toleratedTaints:[],capacity:{cpu:0,memoryBytes:0,diskBytes:0,gpu:0,...Oa(t.capacity)},maxWorkloads:Number(t.max),costWeight:1,concurrency:Number(t.concurrency),ephemeralWorkspaces:!0,allowProductionSecrets:!1,status:"active"});h(`Created ${s.name} (${s.id}).`)})),r.command("pool:add-server <pool> <server>","Attach an enrolled server to a pool").action(async(e,t)=>dn((n)=>{let i=n.store.listPools(n.controlPlane.project.id).find((s)=>s.id===e||s.name===e),o=new et(n.controlPlane.store).list(n.controlPlane.project.id).find((s)=>s.id===t||s.name===t);if(!i||!o)throw Error("Pool or server was not found.");n.store.addMember(i.id,o.id),h(`Attached ${o.name} to ${i.name}.`)})),r.command("placement:explain","Explain eligible and rejected placement targets").option("--purpose <purpose>","Workload purpose",{default:"application"}).option("--resources <values>","Requested resources").option("--region <region>","Required region").option("--arch <architecture>","Required architecture").option("--labels <labels>","Required pool labels").option("--json","Print structured JSON").action(async(e)=>dn((t)=>{let n=t.service.explain(t.controlPlane.project.id,{purpose:e.purpose,resources:Oa(e.resources),region:e.region,architecture:e.arch,labels:Vn(e.labels)});if(e.json)console.log(JSON.stringify(n,null,2));else x(["Pool","Server","Eligible","Available","Fit / spread / cost","Reasons"],n.map((i)=>[i.poolName,i.serverId??"provider-managed",i.eligible?"yes":"no",JSON.stringify(i.available),`${i.score.fit.toFixed(2)} / ${i.score.spread} / ${i.score.cost}`,i.reasons.join("; ")||"eligible"]))})),r.command("pool:drain <pool>","Move stateless placements and block stateful placements").action(async(e)=>dn((t)=>{let n=t.store.listPools(t.controlPlane.project.id).find((o)=>o.id===e||o.name===e);if(!n)throw Error("Pool was not found.");let i=t.service.drainPool(n.id);h(`Draining ${n.name}: moved ${i.moved.length}, blocked ${i.blocked.length} stateful placement(s).`)}))}async function om(){let r=await P(),e=q(process.cwd(),r),t=new Fa(e.store),n=new Ma(t);return{controlPlane:e,store:t,service:n}}async function It(r){let e=await om();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}var un=(r,e)=>{let t=r.store.list(r.controlPlane.project.id).find((n)=>n.id===e||n.name===e);if(!t)throw Error("Regional topology was not found.");return t},sm=(r)=>r.split(",").map((e,t)=>{let[n,i="aws"]=e.split(":");return{region:String(n),provider:i,role:t===0?"primary":"secondary"}}),am=(r)=>{let e=new Set(["s3","dynamodb","secrets"]),t=r.split(",").map((i)=>i.trim()),n=t.find((i)=>!e.has(i));if(n)throw Error(`Unsupported replication kind: ${n}`);return t};function ei(r){r.command("region:list","List regional topology, traffic, and replication health").option("--json","Print structured JSON").action(async(e)=>It((t)=>{let n=t.store.list(t.controlPlane.project.id).map((i)=>({topology:i,targets:t.store.targets(i.id),channels:t.store.channels(i.id),route:t.store.route(i.id),executions:t.store.executions(i.id)}));if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Hostname","Active","Status","Revision","Replication"],n.map((i)=>[i.topology.name,i.topology.hostname,i.topology.activeRegion,i.topology.status,i.topology.revision??"—",`${i.channels.filter((o)=>o.status==="in_sync").length}/${i.channels.length} in sync`]))})),r.command("region:create <name>","Create a persistent multi-region topology").option("--hostname <hostname>","Traffic hostname").option("--regions <regions>","Comma-separated region[:provider], primary first").option("--replicate <kinds>","s3,dynamodb,secrets",{default:"s3,dynamodb,secrets"}).option("--max-lag <seconds>","Maximum failover lag",{default:"30"}).option("--cdn","Enable CloudFront edge state").option("--waf","Enable WAF edge state").action(async(e,t)=>It((n)=>{if(!t.hostname||!t.regions)throw Error("--hostname and --regions are required.");let i=n.service.create({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,hostname:t.hostname,regions:sm(t.regions),trafficPolicy:"active_passive",dataPolicy:{replicate:am(String(t.replicate)),maxLagSeconds:Number(t.maxLag),retainOnDestroy:!0},dnsProvider:"route53",cdnEnabled:!!t.cdn,wafEnabled:!!t.waf});h(`Created ${i.name} (${i.id}); run region:rollout with an immutable manifest.`)})),r.command("region:plan <name> <operation>","Preview exact regional execution steps").option("--target <region>","Failover or failback region").action(async(e,t,n)=>It((i)=>x(["Step"],i.service.plan(un(i,e).id,t,n.target).map((o)=>[o])))),r.command("region:rollout <name> <revision>","Queue secondary-first regional stack rollout").option("--manifest <file>","Regional stack manifest JSON").action(async(e,t,n)=>It(async(i)=>{let o=n.manifest?await Bun.file(n.manifest).json():{},s=i.service.enqueue({topologyId:un(i,e).id,kind:"rollout",revision:t,manifest:o});h(`Regional rollout queued: ${s.operation.id}`)})),r.command("region:failover <name> <region>","Queue health and RPO-gated failover").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"failover",targetRegion:t});h(`Failover queued: ${i.operation.id}`)})),r.command("region:failback <name> <region>","Queue health and RPO-gated failback").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"failback",targetRegion:t});h(`Failback queued: ${i.operation.id}`)})),r.command("region:destroy <name>","Drain traffic and delete regional stacks while retaining data").option("--confirm <name>","Exact topology name").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"destroy",confirmation:t.confirm});h(`Regional teardown queued: ${i.operation.id}; replicated data remains retained.`)}))}var mn="0.11.3";async function lm(){let r=await P(),e=q(process.cwd(),r),t=new Ba(e.store),n=new _a(t),i=["releases","artifacts","operations","logs","previews","snapshots","orphaned_resources"].map((o)=>new za(o,e.store,e.project.id,e.organization.id));return{controlPlane:e,store:t,service:n,cleanup:i}}async function tt(r){let e=await lm();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}function ti(r){r.command("maintenance:list","List updates, campaigns, cleanup plans, and DR drills").option("--json","Print structured JSON").action(async(e)=>tt((t)=>{let n={manifests:t.store.manifests(t.controlPlane.organization.id),windows:t.store.windows(t.controlPlane.project.id),campaigns:t.store.campaigns(t.controlPlane.project.id),cleanups:t.store.cleanups(t.controlPlane.project.id),drills:t.store.drills(t.controlPlane.project.id)},i=[...n.manifests.map((o)=>["update",o.version,o.compatibility.compatible?"compatible":"blocked",o.compatibility.reasons.join("; ")||o.channel]),...n.campaigns.map((o)=>["upgrade",o.id,o.status,o.currentStage??o.fromVersion]),...n.cleanups.map((o)=>["cleanup",o.kind,o.status,`${o.candidates.length} items / ${o.estimatedBytes} bytes`]),...n.drills.map((o)=>["DR drill",o.scenario,o.status,o.isolatedTarget])];if(e.json)console.log(JSON.stringify(n,null,2));else x(["Area","Name / version","Status","Detail"],i)})),r.command("maintenance:key:add <name> <pem>","Trust an Ed25519 update signing key").action(async(e,t)=>tt(async(n)=>{let i=n.store.addTrustedKey({organizationId:n.controlPlane.organization.id,name:e,publicKeyPem:await Bun.file(t).text()});h(`Trusted ${i.name}: ${i.fingerprint}`)})),r.command("maintenance:manifest <document> <signature> <key>","Verify and import a signed update manifest").action(async(e,t,n)=>tt(async(i)=>{let o=await Bun.file(e).json(),s=(await Bun.file(t).text()).trim(),c=i.store.importManifest({organizationId:i.controlPlane.organization.id,keyId:n,document:o,signature:s,current:{schemaVersion:i.controlPlane.store.health().schemaVersion,currentVersion:mn,platform:process.platform,architecture:process.arch}});h(`Verified ${c.version}; ${c.compatibility.compatible?"compatible":"blocked: "+c.compatibility.reasons.join("; ")}`)})),r.command("maintenance:window <name> <schedule>","Create an approved maintenance window").option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--minutes <minutes>","Duration",{default:"60"}).action(async(e,t,n)=>tt((i)=>{let o=i.store.createWindow({projectId:i.controlPlane.project.id,name:e,schedule:t,timezone:n.timezone,durationMinutes:Number(n.minutes),allowedOperations:["platform.upgrade"],requireApproval:!0});h(`Created ${o.name} (${o.id}).`)})),r.command("platform:upgrade <manifest> <backup>","Plan and queue a signed staged platform update").option("--strategy <strategy>","canary or rolling",{default:"canary"}).option("--batch <count>","Rolling batch size",{default:"1"}).action(async(e,t,n)=>tt((i)=>{let o=new et(i.controlPlane.store).list(i.controlPlane.project.id).filter((l)=>l.status==="ready").map((l)=>({serverId:l.id,target:l.name,previousVersion:l.bootstrapVersion??mn}));if(!o.length)o.push({target:"control-plane",previousVersion:mn});let s=i.store.createCampaign({projectId:i.controlPlane.project.id,manifestId:e,fromVersion:mn,strategy:n.strategy==="rolling"?"rolling":"canary",batchSize:Number(n.batch),healthGate:{kind:"platform"},backupId:t,targets:o}),c=i.service.enqueueCampaign(s.id);h(`Staged upgrade queued: ${c.operation.id}`)})),r.command("cleanup:preview <kind>","Discover a bounded cleanup candidate set without deleting").option("--older-than <days>","Minimum age",{default:"30"}).action(async(e,t)=>tt(async(n)=>{let i=await n.service.previewCleanup({projectId:n.controlPlane.project.id,kind:e,criteria:{olderThanDays:Number(t.olderThan)}},n.cleanup);x(["ID","Candidate","Bytes"],i.candidates.map((o)=>[o.id,o.label,String(o.bytes)])),a(`Apply with: cloud cleanup:apply ${i.id} --confirm "DELETE ${i.candidates.length} ${i.kind}"`)})),r.command("cleanup:apply <plan>","Apply an unchanged cleanup preview").option("--confirm <text>","Exact preview confirmation").action(async(e,t)=>tt((n)=>{let i=n.service.enqueueCleanup(e,String(t.confirm??""));h(`Cleanup queued: ${i.operation.id}`)})),r.command("dr:plan <scenario> <target>","Plan an isolated disaster-recovery drill").option("--rpo <minutes>","RPO objective",{default:"60"}).option("--rto <minutes>","RTO objective",{default:"30"}).option("--recovery-point <id>","Recovery point").option("--topology <id>","Regional topology").action(async(e,t,n)=>tt((i)=>{let o=i.store.createDrill({projectId:i.controlPlane.project.id,scenario:e,isolatedTarget:t,expectedRpoMinutes:Number(n.rpo),expectedRtoMinutes:Number(n.rto),recoveryPointId:n.recoveryPoint,topologyId:n.topology});h(`Planned isolated drill ${o.id}.`)})),r.command("dr:run <drill>","Queue an isolated disaster-recovery drill").action(async(e)=>tt((t)=>{let n=t.service.enqueueDrill(e);h(`DR drill queued: ${n.operation.id}`)}))}async function ni(){try{return(await P()).project.region||process.env.AWS_REGION||"us-east-1"}catch{return process.env.AWS_REGION||"us-east-1"}}function ri(r){r.command("function:list","List all Lambda functions").option("--region <region>","AWS region").action(async(e)=>{w("Lambda Functions");let t=e?.region||await ni(),n=new nt(t);try{let{Functions:i=[]}=await n.listFunctions({MaxItems:50});if(!i.length){a("No functions found");return}x(["Name","Runtime","Memory","Timeout","Last Modified"],i.map((o)=>[o.FunctionName??"-",o.Runtime??"-",o.MemorySize?`${o.MemorySize} MB`:"-",o.Timeout?`${o.Timeout}s`:"-",o.LastModified??"-"]))}catch(i){p(`Failed to list functions: ${i.message}`),process.exitCode=1}}),r.command("function:invoke <name>","Invoke a Lambda function").option("--payload <json>","Event payload as JSON",{default:"{}"}).option("--async","Fire-and-forget (Event invocation)").option("--region <region>","AWS region").action(async(e,t)=>{w(`Invoking ${e}`);let n=t?.region||await ni(),i=new nt(n),o=new A("Invoking function...");o.start();try{let s=await i.invoke({FunctionName:e,InvocationType:t?.async?"Event":"RequestResponse",Payload:t?.payload||"{}",LogType:t?.async?"None":"Tail"});if(s.FunctionError){if(o.fail(`Function returned an error (${s.FunctionError})`),s.Payload)a(s.Payload);process.exitCode=1;return}if(o.succeed(`Invoked (status ${s.StatusCode})`),s.Payload)a(`
318
+ `)}async function Zu(){let r=await P(),e=q(process.cwd(),r),t=new et(e.store),n=new Ta(t,[new Un("aws"),new Un("hetzner"),new Un("ssh")]);return{controlPlane:e,store:t,service:n}}async function Ue(r){let e=await Zu();try{return await r(e)}finally{e.controlPlane.store.close()}}var yt=(r,e)=>{let t=r.store.list(r.controlPlane.project.id).find((n)=>n.id===e||n.name===e);if(!t)throw Error(`Server ${e} was not found.`);return t},fe=(r)=>{p(r instanceof Error?r.message:String(r)),process.exitCode=1};function Zr(r,e){let t=Number.parseInt(r??"",10);return Number.isFinite(t)&&t>=0?t:e}function Qu(r="3h"){let e=r.match(/^(\d+)(m|h)$/);if(!e)throw Error("--range must use minutes or hours, for example 30m or 3h.");let t=Number(e[1])*(e[2]==="h"?3600000:60000);if(t<300000||t>86400000)throw Error("--range must be between 5 minutes and 24 hours.");return t}function cn(r,e="%"){return r==null?"unavailable":`${r.toFixed(1)}${e}`}async function Xu(r,e){let t=await P();if(t.cloud?.provider!=="hetzner")throw Error("server:monitoring currently requires the Hetzner provider.");let n=e.env??process.env.CLOUD_ENV??"production",i=Ve(t,n),o=await Rt(i);if(!o?.serverId)throw Error(`No pinned Hetzner server was found for ${i}.`);if(![o.serverName,String(o.serverId),o.stackName].includes(r))throw Error(`Refusing to read ${r}: project state is pinned to ${o.serverName}.`);let s=ln(t),c=new Vt({apiToken:Wt(s.apiToken,t)}),l=await c.getServer(o.serverId),d=o.publicIp??l.public_net.ipv4?.ip;if(!d)throw Error(`Server ${l.name} has no public IPv4 address.`);let u=new Date,m=new Date(u.getTime()-Qu(e.range)),f=Math.min(3600,Math.max(60,Zr(e.step,60))),y=await ha({client:c,serverId:l.id,cores:Math.max(1,Number(l.server_type.cores)||1),from:m,to:u,step:f,remote:{host:d,user:o.sshUser??s.sshUser,identityFile:s.sshPrivateKeyPath,connectTimeoutSec:15}});if(e.json){console.log(JSON.stringify({schemaVersion:1,server:l.name,result:y},null,2));return}x(["Server","Window","CPU average / peak","RAM average / peak","Minimum available","Swap peak"],[[l.name,`${y.from} to ${y.to}`,`${cn(y.summary.cpuAveragePercent)} / ${cn(y.summary.cpuPeakPercent)}`,`${cn(y.summary.memoryAveragePercent)} / ${cn(y.summary.memoryPeakPercent)}`,y.summary.memoryMinimumAvailableBytes==null?"unavailable":`${(y.summary.memoryMinimumAvailableBytes/1073741824).toFixed(2)} GB`,cn(y.summary.swapPeakPercent)]])}function Na(r){let e=r.targetType.prices?.find((t)=>t.location===r.location);return{serverId:r.serverId,serverName:r.serverName,location:r.location,from:r.currentType.name,to:r.targetType.name,capacityAvailable:r.capacityAvailable,alreadyComplete:r.alreadyComplete,upgradeDisk:r.upgradeDisk,target:{cores:r.targetType.cores,memoryGb:r.targetType.memory,diskGb:r.targetType.disk,architecture:r.targetType.architecture,cpuType:r.targetType.cpu_type},price:e?{hourlyNet:e.price_hourly.net,hourlyGross:e.price_hourly.gross,monthlyNet:e.price_monthly.net,monthlyGross:e.price_monthly.gross}:void 0}}function em(r){let e=Na(r);x(["Server","Location","Resize","Capacity","CPU / RAM / disk","Monthly"],[[`${e.serverName} (${e.serverId})`,e.location,`${e.from} -> ${e.to}`,e.capacityAvailable?"available":"waiting",`${e.target.cores??0} / ${e.target.memoryGb??0} GB / ${e.target.diskGb??0} GB`,e.price?.monthlyGross??e.price?.monthlyNet??"not reported"]])}async function tm(r,e,t){let n=await P();if(n.cloud?.provider!=="hetzner")throw Error("server:resize currently requires the Hetzner provider.");let i=t.env??process.env.CLOUD_ENV??"production",o=Ve(n,i),s=await Rt(o),c=ln(n),l=new Vt({apiToken:Wt(c.apiToken,n)}),u=s&&(r===s.serverName||r===String(s.serverId)||r===s.stackName)?await l.getServer(s.serverId):(await l.listServers()).find((E)=>E.name===r||String(E.id)===r);if(!u)throw Error(`Hetzner server ${r} was not found.`);if(s?.serverId&&s.serverId!==u.id)throw Error(`Refusing to resize ${r}: project state is pinned to server ${s.serverId}.`);let m=wa(e),f=t.upgradeDisk!==!1,y=await Jr(l,u.id,m,f);if(t.json)console.log(JSON.stringify({schemaVersion:1,plan:Na(y)},null,2));else em(y);if(!t.apply){if(!t.json)a(`Preview only. Re-run with --apply --confirm ${u.name} to execute.`);return}if(t.confirm!==u.name)throw Error(`Resize requires --confirm ${u.name}.`);let S=s?.publicIp??u.public_net.ipv4?.ip;if(!S)throw Error(`Server ${u.name} has no public IPv4 address for verification.`);let g={host:S,user:s?.sshUser??c.sshUser,identityFile:c.sshPrivateKeyPath,connectTimeoutSec:15,sshTimeoutMs:600000,routeTimeoutMs:20000},b=await Ea(o),I=new Date().toISOString(),v={schemaVersion:1,operationId:b?.targetType===m&&b.serverId===u.id?b.operationId:crypto.randomUUID(),stackName:o,serverId:u.id,serverName:u.name,sourceType:y.currentType.name,targetType:m,upgradeDisk:f,phase:"planning",status:"running",attempts:b?.targetType===m?b.attempts:0,startedAt:b?.targetType===m?b.startedAt:I,updatedAt:I,manifest:b?.targetType===m?b.manifest:void 0};await ht(v);let C=Zr(t.pollSeconds,900)*1000,k=Zr(t.maxWaitMinutes,0)*60000,T=Date.now();for(;;){let E=await Jr(l,u.id,m,f);if(!E.capacityAvailable&&!E.alreadyComplete){if(v={...v,phase:"waiting-capacity",status:"waiting-capacity",updatedAt:new Date().toISOString()},await ht(v),!t.wait){if(!t.json)a(`${m} has no capacity in ${E.location}. The server remains running and unchanged. Use --wait to poll safely.`);return}if(k>0&&Date.now()-T>=k)throw Error(`Timed out waiting for ${m} capacity in ${E.location}.`);if(!t.json)a(`Waiting ${Math.round(C/1000)} seconds for ${m} capacity...`);await Bun.sleep(C);continue}let D=await Da(o);try{let N=await(async()=>{try{return v={...v,status:"running",attempts:v.attempts+1,updatedAt:new Date().toISOString()},await ht(v),await $a({client:l,serverId:u.id,targetType:m,upgradeDisk:f,resumeFromOff:v.phase==="shutting-down"||v.phase==="resizing"||v.phase==="powering-on"||v.phase==="verifying",manifest:v.manifest,hooks:{preflight:async()=>{let _=await Ca(g);return v={...v,manifest:_,updatedAt:new Date().toISOString()},await ht(v),_},afterBoot:async(_,B,V,K)=>Aa(g,B,V,K),onPhase:async(_,B)=>{if(v={...v,phase:_,status:_==="waiting-capacity"?"waiting-capacity":_==="complete"?"completed":"running",updatedAt:new Date().toISOString()},await ht(v),!t.json)a(`${_}${B?` ${JSON.stringify(B)}`:""}`)}}})}finally{await D()}})();if(v={...v,status:N.status==="completed"||N.status==="already-complete"?"completed":N.status==="recovered"?"recovered":"waiting-capacity",lastError:N.status==="recovered"?N.error:void 0,updatedAt:new Date().toISOString()},await ht(v),t.json)console.log(JSON.stringify({schemaVersion:1,result:N},null,2));else if(N.status==="completed"||N.status==="already-complete")h(`${u.name} is on ${m} and passed full post-boot verification.`);else if(N.status==="recovered"&&N.retryable&&t.wait){a(`Capacity race recovered safely. Waiting ${Math.round(C/1000)} seconds before retry.`),await Bun.sleep(C);continue}else if(N.status==="recovered")throw Error(`Resize did not complete and the original server was recovered: ${N.error}`);return}catch(N){throw v={...v,status:"failed",lastError:N instanceof Error?N.message:String(N),updatedAt:new Date().toISOString()},await ht(v),N}}}async function nm(r,e){let t=await P();if(t.cloud?.provider!=="hetzner")throw Error("server:optimize currently requires the Hetzner provider.");let n=e.env??process.env.CLOUD_ENV??"production",i=Ve(t,n),o=await Rt(i);if(!o?.serverId)throw Error(`No pinned Hetzner server was found for ${i}.`);if(![o.serverName,String(o.serverId),o.stackName].includes(r))throw Error(`Refusing to optimize ${r}: project state is pinned to ${o.serverName}.`);let s=ln(t),c=new Vt({apiToken:Wt(s.apiToken,t)}),l=await c.getServer(o.serverId),d=o.publicIp??l.public_net.ipv4?.ip;if(!d)throw Error(`Server ${l.name} has no public IPv4 address.`);let u=Ra(t);if(e.json)console.log(JSON.stringify({schemaVersion:1,server:l.name,plan:u},null,2));else x(["Server","Firewall ports","Monitoring","Auto updates","Swap","SSH passwords"],[[l.name,u.firewallPorts.join(","),u.monitoring?"on":"off",u.autoUpdates?"on":"off",`${u.swapGb} GB`,"disabled"]]);if(!e.apply){if(!e.json)a(`Preview only. Re-run with --apply --confirm ${l.name} to execute.`);return}if(e.confirm!==l.name)throw Error(`Optimization requires --confirm ${l.name}.`);let m={host:d,user:o.sshUser??s.sshUser,identityFile:s.sshPrivateKeyPath,connectTimeoutSec:15},f=await Yr(m),y=f.routeProbes.filter((V)=>!V.ok),S=t.infrastructure?.compute,g=Hr(S)?new Set(["nginx.service","bun-gateway.service","ts-cloud-nginx.service"]):new Set,b=f.failedUnits.filter((V)=>!g.has(V));if(b.length>0)throw Error(`Preflight found failed services: ${b.join(", ")}`);if(y.length>0)throw Error(`Preflight found unhealthy routes: ${y.map((V)=>V.domain).join(", ")}`);let I=Hr(S)?[]:Object.values(t.sites??{}).flatMap((V)=>typeof V?.port==="number"&&![80,443].includes(V.port)?[V.port]:[]),v=ba({allowSsh:S?.allowSsh!==!1,sitePorts:I,allowedPorts:S?.firewall?.allowedPorts}),C=(o.firewallId?(await c.listFirewalls()).find((V)=>V.id===o.firewallId):void 0)??(await c.listFirewalls()).find((V)=>V.name===`${t.project.slug}-${n}-app-fw`);if(!C)throw Error(`Managed firewall for ${l.name} was not found.`);let k=await c.setFirewallRules(C.id,v);await Promise.all(k.map((V)=>c.waitForAction(V.id))),await Ia(t,m);let[T,E]=await Promise.all([Yr(m),Pa(m)]),D=xa(u,E),N=ka(f,T),_=T.routeProbes.filter((V)=>!V.ok);if(N.stoppedServices.length>0)D.push(`services stopped: ${N.stoppedServices.join(", ")}`);if(N.changedRouteFragments.length>0)D.push(`route fragments changed: ${N.changedRouteFragments.join(", ")}`);if(N.missingRouteIds.length>0)D.push(`routes disappeared: ${N.missingRouteIds.join(", ")}`);if(N.changedReleaseLinks.length>0)D.push(`release links changed: ${N.changedReleaseLinks.join(", ")}`);if(N.missingData.length>0)D.push(`databases or volumes disappeared: ${N.missingData.join(", ")}`);if(_.length>0)D.push(`unhealthy routes: ${_.map((V)=>V.domain).join(", ")}`);if(D.length>0)throw Error(`Host optimization verification failed: ${D.join("; ")}`);let B={serverId:l.id,serverName:l.name,services:T.runningServices.length,routes:T.routeIds.length,healthyDomains:T.routeProbes.length,releaseLinks:T.releaseLinks.length,databasesAndVolumes:T.dataCatalog.length,report:E};if(e.json)console.log(JSON.stringify({schemaVersion:1,result:B},null,2));else h(`${l.name} passed full host, rpx route, service, release, and data verification.`)}async function rm(r,e,t){let n=await P(),i=t.env??"production",o=Ve(n,i);await Ue(async(s)=>{let c=yt(s,r),l=s.store.list(s.controlPlane.project.id,!0),d=c.provider==="hetzner"&&c.providerId?(()=>{try{let C=ln(n);return new Vt({apiToken:Wt(C.apiToken,n)})}catch{return null}})():null,u=c.providerId?Number(c.providerId):Number.NaN,m=await Rt(o),f=m!=null&&m.serverId===u,y=new Mn,S=c.trustState==="pinned",g={takenNames:async()=>{let C=l.map((k)=>k.name);if(d)C.push(...(await d.listServers()).map((k)=>k.name));return C},inventoryName:()=>s.store.get(c.id)?.name??c.name,renameInventory:(C)=>{s.store.update(c.id,{name:C})},...d&&Number.isFinite(u)?{providerName:async()=>(await d.getServer(u)).name,renameProvider:async(C)=>{await d.renameServer(u,C)}}:{},...f?{stateName:async()=>(await Rt(o))?.serverName,writeStateName:async(C)=>{let k=await Rt(o);if(k)await Sa(o,{...k,serverName:C})}}:{},...S?{remoteHostname:async()=>{let C=await y.exec(c,"hostname");return C.code===0?C.stdout.trim():void 0},setRemoteHostname:async(C)=>{let k=await y.exec(c,va(C));if(k.code!==0)throw Error(k.stderr.trim()||`hostname exited ${k.code}`)}}:{}},b=await ya(c.name,e,g),I=await On(b);if(!t.apply){let C=[d?"":"provider record (no provider id or no API token)",f?"":"local state pin (none points at this server)",S?"":"box hostname (host key is not pinned — run server:validate first)"].filter(Boolean);if(t.json){console.log(JSON.stringify({schemaVersion:1,operation:b.operation,target:b.target,rename:{from:c.name,to:e},notCovered:C,steps:I.map((k)=>({id:k.step.id,title:k.step.title,state:k.state,change:k.step.change,reason:k.reason}))},null,2));return}for(let k of Ln(b,I))console.log(k);for(let k of C)console.log(` not covered: ${k}`);console.log(" Re-run with --apply to perform it.");return}let v=await jn(b,I,{log:(C)=>a(C),audit:(C)=>s.controlPlane.store.appendEvent({projectId:s.controlPlane.project.id,resourceId:c.resourceId,type:`${C.operation}.${C.step}.${C.state}`,level:C.state==="failed"?"error":"info",payload:{target:C.target,renameTo:e,...C.error?{error:C.error}:{}}})});if(t.json)console.log(JSON.stringify({schemaVersion:1,outcome:v},null,2));if(!v.success){let C=v.steps.find((k)=>k.state==="failed");throw Error(`${b.operation} stopped at '${C?.title}': ${C?.error}. Earlier steps stayed applied; re-run the same command to continue from here.`)}if(!t.json)h(`Renamed ${c.name} to ${e}.`)})}function Qr(r){r.command("capabilities [server]","Show provider and target operation support").option("--json","Print structured JSON").action(async(e,t)=>{try{await Ue(async(n)=>{let i=e?[yt(n,e)]:n.store.list(n.controlPlane.project.id),o=i.flatMap((s)=>Object.entries(s.capabilities).map(([c,l])=>({server:s.name,provider:s.provider,action:c,...l})));if(t.json)console.log(JSON.stringify({schemaVersion:1,targets:o},null,2));else x(["Server","Provider","Action","Support","Explanation"],o.map((s)=>[s.server,s.provider,s.action,s.supported?"supported":"unsupported",s.reason??"—"]))})}catch(n){fe(n)}}),r.command("server:resize <name> <type>","Safely resize Hetzner compute with full host verification").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Execute the reviewed resize plan").option("--confirm <name>","Exact server name required for mutation").option("--upgrade-disk","Permanently expand the root disk to the target size",{default:!0}).option("--wait","Poll capacity without taking the server offline until placement is available").option("--poll-seconds <seconds>","Capacity retry interval",{default:"900"}).option("--max-wait-minutes <minutes>","Stop waiting after this many minutes; 0 waits indefinitely",{default:"0"}).option("--json","Print structured JSON").action(async(e,t,n)=>{try{await tm(e,t,n)}catch(i){fe(i)}}),r.command("server:optimize <name>","Reconcile and verify a production Hetzner host").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Apply the reviewed host optimization plan").option("--confirm <name>","Exact server name required for mutation").option("--json","Print structured JSON").action(async(e,t)=>{try{await nm(e,t)}catch(n){fe(n)}}),r.command("server:monitoring <name>","Read historical Hetzner CPU, RAM, swap, disk, and network metrics").option("--env <environment>","Deployment environment",{default:"production"}).option("--range <duration>","Historical window from 5m to 24h",{default:"3h"}).option("--step <seconds>","Hetzner provider sample interval",{default:"60"}).option("--json","Print structured JSON").action(async(e,t)=>{try{await Xu(e,t)}catch(n){fe(n)}}),r.command("server:list","List fleet servers").option("--json","Print structured JSON").action(async(e)=>{try{await Ue(async(t)=>{let n=t.store.list(t.controlPlane.project.id);if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Provider ID","Provider / region","Status / trust","Roles","CPU / memory","Heartbeat"],n.map((i)=>[i.name,i.providerId??"—",`${i.provider} / ${i.region??"external"}`,`${i.status} / ${i.trustState}`,i.roles.join(","),`${i.capacity.cpu??0} / ${i.capacity.memoryBytes??0}`,i.heartbeatAt??"never"]))})}catch(t){fe(t)}}),r.command("server:create <name>","Enroll a provisioned or existing server").option("--provider <provider>","aws, hetzner, or ssh",{default:"ssh"}).option("--provider-id <id>","Stable provider identity").option("--endpoint <host>","SSH hostname or IP").option("--user <user>","Non-root SSH user",{default:"deploy"}).option("--credential-ref <ref>","Secret reference",{default:"secret://fleet/agent"}).option("--region <region>","Provider region").option("--roles <roles>","Comma-separated roles",{default:"application"}).option("--labels <labels>","Comma-separated key=value labels").action(async(e,t)=>{try{await Ue(async(n)=>{let i=t.provider;if(!["aws","hetzner","ssh"].includes(i)||!t.endpoint)throw Error("A supported --provider and --endpoint are required; provisioning and enrollment are separate operations.");let o=n.service.enroll({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,provider:i,providerId:t.providerId,endpoint:t.endpoint,sshUser:t.user??"deploy",credentialRef:t.credentialRef??"secret://fleet/agent",region:t.region,roles:String(t.roles??"application").split(","),labels:Object.fromEntries(String(t.labels??"").split(",").map((s)=>s.split("=")).filter((s)=>s[0]&&s[1]))});h(`Enrolled ${o.name} as ${o.id}; no remote mutation was performed.`)})}catch(n){fe(n)}}),r.command("server:validate <name>","Pin trust and validate a server").option("--accept-host-key <fingerprint>","Accept reviewed rotation").option("--json","Print JSON").action(async(e,t)=>{try{await Ue(async(n)=>{let i=yt(n,e);if(i=await n.service.test(i.id),i.trustState==="rotation_pending"){if(!t.acceptHostKey)throw Error(`Host key changed to ${i.pendingHostKey}; review it first.`);i=n.service.reviewHostKey(i.id,t.acceptHostKey)}let o=await n.service.validate(i.id);if(t.json)console.log(JSON.stringify(o.validation,null,2));else x(["Severity","Code","Finding","Remediation"],(o.validation?.findings??[]).map((s)=>[s.severity,s.code,s.message,s.remediation??"—"]));if(!o.validation?.valid)process.exitCode=1})}catch(n){fe(n)}}),r.command("server:bootstrap <name>","Preview or queue bootstrap").option("--apply","Apply reviewed plan").action(async(e,t)=>{try{await Ue(async(n)=>{let i=n.service.bootstrap(yt(n,e).id,!!t.apply);if(i.preview)x(["Step"],i.steps.map((o)=>[o]));else h(`Bootstrap queued: ${i.operation?.id}`)})}catch(n){fe(n)}}),r.command("server:rename <name> <new-name>","Rename a server in place, without recreating it").option("--env <environment>","Deployment environment",{default:"production"}).option("--apply","Perform the reviewed rename plan").option("--json","Print structured JSON").action(async(e,t,n)=>{try{await rm(e,t,n)}catch(i){fe(i)}}),r.command("server:drain <name>","Drain without terminating").option("--complete","Mark movement complete").action(async(e,t)=>{try{await Ue(async(n)=>h(n.service.drain(yt(n,e).id,!!t.complete).status))}catch(n){fe(n)}}),r.command("server:uncordon <name>","Return a server to scheduling").action(async(e)=>{try{await Ue(async(t)=>{t.service.uncordon(yt(t,e).id),h(`Uncordoned ${e}.`)})}catch(t){fe(t)}}),r.command("server:archive <name>","Archive inventory without termination").option("--confirm <name>","Exact name").action(async(e,t)=>{try{await Ue(async(n)=>{n.service.archive(yt(n,e).id,t.confirm??""),h(`Archived ${e}; provider infrastructure was not terminated.`)})}catch(n){fe(n)}}),r.command("server:ssh <name>","Open strict SSH using the enrolled endpoint").action(async(e)=>{try{await Ue(async(t)=>{let n=yt(t,e);if(n.trustState!=="pinned")throw Error("Validate and pin the server host key first.");let i=Bun.spawn(["ssh","-p",String(n.sshPort),"-o","StrictHostKeyChecking=yes",`${n.sshUser}@${n.endpoint}`],{stdin:"inherit",stdout:"inherit",stderr:"inherit"}),o=await i.exited;if(o)process.exitCode=o})}catch(t){fe(t)}});for(let[e,t]of[["server:logs <name>","Use the runtime log service"],["server:deploy <name>","Use immutable release deployment"],["server:reboot <name>","Reboot provider compute"],["server:destroy <name>","Terminate provider compute"],["server:recipe <name> <recipe>","Apply a server recipe"],["server:firewall:add <name> <rule>","Add firewall rule"],["server:firewall:list <name>","List firewall rules"],["server:firewall:remove <name> <rule>","Remove firewall rule"],["server:ssl:install <domain>","Install TLS"],["server:ssl:renew <domain>","Renew TLS"],["server:snapshot <name>","Create provider snapshot"],["server:snapshot:restore <name> <snapshot-id>","Restore provider snapshot"],["server:update <name>","Update OS packages"],["server:secure <name>","Apply OS hardening"]])r.command(e,t).action(async(...n)=>be(e.split(" ")[0],{target:String(n[0]??""),nextAction:"Use the dashboard capability view or the corresponding runtime, release, firewall, backup, or maintenance service."}))}async function im(){let r=await P(),e=q(process.cwd(),r),t=new La(e.store),n=new ja(t,new et(e.store));return{controlPlane:e,store:t,service:n}}async function dn(r){let e=await im();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}var Vn=(r)=>Object.fromEntries(String(r??"").split(",").map((e)=>e.split("=")).filter((e)=>e[0]&&e[1])),Oa=(r)=>Object.fromEntries(Object.entries(Vn(r)).map(([e,t])=>[e,Number(t)]));function Xr(r){r.command("pool:list","List capacity pools and reservations").option("--json","Print structured JSON").action(async(e)=>dn((t)=>{let n=t.store.listPools(t.controlPlane.project.id);if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Purpose","Backend","Region / arch","Status","Capacity","Concurrency"],n.map((i)=>[i.name,i.purpose,i.backend,`${i.region??"any"} / ${i.architecture??"any"}`,i.status,JSON.stringify(i.capacity),String(i.concurrency)]))})),r.command("pool:create <name>","Create a capacity pool").option("--purpose <purpose>","application, build, worker, monitoring, or backup",{default:"application"}).option("--backend <backend>","server, ecs, or asg",{default:"server"}).option("--capacity <values>","cpu=4,memoryBytes=8589934592,diskBytes=...").option("--region <region>","Required region").option("--arch <architecture>","Required architecture").option("--labels <labels>","Pool key=value labels").option("--selector <labels>","Required server labels").option("--max <count>","Maximum workloads",{default:"10"}).option("--concurrency <count>","Build concurrency",{default:"1"}).action(async(e,t)=>dn((n)=>{let{purpose:i,backend:o}=t;if(!["application","build","worker","monitoring","backup"].includes(i)||!["server","ecs","asg"].includes(o))throw Error("Unsupported pool purpose or backend.");let s=n.store.createPool({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,purpose:i,backend:o,region:t.region,architecture:t.arch,labels:Vn(t.labels),requiredServerLabels:Vn(t.selector),toleratedTaints:[],capacity:{cpu:0,memoryBytes:0,diskBytes:0,gpu:0,...Oa(t.capacity)},maxWorkloads:Number(t.max),costWeight:1,concurrency:Number(t.concurrency),ephemeralWorkspaces:!0,allowProductionSecrets:!1,status:"active"});h(`Created ${s.name} (${s.id}).`)})),r.command("pool:add-server <pool> <server>","Attach an enrolled server to a pool").action(async(e,t)=>dn((n)=>{let i=n.store.listPools(n.controlPlane.project.id).find((s)=>s.id===e||s.name===e),o=new et(n.controlPlane.store).list(n.controlPlane.project.id).find((s)=>s.id===t||s.name===t);if(!i||!o)throw Error("Pool or server was not found.");n.store.addMember(i.id,o.id),h(`Attached ${o.name} to ${i.name}.`)})),r.command("placement:explain","Explain eligible and rejected placement targets").option("--purpose <purpose>","Workload purpose",{default:"application"}).option("--resources <values>","Requested resources").option("--region <region>","Required region").option("--arch <architecture>","Required architecture").option("--labels <labels>","Required pool labels").option("--json","Print structured JSON").action(async(e)=>dn((t)=>{let n=t.service.explain(t.controlPlane.project.id,{purpose:e.purpose,resources:Oa(e.resources),region:e.region,architecture:e.arch,labels:Vn(e.labels)});if(e.json)console.log(JSON.stringify(n,null,2));else x(["Pool","Server","Eligible","Available","Fit / spread / cost","Reasons"],n.map((i)=>[i.poolName,i.serverId??"provider-managed",i.eligible?"yes":"no",JSON.stringify(i.available),`${i.score.fit.toFixed(2)} / ${i.score.spread} / ${i.score.cost}`,i.reasons.join("; ")||"eligible"]))})),r.command("pool:drain <pool>","Move stateless placements and block stateful placements").action(async(e)=>dn((t)=>{let n=t.store.listPools(t.controlPlane.project.id).find((o)=>o.id===e||o.name===e);if(!n)throw Error("Pool was not found.");let i=t.service.drainPool(n.id);h(`Draining ${n.name}: moved ${i.moved.length}, blocked ${i.blocked.length} stateful placement(s).`)}))}async function om(){let r=await P(),e=q(process.cwd(),r),t=new Fa(e.store),n=new Ma(t);return{controlPlane:e,store:t,service:n}}async function It(r){let e=await om();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}var un=(r,e)=>{let t=r.store.list(r.controlPlane.project.id).find((n)=>n.id===e||n.name===e);if(!t)throw Error("Regional topology was not found.");return t},sm=(r)=>r.split(",").map((e,t)=>{let[n,i="aws"]=e.split(":");return{region:String(n),provider:i,role:t===0?"primary":"secondary"}}),am=(r)=>{let e=new Set(["s3","dynamodb","secrets"]),t=r.split(",").map((i)=>i.trim()),n=t.find((i)=>!e.has(i));if(n)throw Error(`Unsupported replication kind: ${n}`);return t};function ei(r){r.command("region:list","List regional topology, traffic, and replication health").option("--json","Print structured JSON").action(async(e)=>It((t)=>{let n=t.store.list(t.controlPlane.project.id).map((i)=>({topology:i,targets:t.store.targets(i.id),channels:t.store.channels(i.id),route:t.store.route(i.id),executions:t.store.executions(i.id)}));if(e.json)console.log(JSON.stringify(n,null,2));else x(["Name","Hostname","Active","Status","Revision","Replication"],n.map((i)=>[i.topology.name,i.topology.hostname,i.topology.activeRegion,i.topology.status,i.topology.revision??"—",`${i.channels.filter((o)=>o.status==="in_sync").length}/${i.channels.length} in sync`]))})),r.command("region:create <name>","Create a persistent multi-region topology").option("--hostname <hostname>","Traffic hostname").option("--regions <regions>","Comma-separated region[:provider], primary first").option("--replicate <kinds>","s3,dynamodb,secrets",{default:"s3,dynamodb,secrets"}).option("--max-lag <seconds>","Maximum failover lag",{default:"30"}).option("--cdn","Enable CloudFront edge state").option("--waf","Enable WAF edge state").action(async(e,t)=>It((n)=>{if(!t.hostname||!t.regions)throw Error("--hostname and --regions are required.");let i=n.service.create({organizationId:n.controlPlane.organization.id,projectId:n.controlPlane.project.id,name:e,hostname:t.hostname,regions:sm(t.regions),trafficPolicy:"active_passive",dataPolicy:{replicate:am(String(t.replicate)),maxLagSeconds:Number(t.maxLag),retainOnDestroy:!0},dnsProvider:"route53",cdnEnabled:!!t.cdn,wafEnabled:!!t.waf});h(`Created ${i.name} (${i.id}); run region:rollout with an immutable manifest.`)})),r.command("region:plan <name> <operation>","Preview exact regional execution steps").option("--target <region>","Failover or failback region").action(async(e,t,n)=>It((i)=>x(["Step"],i.service.plan(un(i,e).id,t,n.target).map((o)=>[o])))),r.command("region:rollout <name> <revision>","Queue secondary-first regional stack rollout").option("--manifest <file>","Regional stack manifest JSON").action(async(e,t,n)=>It(async(i)=>{let o=n.manifest?await Bun.file(n.manifest).json():{},s=i.service.enqueue({topologyId:un(i,e).id,kind:"rollout",revision:t,manifest:o});h(`Regional rollout queued: ${s.operation.id}`)})),r.command("region:failover <name> <region>","Queue health and RPO-gated failover").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"failover",targetRegion:t});h(`Failover queued: ${i.operation.id}`)})),r.command("region:failback <name> <region>","Queue health and RPO-gated failback").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"failback",targetRegion:t});h(`Failback queued: ${i.operation.id}`)})),r.command("region:destroy <name>","Drain traffic and delete regional stacks while retaining data").option("--confirm <name>","Exact topology name").action(async(e,t)=>It((n)=>{let i=n.service.enqueue({topologyId:un(n,e).id,kind:"destroy",confirmation:t.confirm});h(`Regional teardown queued: ${i.operation.id}; replicated data remains retained.`)}))}var mn="0.11.4";async function lm(){let r=await P(),e=q(process.cwd(),r),t=new Ba(e.store),n=new _a(t),i=["releases","artifacts","operations","logs","previews","snapshots","orphaned_resources"].map((o)=>new za(o,e.store,e.project.id,e.organization.id));return{controlPlane:e,store:t,service:n,cleanup:i}}async function tt(r){let e=await lm();try{await r(e)}catch(t){p(t instanceof Error?t.message:String(t)),process.exitCode=1}finally{e.controlPlane.store.close()}}function ti(r){r.command("maintenance:list","List updates, campaigns, cleanup plans, and DR drills").option("--json","Print structured JSON").action(async(e)=>tt((t)=>{let n={manifests:t.store.manifests(t.controlPlane.organization.id),windows:t.store.windows(t.controlPlane.project.id),campaigns:t.store.campaigns(t.controlPlane.project.id),cleanups:t.store.cleanups(t.controlPlane.project.id),drills:t.store.drills(t.controlPlane.project.id)},i=[...n.manifests.map((o)=>["update",o.version,o.compatibility.compatible?"compatible":"blocked",o.compatibility.reasons.join("; ")||o.channel]),...n.campaigns.map((o)=>["upgrade",o.id,o.status,o.currentStage??o.fromVersion]),...n.cleanups.map((o)=>["cleanup",o.kind,o.status,`${o.candidates.length} items / ${o.estimatedBytes} bytes`]),...n.drills.map((o)=>["DR drill",o.scenario,o.status,o.isolatedTarget])];if(e.json)console.log(JSON.stringify(n,null,2));else x(["Area","Name / version","Status","Detail"],i)})),r.command("maintenance:key:add <name> <pem>","Trust an Ed25519 update signing key").action(async(e,t)=>tt(async(n)=>{let i=n.store.addTrustedKey({organizationId:n.controlPlane.organization.id,name:e,publicKeyPem:await Bun.file(t).text()});h(`Trusted ${i.name}: ${i.fingerprint}`)})),r.command("maintenance:manifest <document> <signature> <key>","Verify and import a signed update manifest").action(async(e,t,n)=>tt(async(i)=>{let o=await Bun.file(e).json(),s=(await Bun.file(t).text()).trim(),c=i.store.importManifest({organizationId:i.controlPlane.organization.id,keyId:n,document:o,signature:s,current:{schemaVersion:i.controlPlane.store.health().schemaVersion,currentVersion:mn,platform:process.platform,architecture:process.arch}});h(`Verified ${c.version}; ${c.compatibility.compatible?"compatible":"blocked: "+c.compatibility.reasons.join("; ")}`)})),r.command("maintenance:window <name> <schedule>","Create an approved maintenance window").option("--timezone <timezone>","IANA timezone",{default:"UTC"}).option("--minutes <minutes>","Duration",{default:"60"}).action(async(e,t,n)=>tt((i)=>{let o=i.store.createWindow({projectId:i.controlPlane.project.id,name:e,schedule:t,timezone:n.timezone,durationMinutes:Number(n.minutes),allowedOperations:["platform.upgrade"],requireApproval:!0});h(`Created ${o.name} (${o.id}).`)})),r.command("platform:upgrade <manifest> <backup>","Plan and queue a signed staged platform update").option("--strategy <strategy>","canary or rolling",{default:"canary"}).option("--batch <count>","Rolling batch size",{default:"1"}).action(async(e,t,n)=>tt((i)=>{let o=new et(i.controlPlane.store).list(i.controlPlane.project.id).filter((l)=>l.status==="ready").map((l)=>({serverId:l.id,target:l.name,previousVersion:l.bootstrapVersion??mn}));if(!o.length)o.push({target:"control-plane",previousVersion:mn});let s=i.store.createCampaign({projectId:i.controlPlane.project.id,manifestId:e,fromVersion:mn,strategy:n.strategy==="rolling"?"rolling":"canary",batchSize:Number(n.batch),healthGate:{kind:"platform"},backupId:t,targets:o}),c=i.service.enqueueCampaign(s.id);h(`Staged upgrade queued: ${c.operation.id}`)})),r.command("cleanup:preview <kind>","Discover a bounded cleanup candidate set without deleting").option("--older-than <days>","Minimum age",{default:"30"}).action(async(e,t)=>tt(async(n)=>{let i=await n.service.previewCleanup({projectId:n.controlPlane.project.id,kind:e,criteria:{olderThanDays:Number(t.olderThan)}},n.cleanup);x(["ID","Candidate","Bytes"],i.candidates.map((o)=>[o.id,o.label,String(o.bytes)])),a(`Apply with: cloud cleanup:apply ${i.id} --confirm "DELETE ${i.candidates.length} ${i.kind}"`)})),r.command("cleanup:apply <plan>","Apply an unchanged cleanup preview").option("--confirm <text>","Exact preview confirmation").action(async(e,t)=>tt((n)=>{let i=n.service.enqueueCleanup(e,String(t.confirm??""));h(`Cleanup queued: ${i.operation.id}`)})),r.command("dr:plan <scenario> <target>","Plan an isolated disaster-recovery drill").option("--rpo <minutes>","RPO objective",{default:"60"}).option("--rto <minutes>","RTO objective",{default:"30"}).option("--recovery-point <id>","Recovery point").option("--topology <id>","Regional topology").action(async(e,t,n)=>tt((i)=>{let o=i.store.createDrill({projectId:i.controlPlane.project.id,scenario:e,isolatedTarget:t,expectedRpoMinutes:Number(n.rpo),expectedRtoMinutes:Number(n.rto),recoveryPointId:n.recoveryPoint,topologyId:n.topology});h(`Planned isolated drill ${o.id}.`)})),r.command("dr:run <drill>","Queue an isolated disaster-recovery drill").action(async(e)=>tt((t)=>{let n=t.service.enqueueDrill(e);h(`DR drill queued: ${n.operation.id}`)}))}async function ni(){try{return(await P()).project.region||process.env.AWS_REGION||"us-east-1"}catch{return process.env.AWS_REGION||"us-east-1"}}function ri(r){r.command("function:list","List all Lambda functions").option("--region <region>","AWS region").action(async(e)=>{w("Lambda Functions");let t=e?.region||await ni(),n=new nt(t);try{let{Functions:i=[]}=await n.listFunctions({MaxItems:50});if(!i.length){a("No functions found");return}x(["Name","Runtime","Memory","Timeout","Last Modified"],i.map((o)=>[o.FunctionName??"-",o.Runtime??"-",o.MemorySize?`${o.MemorySize} MB`:"-",o.Timeout?`${o.Timeout}s`:"-",o.LastModified??"-"]))}catch(i){p(`Failed to list functions: ${i.message}`),process.exitCode=1}}),r.command("function:invoke <name>","Invoke a Lambda function").option("--payload <json>","Event payload as JSON",{default:"{}"}).option("--async","Fire-and-forget (Event invocation)").option("--region <region>","AWS region").action(async(e,t)=>{w(`Invoking ${e}`);let n=t?.region||await ni(),i=new nt(n),o=new A("Invoking function...");o.start();try{let s=await i.invoke({FunctionName:e,InvocationType:t?.async?"Event":"RequestResponse",Payload:t?.payload||"{}",LogType:t?.async?"None":"Tail"});if(s.FunctionError){if(o.fail(`Function returned an error (${s.FunctionError})`),s.Payload)a(s.Payload);process.exitCode=1;return}if(o.succeed(`Invoked (status ${s.StatusCode})`),s.Payload)a(`
319
319
  Response:`),a(s.Payload);if(s.LogResult)a(`
320
320
  Logs:`),a(Buffer.from(s.LogResult,"base64").toString("utf-8"))}catch(s){o.fail(`Invocation failed: ${s.message}`),process.exitCode=1}}),r.command("function:logs <name>","View recent function logs").option("--tail","Continuously stream new log events").option("--filter <pattern>","CloudWatch filter pattern").option("--since <minutes>","Look back this many minutes",{default:"15"}).option("--region <region>","AWS region").action(async(e,t)=>{w(`Logs for ${e}`);let n=t?.region||await ni(),i=new rt(n),o=e.startsWith("/aws/lambda/")?e:`/aws/lambda/${e}`,s=Number(t?.since??15),c=(l)=>{for(let d of l){let u=d.timestamp?new Date(d.timestamp).toISOString():"";a(`${u} ${(d.message??"").trimEnd()}`)}};try{let l=Date.now()-s*60000,{events:d=[]}=await i.filterLogEvents({logGroupName:o,startTime:l,filterPattern:t?.filter,limit:200});if(c(d),d.length)l=(d[d.length-1].timestamp??l)+1;if(t?.tail){a(`
321
321
  (streaming — Ctrl+C to stop)`);for(;;){await new Promise((m)=>setTimeout(m,3000));let{events:u=[]}=await i.filterLogEvents({logGroupName:o,startTime:l,filterPattern:t?.filter,limit:200});if(u.length)c(u),l=(u[u.length-1].timestamp??l)+1}}}catch(l){p(`Failed to read logs: ${l.message}`),process.exitCode=1}})}async function Ua(r){let e=Bun.spawn(["docker",...r],{stdin:"inherit",stdout:"inherit",stderr:"pipe"}),[t,n]=await Promise.all([e.exited,new Response(e.stderr).text()]);return{ok:t===0,exitCode:t,command:["docker",...r].join(" "),stderr:n.trim()}}function ii(r){r.command("container:build","Build a Docker image").option("--tag <tag>","Image tag",{default:"latest"}).option("--file <dockerfile>","Dockerfile",{default:"Dockerfile"}).option("--dry-run","Print the exact command").option("--json","Print result JSON").action(async(e)=>{let t=["build","--file",e.file??"Dockerfile","--tag",e.tag??"latest","."];if(e.dryRun){console.log(`docker ${t.join(" ")}`);return}let n=await Ua(t);if(e.json)console.log(JSON.stringify(n));if(!n.ok)p(n.stderr||`docker exited ${n.exitCode}`),process.exitCode=n.exitCode;else h(`Built ${e.tag??"latest"}.`)}),r.command("container:push","Push an already-tagged image").option("--image <image>","Full registry image reference").option("--dry-run","Print command").option("--json","Print JSON").action(async(e)=>{if(!e.image){p("--image with a full registry reference is required; credentials must come from docker credential helpers."),process.exitCode=2;return}if(e.dryRun){console.log(`docker push ${e.image}`);return}let t=await Ua(["push",e.image]);if(e.json)console.log(JSON.stringify(t));if(!t.ok)p(t.stderr),process.exitCode=t.exitCode;else h(`Pushed ${e.image}.`)}),r.command("container:deploy","Deploy a container image").action(async()=>be("container:deploy",{message:"Direct ECS mutation is not registered as a shared release driver.",nextAction:"Import the image as an application and use the immutable release deployment workflow."}))}function mi(r){r.command("domain:list","List all domains").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(e)=>{w("Domains");try{let t=Be(e?.provider),n=ce(e?.provider)?.provider||"unknown",i=new A(`Fetching domains from ${n}...`);i.start();let o=await t.listDomains();if(i.succeed(`Found ${o.length} domain(s)`),o.length===0){a("No domains found in this provider");return}let s=o.map((c)=>[c,"Active","-",n.charAt(0).toUpperCase()+n.slice(1)]);x(["Domain","Status","SSL","Provider"],s)}catch(t){p(`Failed to list domains: ${t instanceof Error?t.message:String(t)}`)}}),r.command("domain:add <domain>","Add a new domain").option("--provider <provider>","DNS provider: porkbun, godaddy, cloudflare, or route53").action(async(e,t)=>{w(`Adding Domain: ${e}`);try{let n=Be(t?.provider),i=ce(t?.provider)?.provider||"unknown",o=new A(`Checking if ${i} can manage ${e}...`);if(o.start(),await n.canManageDomain(e))o.succeed(`Domain ${e} is already available in ${i}`),a(`
package/dist/ui/404.html CHANGED
@@ -2,7 +2,7 @@
2
2
  <html lang="en" data-theme="dark">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <meta name="stx-build" content="mt3tjhm889cvca">
5
+ <meta name="stx-build" content="mt3u6o68ittj80">
6
6
 
7
7
  <!-- stx SEO Tags -->
8
8
  <meta name="title" content="Page not found · ts-cloud">
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt3tj2le8bg297"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - Accept invitation</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.616c25f3.css"></head><body class="auth-page"><main class="auth-card" id="main-content"><a class="brand" href="/"><span class="dot"></span><span>ts-cloud</span></a><div><span class="context-label">Organization invitation</span><h1>Join your team</h1><p>Choose the dashboard username and password you will use after this invitation is accepted.</p></div><form id="accept-invitation-form" class="auth-form"><div class="field-block"><label for="invite-name">Name</label><input id="invite-name" name="name" autocomplete="name" placeholder="Chris Breuer"></div><div class="field-block"><label for="invite-username">Username</label><input id="invite-username" name="username" autocomplete="username" minlength="2" maxlength="32" required></div><div class="field-block"><label for="invite-password">Password</label><input id="invite-password" name="password" type="password" autocomplete="new-password" minlength="12" required><small>At least 12 characters.</small></div><button class="btn" type="submit">Accept invitation</button><div id="invitation-status" class="auth-status" role="status"></div></form><p class="note">If this link expired or was already used, ask an organization owner to resend the invitation.</p></main> <script data-stx-scoped data-stx-run="always">
1
+ <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt3u6820xxaean"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - Accept invitation</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.616c25f3.css"></head><body class="auth-page"><main class="auth-card" id="main-content"><a class="brand" href="/"><span class="dot"></span><span>ts-cloud</span></a><div><span class="context-label">Organization invitation</span><h1>Join your team</h1><p>Choose the dashboard username and password you will use after this invitation is accepted.</p></div><form id="accept-invitation-form" class="auth-form"><div class="field-block"><label for="invite-name">Name</label><input id="invite-name" name="name" autocomplete="name" placeholder="Chris Breuer"></div><div class="field-block"><label for="invite-username">Username</label><input id="invite-username" name="username" autocomplete="username" minlength="2" maxlength="32" required></div><div class="field-block"><label for="invite-password">Password</label><input id="invite-password" name="password" type="password" autocomplete="new-password" minlength="12" required><small>At least 12 characters.</small></div><button class="btn" type="submit">Accept invitation</button><div id="invitation-status" class="auth-status" role="status"></div></form><p class="note">If this link expired or was already used, ask an organization owner to resend the invitation.</p></main> <script data-stx-scoped data-stx-run="always">
2
2
  ;(function() {
3
3
  'use strict';
4
4
  (() => {
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt3tj2le8bg297"><meta name="title" content="Access unavailable · ts-cloud"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="Access unavailable · ts-cloud"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="Access unavailable · ts-cloud"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Access unavailable · ts-cloud</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.906ee17c.css"></head><body data-stx-auto><a data-stx-scope="stx_scope_nav_50" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>ts-cloud</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment &amp; secrets</a><a class="" href="/data/backups?env=production">Backups &amp; recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>ts-cloud</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
1
+ <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt3u6820xxaean"><meta name="title" content="Access unavailable · ts-cloud"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="Access unavailable · ts-cloud"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="Access unavailable · ts-cloud"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Access unavailable · ts-cloud</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.906ee17c.css"></head><body data-stx-auto><a data-stx-scope="stx_scope_nav_50" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>ts-cloud</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment &amp; secrets</a><a class="" href="/data/backups?env=production">Backups &amp; recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>ts-cloud</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
2
2
  (function() {
3
3
  var { batch, computed, configureFetch, defineEmits, defineExpose, definePageMeta, defineProps, defineSlots, defineStore, derived, effect, goBack, goForward, inject, invalidateRoute, isDerived, isSignal, navigate, nextTick, onBeforeMount, onBeforeUnmount, onDestroy, onMount, onMounted, onUnmounted, peek, provide, reactive, ref, refresh, registerStoresClient, setRouteParams, state, untrack, useAsync, useClickOutside, useColorMode, useCookie, useCounter, useDark, useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus, useHead, useId, useInterval, useLocalStorage, useMediaQuery, useMutation, useOptimistic, usePreferredContrast, usePreferredDark, usePreferredLight, usePreferredReducedMotion, useQuery, useReactiveProp, useRef, useRoute, useRouteParam, useRouteParams, useScrollLock, useSearchParams, useSeoMeta, useSessionStorage, useSlots, useStore, useThrottle, useTimeout, useToggle, useWebSocket, watch, watchEffect, watchMultiple, withDefaults } = window.stx;
4
4
  var __destroyHooks = [];
@@ -1,5 +1,5 @@
1
- <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt3tj2le8bg297"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - API &amp; automation</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"> <script data-stx-scoped>
2
- function __stx_setup_1787369294960_0() {
1
+ <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt3u6820xxaean"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - API &amp; automation</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"> <script data-stx-scoped>
2
+ function __stx_setup_1787370375147_0() {
3
3
  const { batch, computed, configureFetch, defineEmits, defineExpose, definePageMeta, defineProps, defineSlots, defineStore, derived, effect, goBack, goForward, inject, invalidateRoute, isDerived, isSignal, navigate, nextTick, onBeforeMount, onBeforeUnmount, onDestroy, onMount, onMounted, onUnmounted, peek, provide, reactive, ref, refresh, registerStoresClient, setRouteParams, state, untrack, useAsync, useClickOutside, useColorMode, useCookie, useCounter, useDark, useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus, useHead, useId, useInterval, useLocalStorage, useMediaQuery, useMutation, useOptimistic, usePreferredContrast, usePreferredDark, usePreferredLight, usePreferredReducedMotion, useQuery, useReactiveProp, useRef, useRoute, useRouteParam, useRouteParams, useScrollLock, useSearchParams, useSeoMeta, useSessionStorage, useSlots, useStore, useThrottle, useTimeout, useToggle, useWebSocket, watch, watchEffect, watchMultiple, withDefaults } = window.stx;
4
4
 
5
5
  const accounts = state([]);
@@ -189,9 +189,9 @@ onMount(() => {
189
189
 
190
190
  return { accounts, project, environments, resources, message, messageKind, reveal, accountSlug, accountName, accountRole, accountScopeType, accountScopeId, selectedAccount, tokenName, tokenCapabilities, tokenScopeType, tokenScopeId, tokenExpiry, password, mfaCode, capabilities, setMessage, setScope, scopeLabel, tokenState, toggleCapability, load, selectAccount, stepUp, createAccount, createToken, tokenAction, disableAccount, curlExample, cliExample, copy };
191
191
  }
192
- window.__stx_latestSetup=__stx_setup_1787369294960_0;
193
- if(window.stx)window.stx._latestSetup=__stx_setup_1787369294960_0;
194
- </script> <style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.906ee17c.css"></head><body data-stx="__stx_setup_1787369294960_0"><a data-stx-scope="stx_scope_nav_1" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>ts-cloud</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment &amp; secrets</a><a class="" href="/data/backups?env=production">Backups &amp; recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="active" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>ts-cloud</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
192
+ window.__stx_latestSetup=__stx_setup_1787370375147_0;
193
+ if(window.stx)window.stx._latestSetup=__stx_setup_1787370375147_0;
194
+ </script> <style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.906ee17c.css"></head><body data-stx="__stx_setup_1787370375147_0"><a data-stx-scope="stx_scope_nav_1" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>ts-cloud</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment &amp; secrets</a><a class="" href="/data/backups?env=production">Backups &amp; recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="active" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>ts-cloud</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
195
195
  (function() {
196
196
  var { batch, computed, configureFetch, defineEmits, defineExpose, definePageMeta, defineProps, defineSlots, defineStore, derived, effect, goBack, goForward, inject, invalidateRoute, isDerived, isSignal, navigate, nextTick, onBeforeMount, onBeforeUnmount, onDestroy, onMount, onMounted, onUnmounted, peek, provide, reactive, ref, refresh, registerStoresClient, setRouteParams, state, untrack, useAsync, useClickOutside, useColorMode, useCookie, useCounter, useDark, useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus, useHead, useId, useInterval, useLocalStorage, useMediaQuery, useMutation, useOptimistic, usePreferredContrast, usePreferredDark, usePreferredLight, usePreferredReducedMotion, useQuery, useReactiveProp, useRef, useRoute, useRouteParam, useRouteParams, useScrollLock, useSearchParams, useSeoMeta, useSessionStorage, useSlots, useStore, useThrottle, useTimeout, useToggle, useWebSocket, watch, watchEffect, watchMultiple, withDefaults } = window.stx;
197
197
  var __destroyHooks = [];
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt3tj2le8bg297"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - Sign-in &amp; security</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style> .security-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, .7fr); gap: 18px; align-items: start; } .security-stack { display: grid; gap: 18px; } .security-summary { display: grid; gap: 10px; } .security-summary .kv { padding: 8px 0; } .session-list { display: grid; gap: 10px; } .session-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: center; padding: 13px; border: 1px solid var(--panel-br); border-radius: 11px; } .session-copy { display: grid; gap: 4px; min-width: 0; } .session-copy strong, .session-copy span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .session-copy span { color: var(--txt3); font-size: 11px; } .security-form { display: grid; gap: 13px; } .security-form label { display: grid; gap: 6px; color: var(--txt3); font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; } .security-form input { width: 100%; } .security-status { min-height: 20px; color: var(--txt2); font-size: 12px; } .mfa-secret { display: block; margin: 9px 0; padding: 10px; overflow-wrap: anywhere; border: 1px solid var(--panel-br); border-radius: 9px; background: rgba(255,255,255,.025); color: var(--accent); font: 700 12px var(--mono); } .recovery-codes { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 10px; } .recovery-codes code { padding: 7px; border: 1px solid var(--panel-br); border-radius: 7px; text-align: center; } .hidden { display: none !important; } @media (max-width: 900px) { .security-layout { grid-template-columns: 1fr; } } </style><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.222ed683.css"></head><body data-stx-auto><a data-stx-scope="stx_scope_nav_4" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>Project</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment &amp; secrets</a><a class="" href="/data/backups?env=production">Backups &amp; recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="active" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>Project</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
1
+ <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt3u6820xxaean"><meta name="title" content="stx Project"><meta name="description" content="A website built with stx templating engine"><meta property="og:title" content="stx Project"><meta property="og:description" content="A website built with stx templating engine"><meta property="og:type" content="website"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:title" content="stx Project"><meta name="twitter:description" content="A website built with stx templating engine"> <script data-stx-runtime src="/_stx/runtime.c696d17a.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ts-cloud - Sign-in &amp; security</title><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect x='3' y='3' width='26' height='26' rx='8' fill='%235a8be0'/%3E%3C/svg%3E"><link rel="preconnect" href="https://fonts.googleapis.com"><link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet"><link rel="stylesheet" href="/_stx/style.448ab192.css"><style> .security-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(280px, .7fr); gap: 18px; align-items: start; } .security-stack { display: grid; gap: 18px; } .security-summary { display: grid; gap: 10px; } .security-summary .kv { padding: 8px 0; } .session-list { display: grid; gap: 10px; } .session-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: center; padding: 13px; border: 1px solid var(--panel-br); border-radius: 11px; } .session-copy { display: grid; gap: 4px; min-width: 0; } .session-copy strong, .session-copy span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .session-copy span { color: var(--txt3); font-size: 11px; } .security-form { display: grid; gap: 13px; } .security-form label { display: grid; gap: 6px; color: var(--txt3); font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; } .security-form input { width: 100%; } .security-status { min-height: 20px; color: var(--txt2); font-size: 12px; } .mfa-secret { display: block; margin: 9px 0; padding: 10px; overflow-wrap: anywhere; border: 1px solid var(--panel-br); border-radius: 9px; background: rgba(255,255,255,.025); color: var(--accent); font: 700 12px var(--mono); } .recovery-codes { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 10px; } .recovery-codes code { padding: 7px; border: 1px solid var(--panel-br); border-radius: 7px; text-align: center; } .hidden { display: none !important; } @media (max-width: 900px) { .security-layout { grid-template-columns: 1fr; } } </style><style data-stx-cloak>[x-cloak]{display:none !important}</style><link data-crosswind="generated" rel="stylesheet" href="/_stx/crosswind.222ed683.css"></head><body data-stx-auto><a data-stx-scope="stx_scope_nav_6" class="skip-link" href="#main-content">Skip to content</a><button class="nav-toggle-button" type="button" aria-label="Open navigation" aria-controls="primary-navigation" aria-expanded="false"><span></span><span></span><span></span></button><button class="nav-scrim" type="button" aria-label="Close navigation"></button><aside class="app-sidebar" id="primary-navigation" aria-label="Primary navigation"><a class="brand" href="/?env=production"><span class="dot"></span><span>ts-cloud</span></a><div class="sidebar-context"><span class="context-label">Project</span><strong>Project</strong></div><div class="environment-picker" aria-label="Environment"><span class="context-label">Environment</span><details><summary class="environment-current production"><span>PROD</span><strong>production</strong></summary></details></div><button class="command-trigger" type="button" aria-keyshortcuts="Meta+K Control+K"><span>Search resources</span><kbd>⌘ K</kbd></button><div class="nav-groups"><section class="nav-group"><h2>Overview</h2><a class="" href="/?env=production">Dashboard</a><a href="/server/activity?env=production">Activity</a></section><section class="nav-group"><h2>Applications</h2><a class="" href="/server/sites?env=production">Services</a><a class="" href="/server/deployments?env=production">Deployments</a><a class="" href="/server/logs?env=production">Logs</a><a class="" href="/integrations?env=production">Git integrations</a><a class="" href="/applications/new?env=production">Create application</a><a class="" href="/applications/compose?env=production">Compose applications</a></section><section class="nav-group"><h2>Operations</h2><a class="" href="/operations/queue?env=production">Deployment queue</a><a class="" href="/operations/previews?env=production">Preview environments</a><a class="" href="/operations/releases?env=production">Releases & promotion</a><a class="" href="/operations/regions?env=production">Regions & failover</a><a class="" href="/operations/maintenance?env=production">Maintenance & DR</a><a class="" href="/operations/workloads?env=production">Workloads</a><a class="" href="/operations/observability?env=production">Observability</a><a class="" href="/operations/alerts?env=production">Alerts & health</a><a class="" href="/operations/spend?env=production">Spend & budgets</a><a href="/server/metrics?env=production">Metrics</a><a href="/server/services?env=production">Runtime services</a><a class="" href="/operations/jobs?env=production">Workers & schedules</a><a class="" href="/operations/configuration?env=production">Environment &amp; secrets</a><a class="" href="/data/backups?env=production">Backups &amp; recovery</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/actions?env=production">Run operations</a></section><section class="nav-group"><h2>Infrastructure</h2><a class="" href="/infrastructure/topology?env=production">Infrastructure map</a><a class="" href="/data/services?env=production">Data services</a><a class="" href="/data/volumes?env=production">Persistent volumes</a><a href="/server/firewall?env=production">Network & firewall</a><a href="/server/ssh-keys?env=production">SSH keys</a><a class="" href="/server/fleet?env=production">Fleet inventory</a><a class="" href="/server/capacity?env=production">Capacity & placement</a><a href="/security?env=production">Security posture</a><a href="/server/terminal?env=production">Terminal</a></section><section class="nav-group"><h2>Organization</h2><a class="" href="/server/team?env=production">People & access</a><a class="" href="/account/automation?env=production">API & automation</a></section><section class="nav-group"><h2>Account</h2><a class="active" href="/account/security?env=production">Sign-in & security</a></section></div><form method="post" action="/api/logout" class="signout"><button type="submit">Sign out</button></form></aside><dialog class="command-palette" aria-labelledby="command-title"><div class="command-search"><span aria-hidden="true">⌕</span><input type="search" placeholder="Search services, domains, deployments…" aria-label="Search resources" autocomplete="off"><button type="button" class="command-close" aria-label="Close search">Esc</button></div><h2 id="command-title" class="sr-only">Search resources and navigation</h2><div class="command-status" role="status">Start typing to search this project.</div><section class="saved-views" aria-labelledby="saved-views-title"><div class="saved-views-heading"><h3 id="saved-views-title">Saved views</h3><form class="save-view-form"><label class="sr-only" for="save-view-name">Saved view name</label><input id="save-view-name" maxlength="64" placeholder="Name this view" autocomplete="off"><button type="submit">Save current filters</button></form></div><div class="saved-view-list"></div></section><form class="tag-result-form"><label for="tag-result-name">Tag selected resource</label><input id="tag-result-name" maxlength="32" placeholder="customer-facing" pattern="[A-Za-z0-9][A-Za-z0-9 -]{0,31}" autocomplete="off"><input class="tag-color" type="color" value="#5a8be0" aria-label="Tag color"><button type="submit">Add tag</button></form><div class="command-results" role="listbox" aria-label="Search results"></div><footer><span>↑ ↓ navigate</span><span>Enter open</span><span>Esc close</span></footer></dialog><div class="context-bar" aria-label="Current scope"><div class="context-path"><div class="context-breadcrumb"><span>Project</span><i>/</i><strong>production</strong></div><div class="context-filters" aria-label="Active filters"></div></div><span class="scope-safety production">Production scope</span></div> <script data-stx-scoped data-stx-run="always">
2
2
  (function() {
3
3
  var { batch, computed, configureFetch, defineEmits, defineExpose, definePageMeta, defineProps, defineSlots, defineStore, derived, effect, goBack, goForward, inject, invalidateRoute, isDerived, isSignal, navigate, nextTick, onBeforeMount, onBeforeUnmount, onDestroy, onMount, onMounted, onUnmounted, peek, provide, reactive, ref, refresh, registerStoresClient, setRouteParams, state, untrack, useAsync, useClickOutside, useColorMode, useCookie, useCounter, useDark, useDebounce, useDebouncedValue, useEventListener, useFetch, useFocus, useHead, useId, useInterval, useLocalStorage, useMediaQuery, useMutation, useOptimistic, usePreferredContrast, usePreferredDark, usePreferredLight, usePreferredReducedMotion, useQuery, useReactiveProp, useRef, useRoute, useRouteParam, useRouteParams, useScrollLock, useSearchParams, useSeoMeta, useSessionStorage, useSlots, useStore, useThrottle, useTimeout, useToggle, useWebSocket, watch, watchEffect, watchMultiple, withDefaults } = window.stx;
4
4
  var __destroyHooks = [];
@@ -349,7 +349,7 @@
349
349
 
350
350
 
351
351
  try { if (typeof __stxScopeRenames !== 'undefined' && __stxScopeRenames) { for (var __stxRK in __stxScopeRenames) __scopeRegistration[__stxRK] = __stxScopeRenames[__stxRK]; } } catch (e) { /* no renames */ }
352
- window.stx._scopes['stx_scope_nav_4'] = __scopeRegistration;
352
+ window.stx._scopes['stx_scope_nav_6'] = __scopeRegistration;
353
353
  })();
354
354
  </script> <main class="wrap" id="main-content"><div class="crumbs"><a href="/">Account</a><span class="sep">/</span> Sign-in & security</div><header class="page"><div><h1>Sign-in & security</h1><p>Manage your password and review every browser session with access to this account.</p></div></header><div class="security-layout"><div class="security-stack"><section class="panel" aria-labelledby="sessions-title"><div class="section-heading"><div><h2 id="sessions-title">Active sessions</h2><p class="note">Last-used times and device details help you spot unfamiliar access.</p></div><button class="btn ghost sm" id="revoke-others" type="button">Sign out other sessions</button></div><div class="session-list" id="session-list" aria-live="polite"><div class="compact empty"><strong>Loading sessions…</strong></div></div></section></div><div class="security-stack"><section class="panel security-summary" aria-labelledby="identity-title"><h2 id="identity-title">Identity</h2><div class="kv"><span>Username</span><b id="identity-username">—</b></div><div class="kv"><span>Email</span><b id="identity-email">Not configured</b></div><div class="kv"><span>Last sign-in</span><b id="identity-login">—</b></div><div class="kv"><span>Multi-factor authentication</span><b id="identity-mfa">Loading…</b></div><details><summary>Confirm identity for sensitive changes</summary><form class="security-form" id="step-up-form"><label>Current password<input name="password" type="password" autocomplete="current-password" required></label><label>Authenticator or recovery code (when enabled)<input name="code" autocomplete="one-time-code"></label><button class="btn ghost" type="submit">Confirm identity</button><div class="security-status" id="step-up-status" role="status"></div></form></details></section><section class="panel" aria-labelledby="mfa-title"><h2 id="mfa-title">Authenticator app</h2><p class="note" id="mfa-copy">Add a time-based code as a second sign-in factor.</p><form class="security-form" id="mfa-enroll-form"><label>Current password<input name="password" type="password" autocomplete="current-password" required></label><button class="btn" type="submit">Set up authenticator</button></form><form class="hidden security-form" id="mfa-verify-form"><p class="note">Add this secret or setup URI to your authenticator app, then enter its six-digit code.</p><code class="mfa-secret" id="mfa-secret"></code><details><summary>Show full setup URI</summary><code class="mfa-secret" id="mfa-uri"></code></details><label>Six-digit code<input name="code" inputmode="numeric" autocomplete="one-time-code" pattern="[0-9]{6}" required></label><button class="btn" type="submit">Verify and enable</button></form><form class="hidden security-form" id="mfa-disable-form"><label>Current password<input name="password" type="password" autocomplete="current-password" required></label><label>Authenticator or recovery code<input name="code" autocomplete="one-time-code" required></label><button class="btn danger" type="submit">Disable MFA</button></form><div class="hidden recovery-codes" id="recovery-codes" aria-label="Recovery codes"></div><div class="security-status" id="mfa-status" role="status"></div></section><section class="panel" aria-labelledby="password-title"><h2 id="password-title">Change password</h2><p class="note">Changing your password rotates this session and immediately signs out every other session.</p><form class="security-form" id="password-form"><label>Current password<input name="currentPassword" type="password" autocomplete="current-password" required></label><label>New password<input name="password" type="password" autocomplete="new-password" minlength="12" required></label><label>Confirm new password<input name="confirmation" type="password" autocomplete="new-password" minlength="12" required></label><button class="btn" type="submit">Change password</button><div class="security-status" id="password-status" role="status"></div></form></section></div></div></main> <script data-stx-scoped data-stx-run="always">
355
355
  ;(function() {