@stacksjs/ts-cloud 0.11.7 → 0.12.1
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.
- package/dist/aws/index.js +1 -1
- package/dist/bin/chunk-0rtn6q5x.js +3 -0
- package/dist/bin/chunk-258ehp9s.js +1 -0
- package/dist/bin/chunk-2yctv06c.js +1 -0
- package/dist/bin/chunk-3276rveh.js +37 -0
- package/dist/bin/chunk-3m665jwp.js +70 -0
- package/dist/bin/chunk-3s2p9vxj.js +1 -0
- package/dist/bin/chunk-4esma5m9.js +1 -0
- package/dist/bin/chunk-4n0j3n3b.js +2 -0
- package/dist/bin/chunk-54bqchag.js +92 -0
- package/dist/bin/{chunk-4q6tad44.js → chunk-5bhx3bsa.js} +121 -121
- package/dist/bin/chunk-5zagqd20.js +1 -0
- package/dist/bin/chunk-7hw2js5h.js +512 -0
- package/dist/bin/chunk-8h1qr6x4.js +50 -0
- package/dist/bin/{chunk-w32xv8n4.js → chunk-9vdc7adf.js} +6 -6
- package/dist/bin/chunk-a1dd6sby.js +1 -0
- package/dist/bin/chunk-a4dw35gs.js +1 -0
- package/dist/bin/chunk-a9d55ker.js +1 -0
- package/dist/bin/chunk-awa1qade.js +9 -0
- package/dist/bin/chunk-b6by94pk.js +2 -0
- package/dist/bin/chunk-c8sypnbe.js +2 -0
- package/dist/bin/chunk-cx4e3d4w.js +1 -0
- package/dist/bin/chunk-d5a3vheh.js +2 -0
- package/dist/bin/chunk-e1fym2bd.js +1 -0
- package/dist/bin/chunk-ej4h0q2y.js +1 -0
- package/dist/bin/chunk-ett305ym.js +4 -0
- package/dist/bin/chunk-f11xf3n6.js +2 -0
- package/dist/bin/chunk-fq7ktvgy.js +3 -0
- package/dist/bin/chunk-fyq4th2m.js +1 -0
- package/dist/bin/chunk-g67qr8yf.js +1 -0
- package/dist/bin/chunk-gc82cx4h.js +1 -0
- package/dist/bin/chunk-hchtvpr9.js +1 -0
- package/dist/bin/chunk-hpe4jjce.js +1 -0
- package/dist/bin/chunk-k5vprsef.js +6 -0
- package/dist/bin/chunk-m22krd4z.js +3 -0
- package/dist/bin/chunk-mkppcwz8.js +1 -0
- package/dist/bin/chunk-mpjg0kc7.js +2 -0
- package/dist/bin/chunk-mrm4m4dk.js +2 -0
- package/dist/bin/chunk-nff46rxd.js +1 -0
- package/dist/bin/chunk-np1z3bgt.js +1 -0
- package/dist/bin/chunk-nskxkk6r.js +1 -0
- package/dist/bin/chunk-pez932b1.js +111 -0
- package/dist/bin/chunk-qsdf5kzk.js +2 -0
- package/dist/bin/chunk-r61qmhv1.js +2 -0
- package/dist/bin/chunk-r6gdcz5c.js +1 -0
- package/dist/bin/chunk-satwnr42.js +2 -0
- package/dist/bin/{chunk-dz1ec589.js → chunk-scqmd4qv.js} +30 -30
- package/dist/bin/chunk-smbycpt5.js +44 -0
- package/dist/bin/chunk-sn7e0sn7.js +2 -0
- package/dist/bin/chunk-ss574pka.js +1 -0
- package/dist/bin/chunk-tj0xb2d8.js +585 -0
- package/dist/bin/chunk-v3rzf9d0.js +141 -0
- package/dist/bin/chunk-wnpma6t3.js +34 -0
- package/dist/bin/chunk-xan0e052.js +5 -0
- package/dist/bin/chunk-xbk2y43r.js +60 -0
- package/dist/bin/chunk-xjp3v95e.js +6 -0
- package/dist/bin/chunk-xyt490vp.js +6 -0
- package/dist/bin/chunk-yn9r02gy.js +1 -0
- package/dist/bin/chunk-zhkhn9cc.js +1 -0
- package/dist/bin/cli.js +445 -430
- package/dist/bin/dashboard-server.js +1 -1
- package/dist/chunk-0zf72ngj.js +1 -0
- package/dist/chunk-2hpjte1d.js +704 -0
- package/dist/chunk-4m6gwhwr.js +5 -0
- package/dist/chunk-5m29dhke.js +34 -0
- package/dist/chunk-6fzeemcs.js +114 -0
- package/dist/{chunk-zz52xje8.js → chunk-6xcbfs2r.js} +111 -111
- package/dist/chunk-bpnhqb70.js +6 -0
- package/dist/chunk-ead1nkbr.js +60 -0
- package/dist/chunk-fea461tj.js +141 -0
- package/dist/chunk-fvjczc73.js +1 -0
- package/dist/chunk-gearw6rk.js +4 -0
- package/dist/chunk-j60hesb3.js +2 -0
- package/dist/chunk-pmgtmt3d.js +1 -0
- package/dist/chunk-prf2pkvq.js +5 -0
- package/dist/chunk-q0wffgw5.js +37 -0
- package/dist/chunk-qcemnt35.js +50 -0
- package/dist/chunk-qvbwsaay.js +1 -0
- package/dist/chunk-r2pbqcnz.js +2 -0
- package/dist/chunk-r6110zv2.js +1 -0
- package/dist/chunk-rr1yaj55.js +2 -0
- package/dist/chunk-wcrj06cg.js +44 -0
- package/dist/chunk-wte9mh18.js +92 -0
- package/dist/{chunk-aw0cgv5w.js → chunk-y1dh3znq.js} +52 -52
- package/dist/chunk-y6e86wjs.js +445 -0
- package/dist/chunk-y8b7ht4v.js +1 -0
- package/dist/chunk-yf487g6e.js +2 -0
- package/dist/chunk-zmmc9j5f.js +2 -0
- package/dist/deploy/index.js +1 -1
- package/dist/dns/index.js +1 -1
- package/dist/drivers/index.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +91 -91
- package/dist/operations/drained-sites.d.ts +56 -0
- package/dist/operations/drained-sites.test.d.ts +1 -0
- package/dist/pages/cloudflare-pages.d.ts +240 -0
- package/dist/pages/cloudflare-pages.test.d.ts +1 -0
- package/dist/pages/index.d.ts +2 -0
- package/dist/protection/index.js +1 -1
- package/dist/push/index.js +1 -1
- package/dist/spend/index.js +1 -1
- package/dist/ui/404.html +1 -1
- package/dist/ui/accept-invitation.html +1 -1
- package/dist/ui/access-denied.html +1 -1
- package/dist/ui/account/automation.html +6 -6
- package/dist/ui/account/security.html +2 -2
- package/dist/ui/applications/compose.html +6 -6
- package/dist/ui/applications/new.html +2 -2
- package/dist/ui/data/backups.html +80 -80
- package/dist/ui/data/services.html +78 -78
- package/dist/ui/data/volumes.html +50 -50
- package/dist/ui/index.html +50 -50
- package/dist/ui/infrastructure/topology.html +118 -118
- package/dist/ui/integrations.html +2 -2
- package/dist/ui/operations/alerts.html +48 -48
- package/dist/ui/operations/configuration.html +6 -6
- package/dist/ui/operations/jobs.html +75 -75
- package/dist/ui/operations/maintenance.html +6 -6
- package/dist/ui/operations/observability.html +82 -82
- package/dist/ui/operations/previews.html +6 -6
- package/dist/ui/operations/queue.html +6 -6
- package/dist/ui/operations/regions.html +6 -6
- package/dist/ui/operations/releases.html +6 -6
- package/dist/ui/operations/spend.html +54 -54
- package/dist/ui/operations/workloads.html +104 -104
- package/dist/ui/security.html +2 -2
- package/dist/ui/server/actions.html +56 -56
- package/dist/ui/server/activity.html +2 -2
- package/dist/ui/server/capacity.html +6 -6
- package/dist/ui/server/database.html +105 -105
- package/dist/ui/server/deployments.html +50 -50
- package/dist/ui/server/diagnostics.html +2 -2
- package/dist/ui/server/firewall.html +76 -76
- package/dist/ui/server/fleet.html +34 -34
- package/dist/ui/server/logs.html +24 -24
- package/dist/ui/server/metrics.html +112 -112
- package/dist/ui/server/security.html +1 -1
- package/dist/ui/server/services.html +26 -26
- package/dist/ui/server/sites.html +120 -120
- package/dist/ui/server/ssh-keys.html +52 -52
- package/dist/ui/server/team.html +144 -144
- package/dist/ui/server/terminal.html +15 -15
- package/dist/ui/serverless/alarms.html +52 -52
- package/dist/ui/serverless/assets.html +27 -27
- package/dist/ui/serverless/cost.html +2 -2
- package/dist/ui/serverless/data.html +35 -35
- package/dist/ui/serverless/deployments.html +27 -27
- package/dist/ui/serverless/firewall.html +2 -2
- package/dist/ui/serverless/functions.html +40 -40
- package/dist/ui/serverless/logs.html +24 -24
- package/dist/ui/serverless/metrics.html +2 -2
- package/dist/ui/serverless/queues.html +48 -48
- package/dist/ui/serverless/secrets.html +52 -52
- package/dist/ui/serverless/traces.html +6 -6
- package/dist/ui/serverless.html +53 -53
- package/dist/ui-src/src/charts.ts +8721 -13558
- package/dist/ui-src/src/topology.ts +2 -2
- package/package.json +4 -3
- package/dist/bin/chunk-0zwknfwp.js +0 -1
- package/dist/bin/chunk-3aph4c1w.js +0 -2
- package/dist/bin/chunk-4xhj37nj.js +0 -37
- package/dist/bin/chunk-5nkqza92.js +0 -1
- package/dist/bin/chunk-5nsqz5n3.js +0 -585
- package/dist/bin/chunk-64vczwwt.js +0 -1
- package/dist/bin/chunk-6tfwm33r.js +0 -6
- package/dist/bin/chunk-7g2ahkse.js +0 -1
- package/dist/bin/chunk-7zhgmp1s.js +0 -3
- package/dist/bin/chunk-82mj52xr.js +0 -141
- package/dist/bin/chunk-8khgprf6.js +0 -44
- package/dist/bin/chunk-9a88fp00.js +0 -2
- package/dist/bin/chunk-a7w13qfm.js +0 -2
- package/dist/bin/chunk-b4wt9jcg.js +0 -60
- package/dist/bin/chunk-btsyjhk7.js +0 -92
- package/dist/bin/chunk-c0bdafbx.js +0 -2
- package/dist/bin/chunk-cbsys5v8.js +0 -111
- package/dist/bin/chunk-cd0ps58j.js +0 -1
- package/dist/bin/chunk-d7ke90zt.js +0 -2
- package/dist/bin/chunk-djg4zyqk.js +0 -1
- package/dist/bin/chunk-dkaz333b.js +0 -1
- package/dist/bin/chunk-dkf4qsp2.js +0 -1
- package/dist/bin/chunk-e2bybahr.js +0 -3
- package/dist/bin/chunk-e5ab5twy.js +0 -50
- package/dist/bin/chunk-edvvx3hg.js +0 -1
- package/dist/bin/chunk-ehc63fvs.js +0 -2
- package/dist/bin/chunk-f06c5np9.js +0 -1
- package/dist/bin/chunk-g0fm1xcs.js +0 -1
- package/dist/bin/chunk-gq8794k6.js +0 -1
- package/dist/bin/chunk-hq9x489e.js +0 -70
- package/dist/bin/chunk-j447p8t4.js +0 -512
- package/dist/bin/chunk-ja1vjdn1.js +0 -6
- package/dist/bin/chunk-k3120rx6.js +0 -1
- package/dist/bin/chunk-kg61p5hw.js +0 -3
- package/dist/bin/chunk-mcr0r4at.js +0 -2
- package/dist/bin/chunk-mfrf9q4b.js +0 -2
- package/dist/bin/chunk-my3ewh3a.js +0 -34
- package/dist/bin/chunk-nw3d4k52.js +0 -1
- package/dist/bin/chunk-p70bc9yr.js +0 -1
- package/dist/bin/chunk-r6b3pq31.js +0 -4
- package/dist/bin/chunk-s4yzes15.js +0 -2
- package/dist/bin/chunk-sj5csg49.js +0 -2
- package/dist/bin/chunk-tvz9c388.js +0 -1
- package/dist/bin/chunk-wkx799z2.js +0 -1
- package/dist/bin/chunk-x6f61g36.js +0 -9
- package/dist/bin/chunk-xhwqpgwq.js +0 -2
- package/dist/bin/chunk-xk08030d.js +0 -6
- package/dist/bin/chunk-y1h63vpm.js +0 -1
- package/dist/bin/chunk-yw6f160x.js +0 -1
- package/dist/bin/chunk-zdt2rjk6.js +0 -1
- package/dist/bin/chunk-zh2d4704.js +0 -1
- package/dist/bin/chunk-znnawt0k.js +0 -1
- package/dist/bin/chunk-zpe64t4r.js +0 -1
- package/dist/bin/chunk-zsf6pccz.js +0 -1
- package/dist/chunk-1qdne0bf.js +0 -1
- package/dist/chunk-5vt93kxx.js +0 -1
- package/dist/chunk-73ztr55z.js +0 -2
- package/dist/chunk-95e6aeqz.js +0 -1
- package/dist/chunk-aj025v2s.js +0 -4
- package/dist/chunk-aw0spg20.js +0 -445
- package/dist/chunk-by2bwzk6.js +0 -141
- package/dist/chunk-dr4pj8hp.js +0 -5
- package/dist/chunk-hn40z11p.js +0 -37
- package/dist/chunk-kasdm9tx.js +0 -34
- package/dist/chunk-mz0dk78z.js +0 -6
- package/dist/chunk-nk7sav4r.js +0 -92
- package/dist/chunk-p10xt3jb.js +0 -1
- package/dist/chunk-q0rc1ax9.js +0 -114
- package/dist/chunk-q9q6cmt0.js +0 -1
- package/dist/chunk-rd38qdnf.js +0 -1
- package/dist/chunk-tz9myq9a.js +0 -2
- package/dist/chunk-wavjkdb8.js +0 -2
- package/dist/chunk-x3hay61c.js +0 -44
- package/dist/chunk-xcj8zfmg.js +0 -2
- package/dist/chunk-y4xkj2f1.js +0 -50
- package/dist/chunk-ywjkyg8p.js +0 -704
- package/dist/chunk-z2qqwy7e.js +0 -2
- package/dist/chunk-zam6ecam.js +0 -60
|
@@ -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,240 @@
|
|
|
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
|
+
* Record hashes as present in the account's asset store.
|
|
129
|
+
*
|
|
130
|
+
* Uploading bytes is not by itself enough to make them findable: until the
|
|
131
|
+
* hashes are upserted, `check-missing` keeps reporting them absent, and every
|
|
132
|
+
* subsequent deploy re-uploads the entire site. Skipping this call is not
|
|
133
|
+
* visible in a single deploy — it only shows up as an upload that never gets
|
|
134
|
+
* cheaper.
|
|
135
|
+
*
|
|
136
|
+
* @param jwt - Upload token
|
|
137
|
+
* @param hashes - Every hash in the manifest, not only the newly uploaded ones
|
|
138
|
+
*/
|
|
139
|
+
private upsertHashes;
|
|
140
|
+
/**
|
|
141
|
+
* Upload one batch of assets.
|
|
142
|
+
*
|
|
143
|
+
* @param jwt - Upload token
|
|
144
|
+
* @param assets - At most {@link UPLOAD_BATCH_SIZE} assets
|
|
145
|
+
*/
|
|
146
|
+
private uploadBatch;
|
|
147
|
+
/**
|
|
148
|
+
* Create a deployment from an already-hashed manifest.
|
|
149
|
+
*
|
|
150
|
+
* @param project - Project name
|
|
151
|
+
* @param assets - Every file in the site
|
|
152
|
+
* @param branch - Branch to record. Matching the production branch publishes
|
|
153
|
+
* to the live domain; anything else becomes a preview deployment.
|
|
154
|
+
* @returns The created deployment
|
|
155
|
+
*/
|
|
156
|
+
createDeployment(project: string, assets: PagesAsset[], branch?: string): Promise<CloudflarePagesDeployment>;
|
|
157
|
+
/**
|
|
158
|
+
* Build, upload and deploy a directory.
|
|
159
|
+
*
|
|
160
|
+
* The whole flow: hash every file, ask what is missing, upload only that,
|
|
161
|
+
* then create the deployment.
|
|
162
|
+
*
|
|
163
|
+
* @param options.project - Project name
|
|
164
|
+
* @param options.directory - Directory to publish; becomes the site root
|
|
165
|
+
* @param options.branch - Branch to record on the deployment
|
|
166
|
+
* @param options.onProgress - Called as batches complete, for CLI output
|
|
167
|
+
* @returns The deployment and what it cost to send
|
|
168
|
+
* @throws {Error} When the directory is empty or a file exceeds the size limit
|
|
169
|
+
*/
|
|
170
|
+
deployDirectory(options: {
|
|
171
|
+
project: string;
|
|
172
|
+
directory: string;
|
|
173
|
+
branch?: string;
|
|
174
|
+
onProgress?: (uploaded: number, total: number) => void;
|
|
175
|
+
}): Promise<DeployDirectoryResult>;
|
|
176
|
+
/** Custom domains already attached to a project. */
|
|
177
|
+
listCustomDomains(project: string): Promise<string[]>;
|
|
178
|
+
/**
|
|
179
|
+
* Attach a custom domain, or leave it attached.
|
|
180
|
+
*
|
|
181
|
+
* Idempotent so a deploy pipeline can call it on every run rather than
|
|
182
|
+
* guarding with a lookup that races.
|
|
183
|
+
*
|
|
184
|
+
* @param project - Project name
|
|
185
|
+
* @param domain - Hostname to serve, e.g. `buddy.sh`
|
|
186
|
+
* @returns Whether this call is what attached it
|
|
187
|
+
*/
|
|
188
|
+
attachCustomDomain(project: string, domain: string): Promise<boolean>;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The DNS record that points a custom domain at a Pages project.
|
|
192
|
+
*
|
|
193
|
+
* Pages serves from a `*.pages.dev` host, so the zone needs a CNAME. Two things
|
|
194
|
+
* are easy to get wrong here:
|
|
195
|
+
*
|
|
196
|
+
* - **The target is the project's `subdomain`, not `<name>.pages.dev`.**
|
|
197
|
+
* Cloudflare only grants the bare name when it is free; a project named
|
|
198
|
+
* `buddy` whose name was taken is served from `buddy-5yo.pages.dev`.
|
|
199
|
+
* Deriving the host from the project name instead of reading `subdomain`
|
|
200
|
+
* produces a CNAME to a host that does not exist.
|
|
201
|
+
* - **It must be proxied.** An unproxied CNAME reaches Cloudflare's edge but
|
|
202
|
+
* arrives without the routing metadata Pages uses to pick a project, and
|
|
203
|
+
* answers 404.
|
|
204
|
+
*
|
|
205
|
+
* A CNAME at the apex is legal here only because Cloudflare flattens it.
|
|
206
|
+
*
|
|
207
|
+
* @param hostname - Public hostname, e.g. `buddy.sh` or `www.buddy.sh`
|
|
208
|
+
* @param subdomain - The project's `subdomain`, e.g. `buddy-5yo.pages.dev`
|
|
209
|
+
* @returns A record ready for `CloudflareProvider.upsertRecord`
|
|
210
|
+
*/
|
|
211
|
+
export declare function pagesDnsRecord(hostname: string, subdomain: string): DnsRecord;
|
|
212
|
+
/**
|
|
213
|
+
* Cloudflare's asset hash: BLAKE3 of `<base64><extension>`, first 32 hex chars.
|
|
214
|
+
*
|
|
215
|
+
* The extension is part of the input, so the same bytes saved as `.html` and
|
|
216
|
+
* as `.txt` hash differently — which is deliberate, since the asset store
|
|
217
|
+
* serves content type from the key.
|
|
218
|
+
*
|
|
219
|
+
* BLAKE3 specifically, and it has to come from a library: Bun's `CryptoHasher`
|
|
220
|
+
* ships blake2b but not blake3. Substituting a different digest is not a
|
|
221
|
+
* degradation that shows up as an error — the upload succeeds and the site
|
|
222
|
+
* then serves 404s, because the manifest names a key the store does not hold.
|
|
223
|
+
*
|
|
224
|
+
* @param base64 - Base64 of the file's bytes
|
|
225
|
+
* @param extension - Extension without the dot
|
|
226
|
+
*/
|
|
227
|
+
export declare function pagesAssetHash(base64: string, extension: string): Promise<string>;
|
|
228
|
+
/**
|
|
229
|
+
* Walk a directory into a deployable manifest.
|
|
230
|
+
*
|
|
231
|
+
* Dotfiles are included: a built site legitimately contains `.well-known`, and
|
|
232
|
+
* bunpress writes its output under a `.bunpress` root. Cloudflare's own
|
|
233
|
+
* reserved paths are skipped, since uploading them as assets shadows the
|
|
234
|
+
* configuration they are meant to be.
|
|
235
|
+
*
|
|
236
|
+
* @param directory - Site root
|
|
237
|
+
* @returns One entry per file, hashed and base64-encoded
|
|
238
|
+
* @throws {Error} When a file exceeds {@link PAGES_MAX_FILE_BYTES}
|
|
239
|
+
*/
|
|
240
|
+
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';
|
package/dist/protection/index.js
CHANGED
|
@@ -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-
|
|
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-fvjczc73.js";import{nl as s,ol as t,pl as u,ql as v,rl as w,sl as x}from"../chunk-q0wffgw5.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-wcrj06cg.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-6xcbfs2r.js";import"../chunk-rr1yaj55.js";export{x as wafEventTelemetry,m as sysctlHardening,A as ruleMatches,w as renderWafInstallScript,v as renderWafConfig,n as renderSysctlFile,o as renderNftablesRuleset,p as renderDdosInstallScript,j as recursionBlockedResponse,B as rateLimitKey,D as rateLimitHeaders,i as propagationHeaders,q as planMitigation,f as parseChain,M as mergeControlsIntoConfig,l as isValidCidr,t as isSafePattern,g as inspectInvocation,u as globToRegex,z as globMatches,d as functionFingerprint,s as escapeSecLang,J as describePosture,E as defaultRateLimitRules,r as applyMitigationFactor,K as applyControlsToRateLimits,L as applyControlsToDdos,c as TRACE_HEADER,h as RecursionGuard,C as RateLimiter,y as RATE_LIMIT_KEY_SEPARATOR,I as ProtectionControlStore,F as PROTECTION_SETTING,G as MAX_CONTROL_HOURS,a as DEPTH_HEADER,e as DEFAULT_RECURSION_LIMITS,k as DEFAULT_DDOS_THRESHOLDS,H as DEFAULT_ATTACK_MODE_HOURS,b as CHAIN_HEADER};
|
package/dist/push/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import"../chunk-
|
|
1
|
+
import"../chunk-rr1yaj55.js";import*as K from"node:http2";import{createSign as U}from"node:crypto";var q="api.push.apple.com",D="api.sandbox.push.apple.com",M=2700000;class E{config;token=null;tokenGeneratedAt=0;client=null;host;constructor(z){this.config=z,this.host=z.production?q:D}generateToken(){let z=Math.floor(Date.now()/1000);if(this.token&&Date.now()-this.tokenGeneratedAt<M)return this.token;let J={alg:"ES256",kid:this.config.keyId},Q={iss:this.config.teamId,iat:z},V=Buffer.from(JSON.stringify(J)).toString("base64url"),W=Buffer.from(JSON.stringify(Q)).toString("base64url"),Z=`${V}.${W}`,$=U("SHA256");$.update(Z);let B=$.sign(this.config.privateKey),x=this.derToRaw(B).toString("base64url");return this.token=`${Z}.${x}`,this.tokenGeneratedAt=Date.now(),this.token}derToRaw(z){let J=2;if(z[J]!==2)throw Error("Invalid DER signature: expected 0x02 for R");J++;let Q=z[J];J++;let V=z.subarray(J,J+Q);if(J+=Q,z[J]!==2)throw Error("Invalid DER signature: expected 0x02 for S");J++;let W=z[J];J++;let Z=z.subarray(J,J+W);if(V[0]===0&&V.length===33)V=V.subarray(1);if(Z[0]===0&&Z.length===33)Z=Z.subarray(1);let $=Buffer.alloc(64);return V.copy($,32-V.length),Z.copy($,64-Z.length),$}async getClient(){if(this.client&&!this.client.destroyed)return this.client;return new Promise((z,J)=>{this.client=K.connect(`https://${this.host}`),this.client.on("error",(Q)=>{J(Q)}),this.client.on("connect",()=>{z(this.client)}),this.client.on("close",()=>{this.client=null})})}buildPayload(z){let J={};if(z.title||z.body||z.subtitle){if(J.alert={},z.title)J.alert.title=z.title;if(z.subtitle)J.alert.subtitle=z.subtitle;if(z.body)J.alert.body=z.body}if(z.badge!==void 0)J.badge=z.badge;if(z.sound!==void 0)J.sound=z.sound;if(z.category)J.category=z.category;if(z.threadId)J["thread-id"]=z.threadId;if(z.contentAvailable)J["content-available"]=1;if(z.mutableContent)J["mutable-content"]=1;if(z.targetContentId)J["target-content-id"]=z.targetContentId;let Q={aps:J};if(z.data)Object.assign(Q,z.data);return Q}async send(z){try{let J=await this.getClient(),Q=this.generateToken(),V=JSON.stringify(this.buildPayload(z)),W={":method":"POST",":path":`/3/device/${z.deviceToken}`,authorization:`bearer ${Q}`,"apns-topic":this.config.bundleId,"apns-push-type":z.pushType||"alert","apns-priority":String(z.priority||10)};if(z.expiration!==void 0)W["apns-expiration"]=String(z.expiration);if(z.collapseId)W["apns-collapse-id"]=z.collapseId;return new Promise((Z)=>{let $=J.request(W),B="",j=0,x;$.on("response",(Y)=>{j=Y[":status"],x=Y["apns-id"]}),$.on("data",(Y)=>{B+=Y.toString()}),$.on("end",()=>{if(j===200)Z({success:!0,deviceToken:z.deviceToken,apnsId:x,statusCode:j});else{let Y="Unknown error",X,w;if(B)try{let G=JSON.parse(B);X=G.reason,w=G.timestamp,Y=X||Y}catch{Y=B}Z({success:!1,deviceToken:z.deviceToken,apnsId:x,statusCode:j,error:Y,reason:X,timestamp:w})}}),$.on("error",(Y)=>{Z({success:!1,deviceToken:z.deviceToken,error:Y.message})}),$.write(V),$.end()})}catch(J){return{success:!1,deviceToken:z.deviceToken,error:J.message}}}async sendBatch(z,J){let Q=J?.concurrency||10,V=[];for(let W=0;W<z.length;W+=Q){let Z=z.slice(W,W+Q),$=await Promise.all(Z.map((B)=>this.send(B)));V.push(...$)}return{sent:V.filter((W)=>W.success).length,failed:V.filter((W)=>!W.success).length,results:V}}async sendSimple(z,J,Q,V){return this.send({deviceToken:z,title:J,body:Q,data:V})}async sendSilent(z,J){return this.send({deviceToken:z,contentAvailable:!0,pushType:"background",priority:5,data:J})}close(){if(this.client)this.client.close(),this.client=null;this.token=null,this.tokenGeneratedAt=0}}import{createSign as L}from"node:crypto";var R="https://fcm.googleapis.com/v1/projects",F="https://oauth2.googleapis.com/token",b=3300000;class H{config;accessToken=null;tokenExpiresAt=0;constructor(z){this.config=z}static fromServiceAccount(z){return new H({projectId:z.project_id,clientEmail:z.client_email,privateKey:z.private_key})}generateJWT(){let z=Math.floor(Date.now()/1000),J=z+3600,Q={alg:"RS256",typ:"JWT"},V={iss:this.config.clientEmail,sub:this.config.clientEmail,aud:F,iat:z,exp:J,scope:"https://www.googleapis.com/auth/firebase.messaging"},W=Buffer.from(JSON.stringify(Q)).toString("base64url"),Z=Buffer.from(JSON.stringify(V)).toString("base64url"),$=`${W}.${Z}`,B=L("SHA256");B.update($);let j=B.sign(this.config.privateKey,"base64url");return`${$}.${j}`}async getAccessToken(){if(this.accessToken&&Date.now()<this.tokenExpiresAt)return this.accessToken;let z=this.generateJWT(),J=await fetch(F,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:jwt-bearer",assertion:z}).toString()});if(!J.ok){let V=await J.text();throw Error(`Failed to get access token: ${V}`)}let Q=await J.json();return this.accessToken=Q.access_token,this.tokenExpiresAt=Date.now()+b,this.accessToken}buildMessage(z){let J={};if(z.token)J.token=z.token;else if(z.topic)J.topic=z.topic;else if(z.condition)J.condition=z.condition;if(z.title||z.body||z.imageUrl){if(J.notification={},z.title)J.notification.title=z.title;if(z.body)J.notification.body=z.body;if(z.imageUrl)J.notification.image=z.imageUrl}if(z.data&&Object.keys(z.data).length>0)J.data=z.data;if(z.android){if(J.android={priority:z.android.priority||"high"},z.android.ttl)J.android.ttl=`${z.android.ttl}s`;if(z.android.collapseKey)J.android.collapse_key=z.android.collapseKey;if(z.android.directBootOk)J.android.direct_boot_ok=z.android.directBootOk;let Q={};if(z.android.channelId)Q.channel_id=z.android.channelId;if(z.android.icon)Q.icon=z.android.icon;if(z.android.color)Q.color=z.android.color;if(z.android.sound)Q.sound=z.android.sound;if(z.android.clickAction)Q.click_action=z.android.clickAction;if(z.android.tag)Q.tag=z.android.tag;if(z.android.visibility)Q.visibility=z.android.visibility;if(z.android.notificationCount!==void 0)Q.notification_count=z.android.notificationCount;if(Object.keys(Q).length>0)J.android.notification=Q}if(z.webpush)J.webpush=z.webpush;if(z.apns)J.apns=z.apns;if(z.fcmOptions)J.fcm_options=z.fcmOptions;return{message:J}}async send(z){try{let J=await this.getAccessToken(),Q=this.buildMessage(z),V=await fetch(`${R}/${this.config.projectId}/messages:send`,{method:"POST",headers:{Authorization:`Bearer ${J}`,"Content-Type":"application/json"},body:JSON.stringify(Q)}),W=await V.json();if(V.ok)return{success:!0,messageId:W.name};else return{success:!1,error:W.error?.message||"Unknown error",errorCode:W.error?.status}}catch(J){return{success:!1,error:J.message}}}async sendBatch(z,J,Q){let V=Q?.concurrency||10,W=[];for(let Z=0;Z<z.length;Z+=V){let B=z.slice(Z,Z+V).map(async(x)=>{return{...await this.send({...J,token:x}),token:x}}),j=await Promise.all(B);W.push(...j)}return{sent:W.filter((Z)=>Z.success).length,failed:W.filter((Z)=>!Z.success).length,results:W}}async sendToTopic(z,J){return this.send({...J,topic:z})}async sendToCondition(z,J){return this.send({...J,condition:z})}async sendSimple(z,J,Q,V){return this.send({token:z,title:J,body:Q,data:V})}async sendSilent(z,J){return this.send({token:z,data:J,android:{priority:"high"}})}async subscribeToTopic(z,J){try{let Q=await this.getAccessToken(),V=await fetch("https://iid.googleapis.com/iid/v1:batchAdd",{method:"POST",headers:{Authorization:`Bearer ${Q}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${J}`,registration_tokens:z})});if(V.ok)return{success:!0};else return{success:!1,error:(await V.json()).error?.message||"Failed to subscribe"}}catch(Q){return{success:!1,error:Q.message}}}async unsubscribeFromTopic(z,J){try{let Q=await this.getAccessToken(),V=await fetch("https://iid.googleapis.com/iid/v1:batchRemove",{method:"POST",headers:{Authorization:`Bearer ${Q}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${J}`,registration_tokens:z})});if(V.ok)return{success:!0};else return{success:!1,error:(await V.json()).error?.message||"Failed to unsubscribe"}}catch(Q){return{success:!1,error:Q.message}}}}export{H as FCMClient,E as APNsClient};
|
package/dist/spend/index.js
CHANGED
|
@@ -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
|
|
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 pa,Kj as J,Kk as qa,Lj as K,Lk as ra,Mj as L,Mk as sa,Nj as M,Nk as ta,Oj as N,Ok as ua,Pj as O,Pk as va,Qj as P,Qk as wa,Rj as Q,Rk as xa,Sj as R,Sk as ya,Tj as S,Tk as za,Uj as T,Uk as Aa,Vj as U,Wj as V,Xj as W,Yj as X,Yk as Ba,Zj as Y,Zk as Ca,_j as Z,ak as $,bj as a,bk as aa,cj as b,ck as ba,dj as c,dk as ca,ej as d,ek as da,fj as e,fk as ea,gj as f,gk as fa,hj as g,hk as ga,ij as h,ik as ha,jj as i,jk as ia,kj as j,kk as ja,lj as k,lk as ka,mj as l,mk as la,nj as m,nk as ma,oj as n,ok as na,pj as o,pk as oa,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-y1dh3znq.js";import"../chunk-wcrj06cg.js";import"../chunk-6xcbfs2r.js";import"../chunk-rr1yaj55.js";export{n as zonedParts,o as zoneOffsetMs,ia as zeroFill,p as utcInstantOfLocal,w as usageDeltaKey,ea as toFocusRecords,fa as toFocusJsonl,f as tieredMicroCents,O as strongestActiveAction,ga as streamFocusJsonl,xa as startSpendLoop,Ca as spendOpenApiPaths,ra as spendNotificationText,$ as signalDefinition,A as scopeKey,N as runEnforcement,Y as routeMatches,j as roundCents,S as robustZScore,D as projectSpend,i as priceUsageLines,h as priceUsage,r as previousBudgetWindow,M as planEnforcement,E as percentOfLimit,ba as optionsForSignal,y as meterTelemetry,k as mergePriceBooks,J as mergeDecisions,R as medianAbsoluteDeviation,Q as median,ca as lookbackHoursForSignal,m as isValidTimeZone,P as isOperationAllowed,t as hourBucketsBetween,s as hourBucket,Ba as handleSpendRequest,H as governingLimitCents,F as formatTimeToExhaustion,qa as formatCents,g as findPriceEntry,v as findMapping,T as ewma,I as evaluateBudget,V as detectLatestAnomaly,U as detectAnomalies,na as createEnforcementHandlers,Aa as compositeSpendTransport,q as budgetWindow,ma as assertSpendAllows,W as anomalyOptionsForSignal,z as aggregateDeltas,L as USER_VISIBLE_ACTIONS,pa as URGENT_SPEND_EVENTS,C as SpendStore,ha as SpendService,va as SpendRunner,sa as SpendNotificationRouter,ua as SpendLoopLease,ka as SpendGate,la as SpendCapError,aa as SignalSource,ta as SPEND_LEASE_SETTING,ja as SPEND_GATE_SETTING,wa as SPEND_CYCLE_SECONDS,X as SENSITIVITY_THRESHOLDS,oa as RecordingSpendTransport,G as MIN_PROJECTION_CONFIDENCE,e as MICRO_CENTS,a as METER_KEYS,b as METERS,da as FOCUS_VERSION,d as ENFORCEMENT_SEVERITY,K as ENFORCEMENT_DESCRIPTIONS,c as ENFORCEMENT_ACTIONS,_ as DETECTABLE_SIGNALS,B as DEFAULT_THRESHOLDS,l as DEFAULT_PRICE_BOOK,u as DEFAULT_METER_MAPPINGS,x as CounterTracker,za as ComputeSpendTransport,ya as AwsSpendTransport,Z as AnomalyConfigStore};
|
package/dist/ui/404.html
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt7lgvq3gjbx1h"><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="
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt7lgvq3gjbx1h"><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 & secrets</a><a class="" href="/data/backups?env=production">Backups & 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="
|
|
2
|
-
function
|
|
1
|
+
<!DOCTYPE html><html lang="en" data-theme="dark"><head><meta charset="UTF-8"><meta name="stx-build" content="mt7lgvq3gjbx1h"><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 & 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_1787597620495_7() {
|
|
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=
|
|
193
|
-
if(window.stx)window.stx._latestSetup=
|
|
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="
|
|
192
|
+
window.__stx_latestSetup=__stx_setup_1787597620495_7;
|
|
193
|
+
if(window.stx)window.stx._latestSetup=__stx_setup_1787597620495_7;
|
|
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_1787597620495_7"><a data-stx-scope="stx_scope_nav_10" 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 & secrets</a><a class="" href="/data/backups?env=production">Backups & 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 = [];
|
|
@@ -542,6 +542,6 @@ if(window.stx)window.stx._latestSetup=__stx_setup_1787514988755_0;
|
|
|
542
542
|
|
|
543
543
|
|
|
544
544
|
try { if (typeof __stxScopeRenames !== 'undefined' && __stxScopeRenames) { for (var __stxRK in __stxScopeRenames) __scopeRegistration[__stxRK] = __stxScopeRenames[__stxRK]; } } catch (e) { /* no renames */ }
|
|
545
|
-
window.stx._scopes['
|
|
545
|
+
window.stx._scopes['stx_scope_nav_10'] = __scopeRegistration;
|
|
546
546
|
})();
|
|
547
547
|
</script> <div class="wrap"><div class="crumbs"><a href="/">Dashboard</a><span class="sep">/</span> Organization <span class="sep">/</span> API & automation</div><header class="page"><div><h1>API & automation</h1><p>Create least-privilege machine identities for CI. Tokens never create browser sessions and cannot access outside their membership and token scope.</p></div><a class="btn ghost" href="/api/v1/openapi.json">OpenAPI 3.1</a></header><div class="automation-message msg" :class="messageKind()" role="status" aria-live="polite" x-cloak>{{ message() }}</div><div class="panel reveal" @show="!!reveal()" x-cloak><div><strong>Copy this token now</strong><span>The raw secret is shown once. Rotation keeps the old token active until you explicitly revoke it.</span></div><code x-cloak>{{ reveal()?.secret }}</code><button class="btn sm" type="button" @click="copy(reveal()?.secret)">Copy token</button><label>curl</label><code x-cloak>{{ curlExample() }}</code><button class="btn ghost sm" type="button" @click="copy(curlExample())">Copy curl</button><label>CLI</label><code x-cloak>{{ cliExample() }}</code><button class="btn ghost sm" type="button" @click="copy(cliExample())">Copy CLI</button><button class="btn danger sm" type="button" @click="reveal.set(null)">I saved it; hide secret</button></div><div class="section"><h2>Service accounts</h2><div class="accounts" x-cloak><template :for="item in accounts()"><article class="account panel" :class="selectedAccount() === item.account.id ? 'selected' : ''" x-cloak><button class="account-main" type="button" @click="selectAccount(item)" x-cloak><span x-cloak><strong x-cloak>{{ item.account.name }}</strong><small x-cloak>{{ item.account.slug }} · {{ item.membership?.roleTemplate }} · {{ scopeLabel(item.membership?.scope) }}</small><small x-cloak>Created {{ new Date(item.account.createdAt).toLocaleDateString() }} by {{ item.createdBy?.displayName || 'offline administrator' }}</small></span><span class="tag" x-cloak>{{ item.account.state }}</span></button><div class="token-list" x-cloak><template :for="token in item.tokens"><div class="token" x-cloak><div x-cloak><strong x-cloak>{{ token.name }}</strong><code x-cloak>{{ token.prefix }}</code><small x-cloak>{{ tokenState(token) }} · created {{ new Date(token.createdAt).toLocaleDateString() }} by {{ token.createdBy?.displayName || 'offline administrator' }} · expires {{ new Date(token.expiresAt).toLocaleDateString() }} · last used {{ token.lastUsedAt ? new Date(token.lastUsedAt).toLocaleString() : 'never' }}</small><small x-cloak>{{ token.capabilities.join(', ') }} · {{ scopeLabel(token.scope) }}</small></div><div class="table-actions"><button class="btn ghost sm" type="button" @show="token.state === 'active'" @click="tokenAction(token, 'rotate')" x-cloak>Rotate</button><button class="btn danger sm" type="button" @show="token.state === 'active'" @click="tokenAction(token, 'revoke')" x-cloak>Revoke</button></div></div></template><div class="compact empty" @show="item.tokens.length === 0" x-cloak><span>No tokens yet.</span></div></div><button class="btn danger sm" type="button" @show="item.account.state === 'active'" @click="disableAccount(item)" x-cloak>Disable account and tokens</button></article></template><div class="compact empty panel" @show="accounts().length === 0" x-cloak><strong>No service accounts</strong><span>Create one for a CI system or external automation.</span></div></div></div><div class="section"><h2>Create service account</h2><div class="panel"><form class="form-grid" @submit.prevent="createAccount()"><div class="field-block"><label for="account-name">Name</label><input id="account-name" :value="accountName()" @input="accountName.set($event.target.value)" placeholder="Production CI" required></div><div class="field-block"><label for="account-slug">Slug</label><input id="account-slug" :value="accountSlug()" @input="accountSlug.set($event.target.value)" pattern="[a-z0-9][a-z0-9-]{1,47}" placeholder="production-ci" required></div><div class="field-block"><label for="account-role">Maximum role</label><select id="account-role" :value="accountRole()" @change="accountRole.set($event.target.value)"><option value="viewer">Viewer</option><option value="auditor">Auditor</option><option value="deployer">Deployer</option><option value="operator">Operator</option><option value="admin">Admin</option></select><small>Service accounts can never be owners.</small></div><div class="field-block"><label for="account-scope">Maximum scope</label><select id="account-scope" :value="accountScopeType()" @change="accountScopeType.set($event.target.value); setScope($event.target.value, accountScopeId)"><option value="organization">Organization</option><option value="project">Current project</option><option value="environment">One environment</option><option value="resource">One service</option></select></div><div class="field-block" @show="accountScopeType() === 'environment'" x-cloak><label for="account-environment">Environment</label><select id="account-environment" :value="accountScopeId()" @change="accountScopeId.set($event.target.value)"><template :for="environment in environments()"><option :value="environment.id">{{ environment.name }}</option></template></select></div><div class="field-block" @show="accountScopeType() === 'resource'" x-cloak><label for="account-resource">Service</label><select id="account-resource" :value="accountScopeId()" @change="accountScopeId.set($event.target.value)"><template :for="resource in resources()"><option :value="resource.id">{{ resource.name }}</option></template></select></div><div class="form-actions"><button class="btn" type="submit">Create service account</button></div></form></div></div><div class="section"><h2>Create API token</h2><div class="panel"><form class="form-grid" @submit.prevent="createToken()"><div class="field-block"><label for="token-account">Service account</label><select id="token-account" :value="selectedAccount()" @change="selectAccount(accounts().find(item => item.account.id === $event.target.value))" required><template :for="item in accounts()"><option :value="item.account.id" :disabled="item.account.state !== 'active'">{{ item.account.name }}</option></template></select></div><div class="field-block"><label for="token-name">Token name</label><input id="token-name" :value="tokenName()" @input="tokenName.set($event.target.value)" required></div><div class="field-block"><label for="token-expiry">Expiry</label><input id="token-expiry" type="date" :value="tokenExpiry()" @input="tokenExpiry.set($event.target.value)"><small>Defaults to 90 days; maximum one year.</small></div><div class="field-block"><label for="token-scope">Token scope</label><select id="token-scope" :value="tokenScopeType()" @change="tokenScopeType.set($event.target.value); setScope($event.target.value, tokenScopeId)"><option value="organization">Organization</option><option value="project">Current project</option><option value="environment">One environment</option><option value="resource">One service</option></select><small>Must be equal to or narrower than the service-account scope.</small></div><div class="field-block" @show="tokenScopeType() === 'environment'" x-cloak><label>Environment</label><select :value="tokenScopeId()" @change="tokenScopeId.set($event.target.value)"><template :for="environment in environments()"><option :value="environment.id">{{ environment.name }}</option></template></select></div><div class="field-block" @show="tokenScopeType() === 'resource'" x-cloak><label>Service</label><select :value="tokenScopeId()" @change="tokenScopeId.set($event.target.value)"><template :for="resource in resources()"><option :value="resource.id">{{ resource.name }}</option></template></select></div><fieldset class="capabilities"><legend>Explicit capabilities</legend><template :for="capability in capabilities"><label class="capability" x-cloak><input type="checkbox" :checked="tokenCapabilities().includes(capability[0])" @change="toggleCapability(capability[0])"><span x-cloak><b x-cloak>{{ capability[0] }}</b><small x-cloak>{{ capability[1] }}</small></span></label></template></fieldset><div class="form-actions"><button class="btn" type="submit" :disabled="!selectedAccount() || tokenCapabilities().length === 0">Create and reveal token</button></div></form></div></div><div class="section"><h2>Confirm sensitive changes</h2><div class="auth-grid panel"><div class="field-block"><label for="automation-password">Your password</label><input id="automation-password" type="password" autocomplete="current-password" :value="password()" @input="password.set($event.target.value)" required></div><div class="field-block"><label for="automation-code">Authenticator or recovery code</label><input id="automation-code" autocomplete="one-time-code" :value="mfaCode()" @input="mfaCode.set($event.target.value)"><small>Required when MFA is enabled. Used only for recent-auth confirmation.</small></div></div></div></div><style> .automation-message{min-height:20px;margin-top:12px}.accounts{display:grid;gap:12px}.account{display:grid;gap:14px}.account.selected{border-color:var(--accent)} .account-main{display:flex;justify-content:space-between;gap:12px;width:100%;text-align:left;background:none;border:0;color:inherit;cursor:pointer}.account-main span:first-child{display:grid;gap:4px}.account-main small,.token small{color:var(--txt3);font-size:12px;line-height:1.5} .token-list{display:grid;gap:10px}.token{display:flex;justify-content:space-between;gap:14px;padding:12px;border:1px solid var(--panel-br);border-radius:10px}.token>div:first-child{display:grid;gap:4px;min-width:0}.token code{color:var(--txt2)} .reveal{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;margin-top:18px;border-color:rgba(53,209,155,.35)}.reveal>div,.reveal>code,.reveal>label{grid-column:1/-1}.reveal>div{display:grid;gap:4px}.reveal span{color:var(--txt2);font-size:12px}.reveal code{padding:10px;background:rgba(0,0,0,.25);border-radius:8px;overflow-wrap:anywhere;user-select:all} .capabilities{grid-column:1/-1;border:1px solid var(--panel-br);border-radius:10px;padding:12px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.capabilities legend{padding:0 6px;color:var(--txt3);font-size:11px;font-weight:800;text-transform:uppercase}.capability{display:flex;gap:9px;align-items:flex-start;padding:9px}.capability input{width:auto;margin-top:3px}.capability span{display:grid;gap:2px}.capability small{color:var(--txt3);font-size:11px}.auth-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px} @media(max-width:700px){.token{display:grid}.reveal,.capabilities,.auth-grid{grid-template-columns:1fr}.reveal>*{grid-column:1}} </style></body></html>
|