@stacksjs/ts-cloud 0.11.7 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/aws/index.js +1 -1
  2. package/dist/bin/{chunk-znnawt0k.js → chunk-71t1gmsq.js} +1 -1
  3. package/dist/bin/{chunk-4xhj37nj.js → chunk-8ewncfce.js} +1 -1
  4. package/dist/bin/{chunk-8khgprf6.js → chunk-9kf0np8j.js} +1 -1
  5. package/dist/bin/chunk-cxt74wsj.js +1 -0
  6. package/dist/bin/{chunk-j447p8t4.js → chunk-g6baf9c5.js} +84 -84
  7. package/dist/bin/{chunk-zsf6pccz.js → chunk-q5cmfpzt.js} +1 -1
  8. package/dist/bin/{chunk-r6b3pq31.js → chunk-rtecnmts.js} +1 -1
  9. package/dist/bin/{chunk-5nsqz5n3.js → chunk-rv18092e.js} +1 -1
  10. package/dist/bin/{chunk-yw6f160x.js → chunk-ta7sh0cc.js} +1 -1
  11. package/dist/bin/{chunk-cbsys5v8.js → chunk-tfbs8k8v.js} +1 -1
  12. package/dist/bin/{chunk-g0fm1xcs.js → chunk-xkfrqqcs.js} +1 -1
  13. package/dist/bin/cli.js +99 -93
  14. package/dist/bin/dashboard-server.js +1 -1
  15. package/dist/chunk-13qjvbqh.js +2 -0
  16. package/dist/{chunk-nk7sav4r.js → chunk-152he7m3.js} +1 -1
  17. package/dist/{chunk-dr4pj8hp.js → chunk-3mgj3hrh.js} +1 -1
  18. package/dist/chunk-6kpbveb8.js +5 -0
  19. package/dist/{chunk-aw0cgv5w.js → chunk-86ss83s2.js} +1 -1
  20. package/dist/chunk-a84gmpyh.js +1 -0
  21. package/dist/{chunk-ywjkyg8p.js → chunk-b087qsyf.js} +99 -99
  22. package/dist/{chunk-zam6ecam.js → chunk-h42zck7h.js} +2 -2
  23. package/dist/{chunk-zz52xje8.js → chunk-jm5mg890.js} +1 -1
  24. package/dist/{chunk-kasdm9tx.js → chunk-m8fs0233.js} +1 -1
  25. package/dist/chunk-nms5q44e.js +1 -0
  26. package/dist/{chunk-aw0spg20.js → chunk-nzqxdkch.js} +2 -2
  27. package/dist/{chunk-rd38qdnf.js → chunk-qm32ytbr.js} +1 -1
  28. package/dist/{chunk-q0rc1ax9.js → chunk-t0xgmhd6.js} +1 -1
  29. package/dist/{chunk-q9q6cmt0.js → chunk-t3t9ymec.js} +1 -1
  30. package/dist/{chunk-5vt93kxx.js → chunk-y2v22hkv.js} +1 -1
  31. package/dist/deploy/index.js +1 -1
  32. package/dist/dns/index.js +1 -1
  33. package/dist/drivers/index.js +1 -1
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +52 -52
  36. package/dist/operations/drained-sites.d.ts +56 -0
  37. package/dist/operations/drained-sites.test.d.ts +1 -0
  38. package/dist/pages/cloudflare-pages.d.ts +227 -0
  39. package/dist/pages/cloudflare-pages.test.d.ts +1 -0
  40. package/dist/pages/index.d.ts +2 -0
  41. package/dist/protection/index.js +1 -1
  42. package/dist/push/index.js +1 -1
  43. package/dist/spend/index.js +1 -1
  44. package/dist/ui/404.html +1 -1
  45. package/dist/ui/accept-invitation.html +1 -1
  46. package/dist/ui/access-denied.html +1 -1
  47. package/dist/ui/account/automation.html +5 -5
  48. package/dist/ui/account/security.html +2 -2
  49. package/dist/ui/applications/compose.html +6 -6
  50. package/dist/ui/applications/new.html +2 -2
  51. package/dist/ui/data/backups.html +6 -6
  52. package/dist/ui/data/services.html +6 -6
  53. package/dist/ui/data/volumes.html +6 -6
  54. package/dist/ui/index.html +6 -6
  55. package/dist/ui/infrastructure/topology.html +5 -5
  56. package/dist/ui/integrations.html +1 -1
  57. package/dist/ui/operations/alerts.html +6 -6
  58. package/dist/ui/operations/configuration.html +6 -6
  59. package/dist/ui/operations/jobs.html +6 -6
  60. package/dist/ui/operations/maintenance.html +6 -6
  61. package/dist/ui/operations/observability.html +6 -6
  62. package/dist/ui/operations/previews.html +6 -6
  63. package/dist/ui/operations/queue.html +5 -5
  64. package/dist/ui/operations/regions.html +6 -6
  65. package/dist/ui/operations/releases.html +6 -6
  66. package/dist/ui/operations/spend.html +6 -6
  67. package/dist/ui/operations/workloads.html +6 -6
  68. package/dist/ui/security.html +2 -2
  69. package/dist/ui/server/actions.html +6 -6
  70. package/dist/ui/server/activity.html +2 -2
  71. package/dist/ui/server/capacity.html +6 -6
  72. package/dist/ui/server/database.html +6 -6
  73. package/dist/ui/server/deployments.html +6 -6
  74. package/dist/ui/server/diagnostics.html +2 -2
  75. package/dist/ui/server/firewall.html +6 -6
  76. package/dist/ui/server/fleet.html +6 -6
  77. package/dist/ui/server/logs.html +6 -6
  78. package/dist/ui/server/metrics.html +6 -6
  79. package/dist/ui/server/security.html +2 -2
  80. package/dist/ui/server/services.html +1 -1
  81. package/dist/ui/server/sites.html +6 -6
  82. package/dist/ui/server/ssh-keys.html +6 -6
  83. package/dist/ui/server/team.html +6 -6
  84. package/dist/ui/server/terminal.html +2 -2
  85. package/dist/ui/serverless/alarms.html +5 -5
  86. package/dist/ui/serverless/assets.html +1 -1
  87. package/dist/ui/serverless/cost.html +2 -2
  88. package/dist/ui/serverless/data.html +5 -5
  89. package/dist/ui/serverless/deployments.html +1 -1
  90. package/dist/ui/serverless/firewall.html +2 -2
  91. package/dist/ui/serverless/functions.html +6 -6
  92. package/dist/ui/serverless/logs.html +6 -6
  93. package/dist/ui/serverless/metrics.html +1 -1
  94. package/dist/ui/serverless/queues.html +6 -6
  95. package/dist/ui/serverless/secrets.html +6 -6
  96. package/dist/ui/serverless/traces.html +6 -6
  97. package/dist/ui/serverless.html +6 -6
  98. package/package.json +4 -3
  99. package/dist/bin/chunk-wkx799z2.js +0 -1
  100. package/dist/chunk-95e6aeqz.js +0 -1
  101. package/dist/chunk-p10xt3jb.js +0 -1
  102. package/dist/chunk-z2qqwy7e.js +0 -2
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Detect sites whose files are still on a box that no longer serves them.
3
+ *
4
+ * `site:move` deliberately never deletes anything on the source: it stops the
5
+ * units, drops the gateway route, and leaves the whole tree in place. That
6
+ * leftover tree IS the rollback — the one thing that makes a bad cutover
7
+ * recoverable, and the reason the operation can promise reversibility "until the
8
+ * source server is destroyed".
9
+ *
10
+ * Which makes destroying that server the moment the promise expires, and nothing
11
+ * about the box says so. It has no running units for the site, so it looks idle;
12
+ * the config has moved on; the operator is tidying up. Terminating it is exactly
13
+ * the right thing to do once the move is verified, and exactly the wrong thing
14
+ * to do before — and those two look identical from outside.
15
+ *
16
+ * So: before a teardown, ask the box whether it is holding anyone's rollback.
17
+ *
18
+ * @see https://github.com/stacksjs/ts-cloud/issues/167
19
+ */
20
+ /**
21
+ * Report every one of this project's site trees on the box and whether anything
22
+ * is still running for it.
23
+ *
24
+ * Read-only by construction — it lists and queries, and that is all. A scan that
25
+ * could change the box would be a poor thing to run immediately before deciding
26
+ * whether to keep it.
27
+ *
28
+ * A directory is only counted when it holds `releases/`, so a stray folder under
29
+ * `/var/www` is not mistaken for a deployed site and does not block a teardown
30
+ * the operator genuinely wants.
31
+ */
32
+ export declare function buildDrainedSiteScanScript(slug: string, wwwRoot?: string): string[];
33
+ export interface DrainedSite {
34
+ name: string;
35
+ /** Human-readable size of the tree left behind, e.g. `1.2G`. */
36
+ size: string;
37
+ }
38
+ /**
39
+ * Sites the box still holds but no longer runs.
40
+ *
41
+ * A site with something active is NOT drained — it is simply a site on a box
42
+ * being torn down, which is ordinary and already covered by the teardown's own
43
+ * confirmation. The interesting case is files with nothing running: either a
44
+ * completed `site:move` whose rollback this is, or a site that has been stopped
45
+ * and forgotten. Both are worth a sentence before the disk goes away.
46
+ */
47
+ export declare function parseDrainedSites(output: string | undefined): DrainedSite[];
48
+ /**
49
+ * What the operator sees instead of a teardown.
50
+ *
51
+ * Names the sites, says what the files are FOR, and gives the two ways forward.
52
+ * The flag is deliberately its own thing rather than `--force`: `--force` exists
53
+ * so a teardown can run unattended, and a CI job that skips a prompt must not
54
+ * also silently discard the only copy of a rollback.
55
+ */
56
+ export declare function formatDrainedSiteRefusal(sites: readonly DrainedSite[], slug: string, flag: string): string;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Deploy a built static site to Cloudflare Pages.
3
+ *
4
+ * Pages is the one Cloudflare product that is not reachable through the zone
5
+ * API the rest of this codebase talks to: projects are account-scoped, and
6
+ * uploads go through a separate content-addressed flow rather than a single
7
+ * PUT. The shape of that flow drives this module:
8
+ *
9
+ * - **Files are addressed by hash, not by path.** A deployment is a manifest
10
+ * mapping `/path` to a hash; the bytes are uploaded separately, and only for
11
+ * hashes the account has never seen. A redeploy of a site whose CSS did not
12
+ * change re-uploads nothing for that file, which is why `deployDirectory`
13
+ * checks `/pages/assets/check-missing` before sending anything.
14
+ * - **The hash is Cloudflare's own, not a plain digest.** It is a blake3 of
15
+ * the base64 payload concatenated with the file extension, truncated to 32
16
+ * hex characters. Getting this wrong does not error — it uploads fine and
17
+ * then serves 404s, because the manifest points at a hash the asset store
18
+ * does not hold under that name.
19
+ * - **Upload auth is a separate, short-lived JWT**, fetched from
20
+ * `/pages/projects/{name}/upload-token`. The account API token cannot be
21
+ * used against the asset endpoints.
22
+ *
23
+ * A custom domain is attached with {@link attachCustomDomain}, which is
24
+ * idempotent, and still needs a CNAME in DNS — see {@link pagesDnsRecord}.
25
+ */
26
+ import type { DnsRecord } from '../dns/types';
27
+ /** Cloudflare's asset-upload ceiling. Larger files are rejected outright. */
28
+ export declare const PAGES_MAX_FILE_BYTES: number;
29
+ export interface CloudflarePagesProject {
30
+ id: string;
31
+ name: string;
32
+ subdomain: string;
33
+ domains: string[];
34
+ production_branch?: string;
35
+ }
36
+ export interface CloudflarePagesDeployment {
37
+ id: string;
38
+ url: string;
39
+ environment: string;
40
+ project_name: string;
41
+ }
42
+ export interface CloudflarePagesOptions {
43
+ /** Account-scoped API token with the `Cloudflare Pages: Edit` permission. */
44
+ apiToken: string;
45
+ /** Account the project lives under. */
46
+ accountId: string;
47
+ }
48
+ /** One file in a deployment, already hashed. */
49
+ export interface PagesAsset {
50
+ /** Site-absolute path, e.g. `/guide/index.html`. */
51
+ path: string;
52
+ /** Cloudflare asset hash (32 hex chars). */
53
+ hash: string;
54
+ /** Base64 of the file's bytes. */
55
+ base64: string;
56
+ /** Extension without the dot, e.g. `html`. Empty when there is none. */
57
+ extension: string;
58
+ /** Byte length of the original file, for reporting and limit checks. */
59
+ size: number;
60
+ }
61
+ export interface DeployDirectoryResult {
62
+ deployment: CloudflarePagesDeployment;
63
+ /** Every asset in the manifest. */
64
+ total: number;
65
+ /** Assets whose bytes had to be sent, the rest already being in the store. */
66
+ uploaded: number;
67
+ /** Bytes actually transferred. */
68
+ uploadedBytes: number;
69
+ }
70
+ /**
71
+ * Cloudflare Pages, scoped to one account.
72
+ *
73
+ * Mirrors {@link CloudflareProvider}: a thin authenticated `request`, and
74
+ * methods that each map onto one API concern.
75
+ */
76
+ export declare class CloudflarePagesProvider {
77
+ readonly name = "cloudflare-pages";
78
+ private readonly apiToken;
79
+ private readonly accountId;
80
+ constructor(options: CloudflarePagesOptions);
81
+ /**
82
+ * Make an authenticated request against the account-scoped Pages API.
83
+ *
84
+ * @param method - HTTP method
85
+ * @param endpoint - Path below `/accounts/{id}`, starting with a slash
86
+ * @param body - JSON body, or a `FormData` for multipart endpoints
87
+ * @returns The unwrapped `result`
88
+ * @throws {Error} When Cloudflare reports failure
89
+ */
90
+ private request;
91
+ /**
92
+ * Look up a project by name.
93
+ *
94
+ * @param name - Project name
95
+ * @returns The project, or `null` when it does not exist
96
+ */
97
+ getProject(name: string): Promise<CloudflarePagesProject | null>;
98
+ /**
99
+ * Create a direct-upload project, or return the existing one.
100
+ *
101
+ * Direct upload means ts-cloud pushes the built output itself rather than
102
+ * Cloudflare cloning and building the repository. That keeps the build in
103
+ * your CI, where the toolchain and secrets already are.
104
+ *
105
+ * @param name - Project name; also the `<name>.pages.dev` subdomain
106
+ * @param productionBranch - Branch treated as production. @default 'main'
107
+ * @returns The project, whether created here or already present
108
+ */
109
+ ensureProject(name: string, productionBranch?: string): Promise<CloudflarePagesProject>;
110
+ /**
111
+ * Short-lived JWT for the asset endpoints.
112
+ *
113
+ * The account API token is not accepted there, so this is fetched per deploy
114
+ * rather than held.
115
+ *
116
+ * @param project - Project name
117
+ */
118
+ private uploadToken;
119
+ /**
120
+ * Ask which of these hashes the asset store does not already hold.
121
+ *
122
+ * @param jwt - Upload token
123
+ * @param hashes - Every hash in the manifest
124
+ * @returns The subset whose bytes still need sending
125
+ */
126
+ private missingHashes;
127
+ /**
128
+ * Upload one batch of assets.
129
+ *
130
+ * @param jwt - Upload token
131
+ * @param assets - At most {@link UPLOAD_BATCH_SIZE} assets
132
+ */
133
+ private uploadBatch;
134
+ /**
135
+ * Create a deployment from an already-hashed manifest.
136
+ *
137
+ * @param project - Project name
138
+ * @param assets - Every file in the site
139
+ * @param branch - Branch to record. Matching the production branch publishes
140
+ * to the live domain; anything else becomes a preview deployment.
141
+ * @returns The created deployment
142
+ */
143
+ createDeployment(project: string, assets: PagesAsset[], branch?: string): Promise<CloudflarePagesDeployment>;
144
+ /**
145
+ * Build, upload and deploy a directory.
146
+ *
147
+ * The whole flow: hash every file, ask what is missing, upload only that,
148
+ * then create the deployment.
149
+ *
150
+ * @param options.project - Project name
151
+ * @param options.directory - Directory to publish; becomes the site root
152
+ * @param options.branch - Branch to record on the deployment
153
+ * @param options.onProgress - Called as batches complete, for CLI output
154
+ * @returns The deployment and what it cost to send
155
+ * @throws {Error} When the directory is empty or a file exceeds the size limit
156
+ */
157
+ deployDirectory(options: {
158
+ project: string;
159
+ directory: string;
160
+ branch?: string;
161
+ onProgress?: (uploaded: number, total: number) => void;
162
+ }): Promise<DeployDirectoryResult>;
163
+ /** Custom domains already attached to a project. */
164
+ listCustomDomains(project: string): Promise<string[]>;
165
+ /**
166
+ * Attach a custom domain, or leave it attached.
167
+ *
168
+ * Idempotent so a deploy pipeline can call it on every run rather than
169
+ * guarding with a lookup that races.
170
+ *
171
+ * @param project - Project name
172
+ * @param domain - Hostname to serve, e.g. `buddy.sh`
173
+ * @returns Whether this call is what attached it
174
+ */
175
+ attachCustomDomain(project: string, domain: string): Promise<boolean>;
176
+ }
177
+ /**
178
+ * The DNS record that points a custom domain at a Pages project.
179
+ *
180
+ * Pages serves from a `*.pages.dev` host, so the zone needs a CNAME. Two things
181
+ * are easy to get wrong here:
182
+ *
183
+ * - **The target is the project's `subdomain`, not `<name>.pages.dev`.**
184
+ * Cloudflare only grants the bare name when it is free; a project named
185
+ * `buddy` whose name was taken is served from `buddy-5yo.pages.dev`.
186
+ * Deriving the host from the project name instead of reading `subdomain`
187
+ * produces a CNAME to a host that does not exist.
188
+ * - **It must be proxied.** An unproxied CNAME reaches Cloudflare's edge but
189
+ * arrives without the routing metadata Pages uses to pick a project, and
190
+ * answers 404.
191
+ *
192
+ * A CNAME at the apex is legal here only because Cloudflare flattens it.
193
+ *
194
+ * @param hostname - Public hostname, e.g. `buddy.sh` or `www.buddy.sh`
195
+ * @param subdomain - The project's `subdomain`, e.g. `buddy-5yo.pages.dev`
196
+ * @returns A record ready for `CloudflareProvider.upsertRecord`
197
+ */
198
+ export declare function pagesDnsRecord(hostname: string, subdomain: string): DnsRecord;
199
+ /**
200
+ * Cloudflare's asset hash: BLAKE3 of `<base64><extension>`, first 32 hex chars.
201
+ *
202
+ * The extension is part of the input, so the same bytes saved as `.html` and
203
+ * as `.txt` hash differently — which is deliberate, since the asset store
204
+ * serves content type from the key.
205
+ *
206
+ * BLAKE3 specifically, and it has to come from a library: Bun's `CryptoHasher`
207
+ * ships blake2b but not blake3. Substituting a different digest is not a
208
+ * degradation that shows up as an error — the upload succeeds and the site
209
+ * then serves 404s, because the manifest names a key the store does not hold.
210
+ *
211
+ * @param base64 - Base64 of the file's bytes
212
+ * @param extension - Extension without the dot
213
+ */
214
+ export declare function pagesAssetHash(base64: string, extension: string): Promise<string>;
215
+ /**
216
+ * Walk a directory into a deployable manifest.
217
+ *
218
+ * Dotfiles are included: a built site legitimately contains `.well-known`, and
219
+ * bunpress writes its output under a `.bunpress` root. Cloudflare's own
220
+ * reserved paths are skipped, since uploading them as assets shadows the
221
+ * configuration they are meant to be.
222
+ *
223
+ * @param directory - Site root
224
+ * @returns One entry per file, hashed and base64-encoded
225
+ * @throws {Error} When a file exceeds {@link PAGES_MAX_FILE_BYTES}
226
+ */
227
+ export declare function collectAssets(directory: string): Promise<PagesAsset[]>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { CloudflarePagesProvider, collectAssets, PAGES_MAX_FILE_BYTES, pagesAssetHash, pagesDnsRecord, } from './cloudflare-pages';
2
+ export type { CloudflarePagesDeployment, CloudflarePagesOptions, CloudflarePagesProject, DeployDirectoryResult, PagesAsset, } from './cloudflare-pages';
@@ -1 +1 @@
1
- import{$k as z,_k as y,al as A,bl as B,cl as C,dl as D,el as E,fl as F,gl as G,hl as H,il as I,jl as J,kl as K,ll as L,ml as M}from"../chunk-1qdne0bf.js";import{nl as s,ol as t,pl as u,ql as v,rl as w,sl as x}from"../chunk-hn40z11p.js";import{Al as r,tl as k,ul as l,vl as m,wl as n,xl as o,yl as p,zl as q}from"../chunk-x3hay61c.js";import{Ns as a,Os as b,Ps as c,Qs as d,Rs as e,Ss as f,Ts as g,Us as h,Vs as i,Ws as j}from"../chunk-zz52xje8.js";import"../chunk-z2qqwy7e.js";export{b as CHAIN_HEADER,H as DEFAULT_ATTACK_MODE_HOURS,k as DEFAULT_DDOS_THRESHOLDS,e as DEFAULT_RECURSION_LIMITS,a as DEPTH_HEADER,G as MAX_CONTROL_HOURS,F as PROTECTION_SETTING,I as ProtectionControlStore,y as RATE_LIMIT_KEY_SEPARATOR,C as RateLimiter,h as RecursionGuard,c as TRACE_HEADER,L as applyControlsToDdos,K as applyControlsToRateLimits,r as applyMitigationFactor,E as defaultRateLimitRules,J as describePosture,s as escapeSecLang,d as functionFingerprint,z as globMatches,u as globToRegex,g as inspectInvocation,t as isSafePattern,l as isValidCidr,M as mergeControlsIntoConfig,f as parseChain,q as planMitigation,i as propagationHeaders,D as rateLimitHeaders,B as rateLimitKey,j as recursionBlockedResponse,p as renderDdosInstallScript,o as renderNftablesRuleset,n as renderSysctlFile,v as renderWafConfig,w as renderWafInstallScript,A as ruleMatches,m as sysctlHardening,x as wafEventTelemetry};
1
+ import{$k as z,_k as y,al as A,bl as B,cl as C,dl as D,el as E,fl as F,gl as G,hl as H,il as I,jl as J,kl as K,ll as L,ml as M}from"../chunk-1qdne0bf.js";import{nl as s,ol as t,pl as u,ql as v,rl as w,sl as x}from"../chunk-hn40z11p.js";import{Al as r,tl as k,ul as l,vl as m,wl as n,xl as o,yl as p,zl as q}from"../chunk-x3hay61c.js";import{Ns as a,Os as b,Ps as c,Qs as d,Rs as e,Ss as f,Ts as g,Us as h,Vs as i,Ws as j}from"../chunk-jm5mg890.js";import"../chunk-13qjvbqh.js";export{b as CHAIN_HEADER,H as DEFAULT_ATTACK_MODE_HOURS,k as DEFAULT_DDOS_THRESHOLDS,e as DEFAULT_RECURSION_LIMITS,a as DEPTH_HEADER,G as MAX_CONTROL_HOURS,F as PROTECTION_SETTING,I as ProtectionControlStore,y as RATE_LIMIT_KEY_SEPARATOR,C as RateLimiter,h as RecursionGuard,c as TRACE_HEADER,L as applyControlsToDdos,K as applyControlsToRateLimits,r as applyMitigationFactor,E as defaultRateLimitRules,J as describePosture,s as escapeSecLang,d as functionFingerprint,z as globMatches,u as globToRegex,g as inspectInvocation,t as isSafePattern,l as isValidCidr,M as mergeControlsIntoConfig,f as parseChain,q as planMitigation,i as propagationHeaders,D as rateLimitHeaders,B as rateLimitKey,j as recursionBlockedResponse,p as renderDdosInstallScript,o as renderNftablesRuleset,n as renderSysctlFile,v as renderWafConfig,w as renderWafInstallScript,A as ruleMatches,m as sysctlHardening,x as wafEventTelemetry};
@@ -1 +1 @@
1
- import"../chunk-z2qqwy7e.js";import*as h from"node:http2";import{createSign as b}from"node:crypto";var f="api.push.apple.com",k="api.sandbox.push.apple.com",S=2700000;class R{config;token=null;tokenGeneratedAt=0;client=null;host;constructor(e){this.config=e,this.host=e.production?f:k}generateToken(){let e=Math.floor(Date.now()/1000);if(this.token&&Date.now()-this.tokenGeneratedAt<S)return this.token;let t={alg:"ES256",kid:this.config.keyId},s={iss:this.config.teamId,iat:e},r=Buffer.from(JSON.stringify(t)).toString("base64url"),n=Buffer.from(JSON.stringify(s)).toString("base64url"),i=`${r}.${n}`,o=b("SHA256");o.update(i);let a=o.sign(this.config.privateKey),l=this.derToRaw(a).toString("base64url");return this.token=`${i}.${l}`,this.tokenGeneratedAt=Date.now(),this.token}derToRaw(e){let t=2;if(e[t]!==2)throw Error("Invalid DER signature: expected 0x02 for R");t++;let s=e[t];t++;let r=e.subarray(t,t+s);if(t+=s,e[t]!==2)throw Error("Invalid DER signature: expected 0x02 for S");t++;let n=e[t];t++;let i=e.subarray(t,t+n);if(r[0]===0&&r.length===33)r=r.subarray(1);if(i[0]===0&&i.length===33)i=i.subarray(1);let o=Buffer.alloc(64);return r.copy(o,32-r.length),i.copy(o,64-i.length),o}async getClient(){if(this.client&&!this.client.destroyed)return this.client;return new Promise((e,t)=>{this.client=h.connect(`https://${this.host}`),this.client.on("error",(s)=>{t(s)}),this.client.on("connect",()=>{e(this.client)}),this.client.on("close",()=>{this.client=null})})}buildPayload(e){let t={};if(e.title||e.body||e.subtitle){if(t.alert={},e.title)t.alert.title=e.title;if(e.subtitle)t.alert.subtitle=e.subtitle;if(e.body)t.alert.body=e.body}if(e.badge!==void 0)t.badge=e.badge;if(e.sound!==void 0)t.sound=e.sound;if(e.category)t.category=e.category;if(e.threadId)t["thread-id"]=e.threadId;if(e.contentAvailable)t["content-available"]=1;if(e.mutableContent)t["mutable-content"]=1;if(e.targetContentId)t["target-content-id"]=e.targetContentId;let s={aps:t};if(e.data)Object.assign(s,e.data);return s}async send(e){try{let t=await this.getClient(),s=this.generateToken(),r=JSON.stringify(this.buildPayload(e)),n={":method":"POST",":path":`/3/device/${e.deviceToken}`,authorization:`bearer ${s}`,"apns-topic":this.config.bundleId,"apns-push-type":e.pushType||"alert","apns-priority":String(e.priority||10)};if(e.expiration!==void 0)n["apns-expiration"]=String(e.expiration);if(e.collapseId)n["apns-collapse-id"]=e.collapseId;return new Promise((i)=>{let o=t.request(n),a="",d=0,l;o.on("response",(c)=>{d=c[":status"],l=c["apns-id"]}),o.on("data",(c)=>{a+=c.toString()}),o.on("end",()=>{if(d===200)i({success:!0,deviceToken:e.deviceToken,apnsId:l,statusCode:d});else{let c="Unknown error",g,u;if(a)try{let p=JSON.parse(a);g=p.reason,u=p.timestamp,c=g||c}catch{c=a}i({success:!1,deviceToken:e.deviceToken,apnsId:l,statusCode:d,error:c,reason:g,timestamp:u})}}),o.on("error",(c)=>{i({success:!1,deviceToken:e.deviceToken,error:c.message})}),o.write(r),o.end()})}catch(t){return{success:!1,deviceToken:e.deviceToken,error:t.message}}}async sendBatch(e,t){let s=t?.concurrency||10,r=[];for(let n=0;n<e.length;n+=s){let i=e.slice(n,n+s),o=await Promise.all(i.map((a)=>this.send(a)));r.push(...o)}return{sent:r.filter((n)=>n.success).length,failed:r.filter((n)=>!n.success).length,results:r}}async sendSimple(e,t,s,r){return this.send({deviceToken:e,title:t,body:s,data:r})}async sendSilent(e,t){return this.send({deviceToken:e,contentAvailable:!0,pushType:"background",priority:5,data:t})}close(){if(this.client)this.client.close(),this.client=null;this.token=null,this.tokenGeneratedAt=0}}import{createSign as T}from"node:crypto";var C="https://fcm.googleapis.com/v1/projects",y="https://oauth2.googleapis.com/token",P=3300000;class m{config;accessToken=null;tokenExpiresAt=0;constructor(e){this.config=e}static fromServiceAccount(e){return new m({projectId:e.project_id,clientEmail:e.client_email,privateKey:e.private_key})}generateJWT(){let e=Math.floor(Date.now()/1000),t=e+3600,s={alg:"RS256",typ:"JWT"},r={iss:this.config.clientEmail,sub:this.config.clientEmail,aud:y,iat:e,exp:t,scope:"https://www.googleapis.com/auth/firebase.messaging"},n=Buffer.from(JSON.stringify(s)).toString("base64url"),i=Buffer.from(JSON.stringify(r)).toString("base64url"),o=`${n}.${i}`,a=T("SHA256");a.update(o);let d=a.sign(this.config.privateKey,"base64url");return`${o}.${d}`}async getAccessToken(){if(this.accessToken&&Date.now()<this.tokenExpiresAt)return this.accessToken;let e=this.generateJWT(),t=await fetch(y,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:jwt-bearer",assertion:e}).toString()});if(!t.ok){let r=await t.text();throw Error(`Failed to get access token: ${r}`)}let s=await t.json();return this.accessToken=s.access_token,this.tokenExpiresAt=Date.now()+P,this.accessToken}buildMessage(e){let t={};if(e.token)t.token=e.token;else if(e.topic)t.topic=e.topic;else if(e.condition)t.condition=e.condition;if(e.title||e.body||e.imageUrl){if(t.notification={},e.title)t.notification.title=e.title;if(e.body)t.notification.body=e.body;if(e.imageUrl)t.notification.image=e.imageUrl}if(e.data&&Object.keys(e.data).length>0)t.data=e.data;if(e.android){if(t.android={priority:e.android.priority||"high"},e.android.ttl)t.android.ttl=`${e.android.ttl}s`;if(e.android.collapseKey)t.android.collapse_key=e.android.collapseKey;if(e.android.directBootOk)t.android.direct_boot_ok=e.android.directBootOk;let s={};if(e.android.channelId)s.channel_id=e.android.channelId;if(e.android.icon)s.icon=e.android.icon;if(e.android.color)s.color=e.android.color;if(e.android.sound)s.sound=e.android.sound;if(e.android.clickAction)s.click_action=e.android.clickAction;if(e.android.tag)s.tag=e.android.tag;if(e.android.visibility)s.visibility=e.android.visibility;if(e.android.notificationCount!==void 0)s.notification_count=e.android.notificationCount;if(Object.keys(s).length>0)t.android.notification=s}if(e.webpush)t.webpush=e.webpush;if(e.apns)t.apns=e.apns;if(e.fcmOptions)t.fcm_options=e.fcmOptions;return{message:t}}async send(e){try{let t=await this.getAccessToken(),s=this.buildMessage(e),r=await fetch(`${C}/${this.config.projectId}/messages:send`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(s)}),n=await r.json();if(r.ok)return{success:!0,messageId:n.name};else return{success:!1,error:n.error?.message||"Unknown error",errorCode:n.error?.status}}catch(t){return{success:!1,error:t.message}}}async sendBatch(e,t,s){let r=s?.concurrency||10,n=[];for(let i=0;i<e.length;i+=r){let a=e.slice(i,i+r).map(async(l)=>({...await this.send({...t,token:l}),token:l})),d=await Promise.all(a);n.push(...d)}return{sent:n.filter((i)=>i.success).length,failed:n.filter((i)=>!i.success).length,results:n}}async sendToTopic(e,t){return this.send({...t,topic:e})}async sendToCondition(e,t){return this.send({...t,condition:e})}async sendSimple(e,t,s,r){return this.send({token:e,title:t,body:s,data:r})}async sendSilent(e,t){return this.send({token:e,data:t,android:{priority:"high"}})}async subscribeToTopic(e,t){try{let s=await this.getAccessToken(),r=await fetch("https://iid.googleapis.com/iid/v1:batchAdd",{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${t}`,registration_tokens:e})});if(r.ok)return{success:!0};else return{success:!1,error:(await r.json()).error?.message||"Failed to subscribe"}}catch(s){return{success:!1,error:s.message}}}async unsubscribeFromTopic(e,t){try{let s=await this.getAccessToken(),r=await fetch("https://iid.googleapis.com/iid/v1:batchRemove",{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${t}`,registration_tokens:e})});if(r.ok)return{success:!0};else return{success:!1,error:(await r.json()).error?.message||"Failed to unsubscribe"}}catch(s){return{success:!1,error:s.message}}}}export{R as APNsClient,m as FCMClient};
1
+ import"../chunk-13qjvbqh.js";import*as h from"node:http2";import{createSign as b}from"node:crypto";var f="api.push.apple.com",k="api.sandbox.push.apple.com",S=2700000;class R{config;token=null;tokenGeneratedAt=0;client=null;host;constructor(e){this.config=e,this.host=e.production?f:k}generateToken(){let e=Math.floor(Date.now()/1000);if(this.token&&Date.now()-this.tokenGeneratedAt<S)return this.token;let t={alg:"ES256",kid:this.config.keyId},s={iss:this.config.teamId,iat:e},r=Buffer.from(JSON.stringify(t)).toString("base64url"),n=Buffer.from(JSON.stringify(s)).toString("base64url"),i=`${r}.${n}`,o=b("SHA256");o.update(i);let a=o.sign(this.config.privateKey),l=this.derToRaw(a).toString("base64url");return this.token=`${i}.${l}`,this.tokenGeneratedAt=Date.now(),this.token}derToRaw(e){let t=2;if(e[t]!==2)throw Error("Invalid DER signature: expected 0x02 for R");t++;let s=e[t];t++;let r=e.subarray(t,t+s);if(t+=s,e[t]!==2)throw Error("Invalid DER signature: expected 0x02 for S");t++;let n=e[t];t++;let i=e.subarray(t,t+n);if(r[0]===0&&r.length===33)r=r.subarray(1);if(i[0]===0&&i.length===33)i=i.subarray(1);let o=Buffer.alloc(64);return r.copy(o,32-r.length),i.copy(o,64-i.length),o}async getClient(){if(this.client&&!this.client.destroyed)return this.client;return new Promise((e,t)=>{this.client=h.connect(`https://${this.host}`),this.client.on("error",(s)=>{t(s)}),this.client.on("connect",()=>{e(this.client)}),this.client.on("close",()=>{this.client=null})})}buildPayload(e){let t={};if(e.title||e.body||e.subtitle){if(t.alert={},e.title)t.alert.title=e.title;if(e.subtitle)t.alert.subtitle=e.subtitle;if(e.body)t.alert.body=e.body}if(e.badge!==void 0)t.badge=e.badge;if(e.sound!==void 0)t.sound=e.sound;if(e.category)t.category=e.category;if(e.threadId)t["thread-id"]=e.threadId;if(e.contentAvailable)t["content-available"]=1;if(e.mutableContent)t["mutable-content"]=1;if(e.targetContentId)t["target-content-id"]=e.targetContentId;let s={aps:t};if(e.data)Object.assign(s,e.data);return s}async send(e){try{let t=await this.getClient(),s=this.generateToken(),r=JSON.stringify(this.buildPayload(e)),n={":method":"POST",":path":`/3/device/${e.deviceToken}`,authorization:`bearer ${s}`,"apns-topic":this.config.bundleId,"apns-push-type":e.pushType||"alert","apns-priority":String(e.priority||10)};if(e.expiration!==void 0)n["apns-expiration"]=String(e.expiration);if(e.collapseId)n["apns-collapse-id"]=e.collapseId;return new Promise((i)=>{let o=t.request(n),a="",d=0,l;o.on("response",(c)=>{d=c[":status"],l=c["apns-id"]}),o.on("data",(c)=>{a+=c.toString()}),o.on("end",()=>{if(d===200)i({success:!0,deviceToken:e.deviceToken,apnsId:l,statusCode:d});else{let c="Unknown error",g,u;if(a)try{let p=JSON.parse(a);g=p.reason,u=p.timestamp,c=g||c}catch{c=a}i({success:!1,deviceToken:e.deviceToken,apnsId:l,statusCode:d,error:c,reason:g,timestamp:u})}}),o.on("error",(c)=>{i({success:!1,deviceToken:e.deviceToken,error:c.message})}),o.write(r),o.end()})}catch(t){return{success:!1,deviceToken:e.deviceToken,error:t.message}}}async sendBatch(e,t){let s=t?.concurrency||10,r=[];for(let n=0;n<e.length;n+=s){let i=e.slice(n,n+s),o=await Promise.all(i.map((a)=>this.send(a)));r.push(...o)}return{sent:r.filter((n)=>n.success).length,failed:r.filter((n)=>!n.success).length,results:r}}async sendSimple(e,t,s,r){return this.send({deviceToken:e,title:t,body:s,data:r})}async sendSilent(e,t){return this.send({deviceToken:e,contentAvailable:!0,pushType:"background",priority:5,data:t})}close(){if(this.client)this.client.close(),this.client=null;this.token=null,this.tokenGeneratedAt=0}}import{createSign as T}from"node:crypto";var C="https://fcm.googleapis.com/v1/projects",y="https://oauth2.googleapis.com/token",P=3300000;class m{config;accessToken=null;tokenExpiresAt=0;constructor(e){this.config=e}static fromServiceAccount(e){return new m({projectId:e.project_id,clientEmail:e.client_email,privateKey:e.private_key})}generateJWT(){let e=Math.floor(Date.now()/1000),t=e+3600,s={alg:"RS256",typ:"JWT"},r={iss:this.config.clientEmail,sub:this.config.clientEmail,aud:y,iat:e,exp:t,scope:"https://www.googleapis.com/auth/firebase.messaging"},n=Buffer.from(JSON.stringify(s)).toString("base64url"),i=Buffer.from(JSON.stringify(r)).toString("base64url"),o=`${n}.${i}`,a=T("SHA256");a.update(o);let d=a.sign(this.config.privateKey,"base64url");return`${o}.${d}`}async getAccessToken(){if(this.accessToken&&Date.now()<this.tokenExpiresAt)return this.accessToken;let e=this.generateJWT(),t=await fetch(y,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:jwt-bearer",assertion:e}).toString()});if(!t.ok){let r=await t.text();throw Error(`Failed to get access token: ${r}`)}let s=await t.json();return this.accessToken=s.access_token,this.tokenExpiresAt=Date.now()+P,this.accessToken}buildMessage(e){let t={};if(e.token)t.token=e.token;else if(e.topic)t.topic=e.topic;else if(e.condition)t.condition=e.condition;if(e.title||e.body||e.imageUrl){if(t.notification={},e.title)t.notification.title=e.title;if(e.body)t.notification.body=e.body;if(e.imageUrl)t.notification.image=e.imageUrl}if(e.data&&Object.keys(e.data).length>0)t.data=e.data;if(e.android){if(t.android={priority:e.android.priority||"high"},e.android.ttl)t.android.ttl=`${e.android.ttl}s`;if(e.android.collapseKey)t.android.collapse_key=e.android.collapseKey;if(e.android.directBootOk)t.android.direct_boot_ok=e.android.directBootOk;let s={};if(e.android.channelId)s.channel_id=e.android.channelId;if(e.android.icon)s.icon=e.android.icon;if(e.android.color)s.color=e.android.color;if(e.android.sound)s.sound=e.android.sound;if(e.android.clickAction)s.click_action=e.android.clickAction;if(e.android.tag)s.tag=e.android.tag;if(e.android.visibility)s.visibility=e.android.visibility;if(e.android.notificationCount!==void 0)s.notification_count=e.android.notificationCount;if(Object.keys(s).length>0)t.android.notification=s}if(e.webpush)t.webpush=e.webpush;if(e.apns)t.apns=e.apns;if(e.fcmOptions)t.fcm_options=e.fcmOptions;return{message:t}}async send(e){try{let t=await this.getAccessToken(),s=this.buildMessage(e),r=await fetch(`${C}/${this.config.projectId}/messages:send`,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(s)}),n=await r.json();if(r.ok)return{success:!0,messageId:n.name};else return{success:!1,error:n.error?.message||"Unknown error",errorCode:n.error?.status}}catch(t){return{success:!1,error:t.message}}}async sendBatch(e,t,s){let r=s?.concurrency||10,n=[];for(let i=0;i<e.length;i+=r){let a=e.slice(i,i+r).map(async(l)=>({...await this.send({...t,token:l}),token:l})),d=await Promise.all(a);n.push(...d)}return{sent:n.filter((i)=>i.success).length,failed:n.filter((i)=>!i.success).length,results:n}}async sendToTopic(e,t){return this.send({...t,topic:e})}async sendToCondition(e,t){return this.send({...t,condition:e})}async sendSimple(e,t,s,r){return this.send({token:e,title:t,body:s,data:r})}async sendSilent(e,t){return this.send({token:e,data:t,android:{priority:"high"}})}async subscribeToTopic(e,t){try{let s=await this.getAccessToken(),r=await fetch("https://iid.googleapis.com/iid/v1:batchAdd",{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${t}`,registration_tokens:e})});if(r.ok)return{success:!0};else return{success:!1,error:(await r.json()).error?.message||"Failed to subscribe"}}catch(s){return{success:!1,error:s.message}}}async unsubscribeFromTopic(e,t){try{let s=await this.getAccessToken(),r=await fetch("https://iid.googleapis.com/iid/v1:batchRemove",{method:"POST",headers:{Authorization:`Bearer ${s}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${t}`,registration_tokens:e})});if(r.ok)return{success:!0};else return{success:!1,error:(await r.json()).error?.message||"Failed to unsubscribe"}}catch(s){return{success:!1,error:s.message}}}}export{R as APNsClient,m as FCMClient};
@@ -1 +1 @@
1
- import{$j as _,Aj as z,Bj as A,Cj as B,Dj as C,Ej as D,Fj as E,Gj as F,Hj as G,Ij as H,Jj as I,Jk as qa,Kj as J,Kk as ra,Lj as K,Lk as sa,Mj as L,Mk as ta,Nj as M,Nk as ua,Oj as N,Ok as va,Pj as O,Pk as wa,Qj as P,Qk as xa,Rj as Q,Rk as ya,Sj as R,Sk as za,Tj as S,Tk as Aa,Uj as T,Uk as Ba,Vj as U,Wj as V,Xj as W,Yj as X,Yk as Ca,Zj as Y,Zk as Da,_j as Z,ak as aa,bj as a,bk as ba,cj as b,ck as ca,dj as c,dk as da,ej as d,ek as ea,fj as e,fk as fa,gj as f,gk as ga,hj as g,hk as ha,ij as h,ik as ia,jj as i,jk as ja,kj as j,kk as ka,lj as k,lk as la,mj as l,mk as ma,nj as m,nk as na,oj as n,ok as oa,pj as o,pk as pa,qj as p,rj as q,sj as r,tj as s,uj as t,vj as u,wj as v,xj as w,yj as x,zj as y}from"../chunk-aw0cgv5w.js";import"../chunk-x3hay61c.js";import"../chunk-zz52xje8.js";import"../chunk-z2qqwy7e.js";export{Z as AnomalyConfigStore,za as AwsSpendTransport,Aa as ComputeSpendTransport,x as CounterTracker,u as DEFAULT_METER_MAPPINGS,l as DEFAULT_PRICE_BOOK,B as DEFAULT_THRESHOLDS,_ as DETECTABLE_SIGNALS,c as ENFORCEMENT_ACTIONS,K as ENFORCEMENT_DESCRIPTIONS,d as ENFORCEMENT_SEVERITY,ea as FOCUS_VERSION,b as METERS,a as METER_KEYS,e as MICRO_CENTS,G as MIN_PROJECTION_CONFIDENCE,pa as RecordingSpendTransport,X as SENSITIVITY_THRESHOLDS,xa as SPEND_CYCLE_SECONDS,ka as SPEND_GATE_SETTING,ua as SPEND_LEASE_SETTING,ba as SignalSource,ma as SpendCapError,la as SpendGate,va as SpendLoopLease,ta as SpendNotificationRouter,wa as SpendRunner,ia as SpendService,C as SpendStore,qa as URGENT_SPEND_EVENTS,L as USER_VISIBLE_ACTIONS,z as aggregateDeltas,W as anomalyOptionsForSignal,na as assertSpendAllows,q as budgetWindow,Ba as compositeSpendTransport,oa as createEnforcementHandlers,U as detectAnomalies,V as detectLatestAnomaly,I as evaluateBudget,T as ewma,v as findMapping,g as findPriceEntry,ra as formatCents,F as formatTimeToExhaustion,H as governingLimitCents,Ca as handleSpendRequest,s as hourBucket,t as hourBucketsBetween,P as isOperationAllowed,m as isValidTimeZone,da as lookbackHoursForSignal,Q as median,R as medianAbsoluteDeviation,J as mergeDecisions,k as mergePriceBooks,y as meterTelemetry,ca as optionsForSignal,E as percentOfLimit,M as planEnforcement,r as previousBudgetWindow,h as priceUsage,i as priceUsageLines,D as projectSpend,S as robustZScore,j as roundCents,Y as routeMatches,N as runEnforcement,A as scopeKey,aa as signalDefinition,sa as spendNotificationText,Da as spendOpenApiPaths,ya as startSpendLoop,ha as streamFocusJsonl,O as strongestActiveAction,f as tieredMicroCents,ga as toFocusJsonl,fa as toFocusRecords,w as usageDeltaKey,p as utcInstantOfLocal,ja as zeroFill,o as zoneOffsetMs,n as zonedParts};
1
+ import{$j as _,Aj as z,Bj as A,Cj as B,Dj as C,Ej as D,Fj as E,Gj as F,Hj as G,Ij as H,Jj as I,Jk as qa,Kj as J,Kk as ra,Lj as K,Lk as sa,Mj as L,Mk as ta,Nj as M,Nk as ua,Oj as N,Ok as va,Pj as O,Pk as wa,Qj as P,Qk as xa,Rj as Q,Rk as ya,Sj as R,Sk as za,Tj as S,Tk as Aa,Uj as T,Uk as Ba,Vj as U,Wj as V,Xj as W,Yj as X,Yk as Ca,Zj as Y,Zk as Da,_j as Z,ak as aa,bj as a,bk as ba,cj as b,ck as ca,dj as c,dk as da,ej as d,ek as ea,fj as e,fk as fa,gj as f,gk as ga,hj as g,hk as ha,ij as h,ik as ia,jj as i,jk as ja,kj as j,kk as ka,lj as k,lk as la,mj as l,mk as ma,nj as m,nk as na,oj as n,ok as oa,pj as o,pk as pa,qj as p,rj as q,sj as r,tj as s,uj as t,vj as u,wj as v,xj as w,yj as x,zj as y}from"../chunk-86ss83s2.js";import"../chunk-x3hay61c.js";import"../chunk-jm5mg890.js";import"../chunk-13qjvbqh.js";export{Z as AnomalyConfigStore,za as AwsSpendTransport,Aa as ComputeSpendTransport,x as CounterTracker,u as DEFAULT_METER_MAPPINGS,l as DEFAULT_PRICE_BOOK,B as DEFAULT_THRESHOLDS,_ as DETECTABLE_SIGNALS,c as ENFORCEMENT_ACTIONS,K as ENFORCEMENT_DESCRIPTIONS,d as ENFORCEMENT_SEVERITY,ea as FOCUS_VERSION,b as METERS,a as METER_KEYS,e as MICRO_CENTS,G as MIN_PROJECTION_CONFIDENCE,pa as RecordingSpendTransport,X as SENSITIVITY_THRESHOLDS,xa as SPEND_CYCLE_SECONDS,ka as SPEND_GATE_SETTING,ua as SPEND_LEASE_SETTING,ba as SignalSource,ma as SpendCapError,la as SpendGate,va as SpendLoopLease,ta as SpendNotificationRouter,wa as SpendRunner,ia as SpendService,C as SpendStore,qa as URGENT_SPEND_EVENTS,L as USER_VISIBLE_ACTIONS,z as aggregateDeltas,W as anomalyOptionsForSignal,na as assertSpendAllows,q as budgetWindow,Ba as compositeSpendTransport,oa as createEnforcementHandlers,U as detectAnomalies,V as detectLatestAnomaly,I as evaluateBudget,T as ewma,v as findMapping,g as findPriceEntry,ra as formatCents,F as formatTimeToExhaustion,H as governingLimitCents,Ca as handleSpendRequest,s as hourBucket,t as hourBucketsBetween,P as isOperationAllowed,m as isValidTimeZone,da as lookbackHoursForSignal,Q as median,R as medianAbsoluteDeviation,J as mergeDecisions,k as mergePriceBooks,y as meterTelemetry,ca as optionsForSignal,E as percentOfLimit,M as planEnforcement,r as previousBudgetWindow,h as priceUsage,i as priceUsageLines,D as projectSpend,S as robustZScore,j as roundCents,Y as routeMatches,N as runEnforcement,A as scopeKey,aa as signalDefinition,sa as spendNotificationText,Da as spendOpenApiPaths,ya as startSpendLoop,ha as streamFocusJsonl,O as strongestActiveAction,f as tieredMicroCents,ga as toFocusJsonl,fa as toFocusRecords,w as usageDeltaKey,p as utcInstantOfLocal,ja as zeroFill,o as zoneOffsetMs,n as zonedParts};
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="mt68a7naip9gue">
5
+ <meta name="stx-build" content="mt7l7lmt0d6evb">
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="mt689snetu3vh5"><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="mt7l77jbzlapbv"><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="mt689snetu3vh5"><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="mt7l77jbzlapbv"><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="mt689snetu3vh5"><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_1787514988755_0() {
1
+ <!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt7l77jbzlapbv"><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_1787597169246_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_1787514988755_0;
193
- if(window.stx)window.stx._latestSetup=__stx_setup_1787514988755_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_1787514988755_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_1787597169246_0;
193
+ if(window.stx)window.stx._latestSetup=__stx_setup_1787597169246_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_1787597169246_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="mt689snetu3vh5"><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_7" 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="mt7l77jbzlapbv"><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_3" 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_7'] = __scopeRegistration;
352
+ window.stx._scopes['stx_scope_nav_3'] = __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() {