@spacefast/common 0.0.24 → 0.2.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/brand-assets/LICENSE-Haskoy.txt +93 -0
- package/brand-assets/haskoy-latin-variable.woff2 +0 -0
- package/brand-assets/spacefast-favicon.svg +4 -4
- package/brand-assets/spacefast-sf-full-bleed.svg +3 -3
- package/brand-assets/spacefast-wordmark.svg +2 -4
- package/dist/agents/connect-targets.d.ts +6 -146
- package/dist/agents/connect-targets.js +63 -295
- package/dist/agents/private-key-oauth.d.ts +31 -2
- package/dist/agents/private-key-oauth.js +166 -92
- package/dist/agents/registry/core.d.ts +292 -0
- package/dist/agents/registry/core.js +1388 -0
- package/dist/agents/registry/deeplink-probes.d.ts +23 -0
- package/dist/agents/registry/deeplink-probes.js +18 -0
- package/dist/agents/registry/node.d.ts +56 -0
- package/dist/agents/registry/node.js +339 -0
- package/dist/brand-assets-build.js +3 -6
- package/dist/brand-assets.d.ts +5 -15
- package/dist/brand-assets.js +12 -14
- package/dist/brand.d.ts +17 -0
- package/dist/brand.js +24 -4
- package/dist/config/domains.d.ts +79 -12
- package/dist/config/domains.js +128 -29
- package/dist/config/index.js +1 -2
- package/dist/contracts/abuse.js +9 -24
- package/dist/contracts/access-profiles.d.ts +86 -0
- package/dist/contracts/access-profiles.js +89 -0
- package/dist/contracts/access.d.ts +24 -30
- package/dist/contracts/access.js +33 -64
- package/dist/contracts/activity.d.ts +18 -1
- package/dist/contracts/activity.js +72 -26
- package/dist/contracts/analytics.js +8 -10
- package/dist/contracts/api-keys.d.ts +77 -52
- package/dist/contracts/api-keys.js +44 -24
- package/dist/contracts/application-journal.d.ts +216 -0
- package/dist/contracts/application-journal.js +167 -0
- package/dist/contracts/auth.d.ts +90 -17
- package/dist/contracts/auth.js +86 -17
- package/dist/contracts/beta.d.ts +9 -4
- package/dist/contracts/beta.js +20 -16
- package/dist/contracts/billing.d.ts +2 -1
- package/dist/contracts/billing.js +37 -11
- package/dist/contracts/bootstrap.d.ts +791 -0
- package/dist/contracts/bootstrap.js +51 -0
- package/dist/contracts/builds.d.ts +209 -18
- package/dist/contracts/builds.js +93 -27
- package/dist/contracts/channels.d.ts +43 -0
- package/dist/contracts/channels.js +25 -9
- package/dist/contracts/cli.d.ts +7 -0
- package/dist/contracts/cli.js +6 -0
- package/dist/contracts/collab-public.d.ts +1 -1
- package/dist/contracts/collab-public.js +5 -8
- package/dist/contracts/comments.d.ts +44 -34
- package/dist/contracts/comments.js +107 -77
- package/dist/contracts/commerce.d.ts +165 -0
- package/dist/contracts/commerce.js +146 -0
- package/dist/contracts/common.d.ts +3 -3
- package/dist/contracts/common.js +20 -31
- package/dist/contracts/content-contract-verification.d.ts +521 -0
- package/dist/contracts/content-contract-verification.js +320 -0
- package/dist/contracts/content-platform-fixture.d.ts +1278 -0
- package/dist/contracts/content-platform-fixture.js +44 -0
- package/dist/contracts/content-program.d.ts +994 -0
- package/dist/contracts/content-program.js +658 -0
- package/dist/contracts/content-sync.d.ts +290 -0
- package/dist/contracts/content-sync.js +167 -0
- package/dist/contracts/content.d.ts +432 -0
- package/dist/contracts/content.js +303 -0
- package/dist/contracts/continuation.d.ts +17 -1
- package/dist/contracts/continuation.js +23 -23
- package/dist/contracts/countries.js +3 -4
- package/dist/contracts/crons.d.ts +52 -0
- package/dist/contracts/crons.js +60 -0
- package/dist/contracts/dashboard-prefs.d.ts +56 -0
- package/dist/contracts/dashboard-prefs.js +51 -0
- package/dist/contracts/device-auth.d.ts +234 -37
- package/dist/contracts/device-auth.js +267 -53
- package/dist/contracts/docs.d.ts +1 -5
- package/dist/contracts/docs.js +7 -15
- package/dist/contracts/domains.d.ts +501 -93
- package/dist/contracts/domains.js +193 -55
- package/dist/contracts/email-preferences.d.ts +3 -3
- package/dist/contracts/email-preferences.js +6 -7
- package/dist/contracts/enums.d.ts +27 -26
- package/dist/contracts/enums.js +68 -61
- package/dist/contracts/error-code-meta.d.ts +283 -7
- package/dist/contracts/error-code-meta.js +97 -5
- package/dist/contracts/error-codes.d.ts +5 -5
- package/dist/contracts/error-codes.js +82 -17
- package/dist/contracts/events.d.ts +100 -20
- package/dist/contracts/events.js +107 -20
- package/dist/contracts/execution.d.ts +48 -64
- package/dist/contracts/execution.js +52 -62
- package/dist/contracts/feature-lifecycle.d.ts +73 -27
- package/dist/contracts/feature-lifecycle.js +86 -26
- package/dist/contracts/features.d.ts +68 -19
- package/dist/contracts/features.js +42 -41
- package/dist/contracts/fixtures/content-platform-v1.json +1108 -0
- package/dist/contracts/frame-session.d.ts +192 -0
- package/dist/contracts/frame-session.js +164 -0
- package/dist/contracts/functions.d.ts +338 -164
- package/dist/contracts/functions.js +367 -198
- package/dist/contracts/git.d.ts +27 -33
- package/dist/contracts/git.js +10 -11
- package/dist/contracts/grant-copy.d.ts +43 -0
- package/dist/contracts/grant-copy.js +44 -0
- package/dist/contracts/grants.d.ts +62 -54
- package/dist/contracts/grants.js +124 -23
- package/dist/contracts/ids.d.ts +4 -1
- package/dist/contracts/ids.js +6 -3
- package/dist/contracts/internal.d.ts +8 -18
- package/dist/contracts/internal.js +13 -14
- package/dist/contracts/mcp.d.ts +28 -0
- package/dist/contracts/mcp.js +28 -0
- package/dist/contracts/me.d.ts +6 -1
- package/dist/contracts/me.js +12 -6
- package/dist/contracts/notifications.d.ts +27 -29
- package/dist/contracts/notifications.js +9 -4
- package/dist/contracts/oauth-resources.d.ts +112 -10
- package/dist/contracts/oauth-resources.js +116 -11
- package/dist/contracts/oauth-scope-actions.d.ts +90 -0
- package/dist/contracts/oauth-scope-actions.js +126 -0
- package/dist/contracts/operations.d.ts +27 -4
- package/dist/contracts/operations.js +12 -6
- package/dist/contracts/pages.d.ts +31 -7
- package/dist/contracts/pages.js +22 -5
- package/dist/contracts/partner-notifications.d.ts +3 -0
- package/dist/contracts/partner-notifications.js +65 -0
- package/dist/contracts/{platform.d.ts → partner.d.ts} +29 -42
- package/dist/contracts/{platform.js → partner.js} +27 -40
- package/dist/contracts/plan-policy.js +1 -1
- package/dist/contracts/principal-assertion.d.ts +43 -0
- package/dist/contracts/principal-assertion.js +67 -0
- package/dist/contracts/principals.d.ts +73 -0
- package/dist/contracts/principals.js +59 -0
- package/dist/contracts/privacy.d.ts +11 -0
- package/dist/contracts/privacy.js +17 -0
- package/dist/contracts/problem-document.js +3 -4
- package/dist/contracts/publish-archive.d.ts +269 -161
- package/dist/contracts/publish-archive.js +11 -1
- package/dist/contracts/push-new.d.ts +8 -8
- package/dist/contracts/quotas.js +5 -5
- package/dist/contracts/realtime.d.ts +52 -0
- package/dist/contracts/realtime.js +54 -0
- package/dist/contracts/repository-connections.d.ts +44 -31
- package/dist/contracts/repository-connections.js +17 -5
- package/dist/contracts/resources.d.ts +105 -14
- package/dist/contracts/resources.js +150 -26
- package/dist/contracts/route-inventory.d.ts +358 -0
- package/dist/contracts/route-inventory.js +141 -0
- package/dist/contracts/runtime-api.d.ts +206 -82
- package/dist/contracts/runtime-api.js +131 -70
- package/dist/contracts/runtime-app.d.ts +13 -23
- package/dist/contracts/runtime-app.js +39 -24
- package/dist/contracts/runtime-components.d.ts +143 -0
- package/dist/contracts/runtime-components.js +199 -0
- package/dist/contracts/runtime-db.d.ts +49 -23
- package/dist/contracts/runtime-db.js +87 -22
- package/dist/contracts/runtime-purge.d.ts +8 -0
- package/dist/contracts/runtime-purge.js +5 -0
- package/dist/contracts/runtime-services.d.ts +43 -75
- package/dist/contracts/runtime-services.js +29 -49
- package/dist/contracts/runtime-storage.d.ts +15 -14
- package/dist/contracts/runtime-storage.js +30 -14
- package/dist/contracts/sf-config-v1.d.ts +48 -14
- package/dist/contracts/slugs.js +13 -4
- package/dist/contracts/space-config.d.ts +283 -19
- package/dist/contracts/space-config.js +430 -80
- package/dist/contracts/spaces.d.ts +376 -290
- package/dist/contracts/spaces.js +200 -135
- package/dist/contracts/superadmin-activity.d.ts +3 -43
- package/dist/contracts/superadmin-activity.js +6 -30
- package/dist/contracts/superadmin-emails.d.ts +1 -0
- package/dist/contracts/superadmin-emails.js +17 -1
- package/dist/contracts/superadmin-runtime.d.ts +199 -0
- package/dist/contracts/superadmin-runtime.js +141 -0
- package/dist/contracts/superadmin-search.d.ts +22 -2
- package/dist/contracts/superadmin-search.js +14 -6
- package/dist/contracts/superadmin-spaces.d.ts +438 -189
- package/dist/contracts/superadmin-spaces.js +131 -25
- package/dist/contracts/superadmin-teams.d.ts +5 -2
- package/dist/contracts/superadmin-teams.js +1 -2
- package/dist/contracts/superadmin-tenants.d.ts +627 -12
- package/dist/contracts/superadmin-tenants.js +32 -13
- package/dist/contracts/superadmin.d.ts +232 -37
- package/dist/contracts/superadmin.js +106 -22
- package/dist/contracts/tags.d.ts +85 -58
- package/dist/contracts/tags.js +47 -37
- package/dist/contracts/teams.d.ts +86 -26
- package/dist/contracts/teams.js +90 -31
- package/dist/contracts/tenants.d.ts +135 -0
- package/dist/contracts/tenants.js +120 -0
- package/dist/contracts/test-triggers.d.ts +70 -0
- package/dist/contracts/test-triggers.js +116 -0
- package/dist/contracts/theme-json.js +3 -8
- package/dist/contracts/transfers.d.ts +1 -0
- package/dist/contracts/transfers.js +3 -2
- package/dist/contracts/usage.d.ts +209 -0
- package/dist/contracts/usage.js +124 -12
- package/dist/contracts/variables.d.ts +2 -2
- package/dist/contracts/variables.js +9 -13
- package/dist/contracts/webhooks.d.ts +13 -5
- package/dist/contracts/webhooks.js +3 -1
- package/dist/contracts/zero.d.ts +72 -81
- package/dist/contracts/zero.js +80 -78
- package/dist/dashboard-paths/index.d.ts +24 -21
- package/dist/dashboard-paths/index.js +35 -47
- package/dist/dashboard-paths/route-reservations.generated.d.ts +2 -0
- package/dist/dashboard-paths/route-reservations.generated.js +36 -0
- package/dist/docs/agent-arrival.d.ts +92 -0
- package/dist/docs/agent-arrival.js +227 -0
- package/dist/docs/agent-output-policy.d.ts +6 -0
- package/dist/docs/agent-output-policy.js +22 -0
- package/dist/docs/agent-prose.d.ts +12 -18
- package/dist/docs/agent-prose.js +73 -51
- package/dist/docs/agent-setup.d.ts +72 -41
- package/dist/docs/agent-setup.js +339 -436
- package/dist/docs/catalog.d.ts +153 -95
- package/dist/docs/catalog.js +163 -48
- package/dist/docs/error-docs.d.ts +2098 -1
- package/dist/docs/error-docs.js +421 -144
- package/dist/docs/index-build.d.ts +0 -1
- package/dist/docs/index-build.js +2 -3
- package/dist/docs/page-context-prompt.d.ts +77 -0
- package/dist/docs/page-context-prompt.js +131 -0
- package/dist/docs/reference-urls.d.ts +46 -0
- package/dist/docs/reference-urls.js +79 -0
- package/dist/docs/search.d.ts +6 -8
- package/dist/docs/search.js +10 -13
- package/dist/docs/skill-distribution.d.ts +4 -1
- package/dist/docs/skill-distribution.js +10 -5
- package/dist/docs/skills.d.ts +120 -120
- package/dist/docs/skills.js +121 -73
- package/dist/docs/start-prompts.d.ts +45 -0
- package/dist/docs/start-prompts.js +62 -0
- package/dist/domain-exploration.js +9 -13
- package/dist/og-template.d.ts +56 -0
- package/dist/og-template.js +56 -0
- package/dist/slug-policy/index.js +5 -11
- package/dist/test-helpers/fetch-stub.js +1 -0
- package/dist/utils/browser-credential.d.ts +13 -6
- package/dist/utils/browser-credential.js +14 -7
- package/dist/utils/build-settings.d.ts +21 -1
- package/dist/utils/build-settings.js +133 -15
- package/dist/utils/canonical-json.d.ts +3 -5
- package/dist/utils/canonical-json.js +4 -5
- package/dist/utils/cast-transport.d.ts +5 -17
- package/dist/utils/cast-transport.js +6 -19
- package/dist/utils/claim-token.js +1 -2
- package/dist/utils/comment-avatars.js +4 -6
- package/dist/utils/concurrency.js +3 -3
- package/dist/utils/content-type.d.ts +7 -6
- package/dist/utils/content-type.js +73 -51
- package/dist/utils/credential-policy.d.ts +50 -6
- package/dist/utils/credential-policy.js +126 -59
- package/dist/utils/dns-instructions.js +9 -9
- package/dist/utils/egress-policy.fixtures.json +1 -1
- package/dist/utils/error-code.js +1 -0
- package/dist/utils/generate-space-name.d.ts +3 -8
- package/dist/utils/generate-space-name.js +3 -8
- package/dist/utils/grant-decision.fixtures.json +2 -2
- package/dist/utils/grants.d.ts +0 -1
- package/dist/utils/grants.js +7 -8
- package/dist/utils/gravatar.d.ts +2 -4
- package/dist/utils/gravatar.js +6 -12
- package/dist/utils/id-hints.js +14 -23
- package/dist/utils/idempotency.d.ts +3 -3
- package/dist/utils/idempotency.js +6 -7
- package/dist/utils/local-space-state.d.ts +23 -26
- package/dist/utils/local-space-state.js +46 -46
- package/dist/utils/oauth-signed-query.d.ts +3 -5
- package/dist/utils/oauth-signed-query.js +6 -9
- package/dist/utils/one-shot-replay.d.ts +7 -6
- package/dist/utils/one-shot-replay.js +3 -5
- package/dist/utils/page-colors.d.ts +15 -19
- package/dist/utils/page-colors.js +23 -45
- package/dist/utils/page-fonts.d.ts +1 -1
- package/dist/utils/page-fonts.js +8 -20
- package/dist/utils/page-preview.d.ts +0 -1
- package/dist/utils/page-preview.js +0 -1
- package/dist/utils/pages.d.ts +33 -3
- package/dist/utils/pages.js +83 -30
- package/dist/utils/privacy-regions.d.ts +19 -0
- package/dist/utils/privacy-regions.js +91 -0
- package/dist/utils/publish-form-data.d.ts +4 -5
- package/dist/utils/publish-form-data.js +2 -3
- package/dist/utils/publish-policy.d.ts +11 -17
- package/dist/utils/publish-policy.fixtures.json +11 -5
- package/dist/utils/publish-policy.js +65 -110
- package/dist/utils/query-client.d.ts +2 -0
- package/dist/utils/query-client.js +10 -1
- package/dist/utils/runtime-paths.d.ts +4 -4
- package/dist/utils/runtime-paths.js +6 -8
- package/dist/utils/runtime-upload.d.ts +9 -0
- package/dist/utils/runtime-upload.js +12 -0
- package/dist/utils/secure-local-file.d.ts +3 -3
- package/dist/utils/secure-local-file.js +23 -40
- package/dist/utils/space-config.d.ts +12 -15
- package/dist/utils/space-config.js +13 -16
- package/dist/utils/static-runtime-policy.d.ts +10 -7
- package/dist/utils/static-runtime-policy.fixtures.json +38 -41
- package/dist/utils/static-runtime-policy.generated.d.ts +16 -0
- package/dist/utils/static-runtime-policy.generated.js +70 -0
- package/dist/utils/static-runtime-policy.js +31 -132
- package/dist/utils/storage-policy.js +1 -2
- package/dist/utils/upload-session.d.ts +4 -1
- package/dist/utils/upload-session.js +41 -19
- package/dist/utils/version-path.js +11 -19
- package/dist/utils/wpcom-auth-redirects.d.ts +5 -14
- package/dist/utils/wpcom-auth-redirects.js +1 -4
- package/dist/vocabulary.d.ts +13 -13
- package/dist/vocabulary.js +52 -27
- package/package.json +15 -18
- package/dist/agents/client-registry.d.ts +0 -22
- package/dist/agents/client-registry.js +0 -41
- package/dist/docs/agent-handoff-document.d.ts +0 -15
- package/dist/docs/agent-handoff-document.js +0 -180
- package/dist/docs/agent-solutions.d.ts +0 -110
- package/dist/docs/agent-solutions.js +0 -183
- package/dist/utils/query-keys.d.ts +0 -84
- package/dist/utils/query-keys.js +0 -108
package/dist/config/domains.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/** Deployment environment a URL is being built for. */
|
|
2
2
|
export type DeployEnv = "dev" | "test" | "prod";
|
|
3
3
|
/** Logical services that each get their own host. */
|
|
4
|
-
export type Service = "www" | "api" | "dashboard" | "superadmin" | "cast" | "mcp" | "push" | "fieldguide";
|
|
4
|
+
export type Service = "www" | "api" | "access" | "dashboard" | "superadmin" | "cast" | "mcp" | "push" | "fieldguide";
|
|
5
5
|
export type EnvDomains = {
|
|
6
6
|
/** Apex/base origin for the marketing site (the `www` service + bare apex). */
|
|
7
7
|
readonly apex: string;
|
|
8
8
|
/** Cookie domain that scopes auth across every subdomain of this env. */
|
|
9
9
|
readonly cookieDomain: string;
|
|
10
|
-
/** Scheme used to build origins. SSL everywhere
|
|
10
|
+
/** Scheme used to build origins. SSL everywhere, even local (via portless). */
|
|
11
11
|
readonly protocol: "http" | "https";
|
|
12
12
|
/** Fully-qualified hostname per service. */
|
|
13
13
|
readonly hosts: Readonly<Record<Service, string>>;
|
|
@@ -16,18 +16,46 @@ export type EnvDomains = {
|
|
|
16
16
|
export declare const PROD_ENV_DOMAIN: "spacefast.com";
|
|
17
17
|
/** The local-dev apex (portless serves every subdomain over HTTPS). */
|
|
18
18
|
export declare const DEV_ENV_DOMAIN: "sf.localhost";
|
|
19
|
+
/** The deployed staging apex. Not local: staging is a hosted environment. */
|
|
20
|
+
export declare const STAGING_ENV_DOMAIN: "stattic.net";
|
|
21
|
+
/** The deployed dev apex. Local dev is separate and stays on sf.localhost. */
|
|
22
|
+
export declare const DEV_DEPLOY_ENV_DOMAIN: "stattic.xyz";
|
|
19
23
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
|
|
24
|
+
* The three environments the deploy lanes ship to, keyed by the name each one
|
|
25
|
+
* carries everywhere else: the branch map in scripts/deploy-environment.mjs,
|
|
26
|
+
* the GitHub Environment, and `SPACEFAST_ENV` on the running service. Distinct
|
|
27
|
+
* from `DeployEnv` above, which names the local stacks beside prod.
|
|
28
|
+
*/
|
|
29
|
+
export type HostedEnv = "dev" | "staging" | "prod";
|
|
30
|
+
export declare const HOSTED_ENV_DOMAINS: Readonly<Record<HostedEnv, string>>;
|
|
31
|
+
/**
|
|
32
|
+
* The hosted environment a `SPACEFAST_ENV` value names, or null. `production`
|
|
33
|
+
* is accepted for `prod`: it is what the running services already carry, and
|
|
34
|
+
* what their alert routing and email tags key on.
|
|
35
|
+
*/
|
|
36
|
+
export declare function hostedEnvFor(environment: string | undefined): HostedEnv | null;
|
|
37
|
+
/** The apex a hosted environment serves from, or null when the name is not one. */
|
|
38
|
+
export declare function hostedEnvDomain(environment: string | undefined): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* The per-service host map for one env domain (`spacefast.com`, `sf.localhost`,
|
|
41
|
+
* …). Services are `<service>.<domain>` over https, with three exceptions: the
|
|
42
|
+
* marketing site takes the bare apex of a hosted environment (so staging
|
|
43
|
+
* rehearses production's shape), and Git push and the internal handbook get
|
|
44
|
+
* their own apexes in prod.
|
|
25
45
|
*/
|
|
26
46
|
export declare function envDomainsFor(domain: string): EnvDomains;
|
|
27
47
|
/** Origin (scheme + host, no trailing slash) for a service in an environment. */
|
|
28
48
|
export declare function serviceOrigin(service: Service, env: DeployEnv): string;
|
|
29
|
-
/** Base URL every documentation link
|
|
49
|
+
/** Base URL every documentation link resolves against: catalog entries, skills, error pages. */
|
|
30
50
|
export declare const DOCS_BASE_URL: string;
|
|
51
|
+
/**
|
|
52
|
+
* The API origin every first-party client falls back to when nothing else names
|
|
53
|
+
* a provider: no `--api-url`, no `SPACEFAST_API_URL`, no saved profile or
|
|
54
|
+
* checkout link. The CLI and the MCP server share this one literal so a caller
|
|
55
|
+
* cannot be pointed at two different "defaults" depending on which surface it
|
|
56
|
+
* came through.
|
|
57
|
+
*/
|
|
58
|
+
export declare const DEFAULT_API_URL: string;
|
|
31
59
|
/**
|
|
32
60
|
* The production infrastructure apex. Committed artifacts and anything
|
|
33
61
|
* customer- or agent-facing (OpenAPI examples, docs, skills) must show this
|
|
@@ -37,9 +65,48 @@ export declare const DOCS_BASE_URL: string;
|
|
|
37
65
|
export declare const PROD_VIEW_FAST_APEX: "view.fast";
|
|
38
66
|
/** The local-dev infrastructure apex. Never appears in committed artifacts. */
|
|
39
67
|
export declare const DEV_VIEW_FAST_APEX: "preview.space";
|
|
40
|
-
/**
|
|
41
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the infrastructure apex, allowing an explicit deployment override.
|
|
70
|
+
*
|
|
71
|
+
* Only production has an apex here. Every other hosted environment names its own
|
|
72
|
+
* through `SPACEFAST_VIEW_FAST_APEX`, because a space apex is a zone that must
|
|
73
|
+
* exist, be delegated, and be attached at the provider. Two environments
|
|
74
|
+
* allocating from one apex race for the same hostname, and the loser is a
|
|
75
|
+
* customer's site.
|
|
76
|
+
*
|
|
77
|
+
* When nothing names the apex this falls back to the local one: a caller that
|
|
78
|
+
* cannot say which environment it is has no claim on production's namespace. The
|
|
79
|
+
* control plane's env schema is what holds deployments to naming it; builds and
|
|
80
|
+
* browser bundles import this registry only for its host scheme.
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveViewFastApex(environment: Readonly<Partial<Pick<NodeJS.ProcessEnv, "NODE_ENV" | "SPACEFAST_ENV" | "SPACEFAST_VIEW_FAST_APEX">>>): string;
|
|
42
83
|
/** Infrastructure apex used for public managed/custom domains. */
|
|
43
84
|
export declare const VIEW_FAST_APEX: string;
|
|
44
|
-
/**
|
|
45
|
-
|
|
85
|
+
/**
|
|
86
|
+
* The Spacefast-owned apex under which a LINKED TEST TENANT's simulated sites and
|
|
87
|
+
* hostname namespaces materialize (partner-operations spec §1). It is never
|
|
88
|
+
* served by real infrastructure — nothing points DNS at it and no cert is
|
|
89
|
+
* provisioned — so it exists purely as a collision-free namespace: a test
|
|
90
|
+
* tenant's hostnames are `{label}.{tenant}.{TEST_APEX}` (per-tenant, see
|
|
91
|
+
* `testApexForTenant`), which is also the marker the runtime-API transport uses
|
|
92
|
+
* to recognize a simulated box and short-circuit rather than reach a real one.
|
|
93
|
+
*
|
|
94
|
+
* Defaults to `test.{VIEW_FAST_APEX}` so every environment gets a distinct test
|
|
95
|
+
* apex for free; a deployment can override it with `SPACEFAST_TEST_APEX`.
|
|
96
|
+
*/
|
|
97
|
+
export declare function resolveTestApex(environment: Readonly<Partial<Pick<NodeJS.ProcessEnv, "NODE_ENV" | "SPACEFAST_ENV" | "SPACEFAST_VIEW_FAST_APEX" | "SPACEFAST_TEST_APEX">>>): string;
|
|
98
|
+
/** The resolved test-mode apex for this process. */
|
|
99
|
+
export declare const TEST_APEX: string;
|
|
100
|
+
export declare function testApexForTenant(tenantId: string, testApex?: string): string;
|
|
101
|
+
/**
|
|
102
|
+
* Whether a hostname belongs to the test-mode apex (a simulated box).
|
|
103
|
+
*
|
|
104
|
+
* STRICTLY a suffix check — the apex ITSELF is never a simulated hostname. Every
|
|
105
|
+
* simulated hostname is `{label}.{tenant}.{TEST_APEX}` (two labels deep), while
|
|
106
|
+
* the bare apex is reachable as a LIVE space's hostname: `TEST_APEX` defaults to
|
|
107
|
+
* `test.{VIEW_FAST_APEX}`, so a live space slugged `test` is served at exactly
|
|
108
|
+
* `test.view.fast`. Matching the apex would make every Runtime-API call for that
|
|
109
|
+
* space short-circuit to a synthetic response — engine installs reporting
|
|
110
|
+
* `installed` and publishes reporting success while nothing ever reaches the box.
|
|
111
|
+
*/
|
|
112
|
+
export declare function isTestApexHostname(hostname: string, testApex?: string): boolean;
|
package/dist/config/domains.js
CHANGED
|
@@ -1,30 +1,57 @@
|
|
|
1
|
-
// THE single source of truth for Spacefast hostnames and per-service origins
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// one-file edit instead of the ~100-site sweep this migration had to untangle.
|
|
1
|
+
// THE single source of truth for Spacefast hostnames and per-service origins.
|
|
2
|
+
// Brand strings, app URL builders, CORS allowlists, cookie scope, and redirect
|
|
3
|
+
// rules all derive from here, so a domain change is a one-file edit.
|
|
5
4
|
//
|
|
6
|
-
// Env-var NAMES
|
|
7
|
-
// VALUES they carry are produced from this registry.
|
|
5
|
+
// Env-var NAMES stay `SPACEFAST_*`; only the VALUES come from this registry.
|
|
8
6
|
/** The production apex. Everything else about prod derives from this string. */
|
|
9
7
|
export const PROD_ENV_DOMAIN = "spacefast.com";
|
|
10
8
|
/** The local-dev apex (portless serves every subdomain over HTTPS). */
|
|
11
9
|
export const DEV_ENV_DOMAIN = "sf.localhost";
|
|
10
|
+
/** The deployed staging apex. Not local: staging is a hosted environment. */
|
|
11
|
+
export const STAGING_ENV_DOMAIN = "stattic.net";
|
|
12
|
+
/** The deployed dev apex. Local dev is separate and stays on sf.localhost. */
|
|
13
|
+
export const DEV_DEPLOY_ENV_DOMAIN = "stattic.xyz";
|
|
14
|
+
export const HOSTED_ENV_DOMAINS = {
|
|
15
|
+
dev: DEV_DEPLOY_ENV_DOMAIN,
|
|
16
|
+
staging: STAGING_ENV_DOMAIN,
|
|
17
|
+
prod: PROD_ENV_DOMAIN,
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The hosted environment a `SPACEFAST_ENV` value names, or null. `production`
|
|
21
|
+
* is accepted for `prod`: it is what the running services already carry, and
|
|
22
|
+
* what their alert routing and email tags key on.
|
|
23
|
+
*/
|
|
24
|
+
export function hostedEnvFor(environment) {
|
|
25
|
+
if (!environment)
|
|
26
|
+
return null;
|
|
27
|
+
const normalized = environment.trim().toLowerCase();
|
|
28
|
+
if (normalized === "production")
|
|
29
|
+
return "prod";
|
|
30
|
+
return Object.hasOwn(HOSTED_ENV_DOMAINS, normalized) ? normalized : null;
|
|
31
|
+
}
|
|
32
|
+
/** The apex a hosted environment serves from, or null when the name is not one. */
|
|
33
|
+
export function hostedEnvDomain(environment) {
|
|
34
|
+
const hosted = hostedEnvFor(environment);
|
|
35
|
+
return hosted ? HOSTED_ENV_DOMAINS[hosted] : null;
|
|
36
|
+
}
|
|
12
37
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
38
|
+
* The per-service host map for one env domain (`spacefast.com`, `sf.localhost`,
|
|
39
|
+
* …). Services are `<service>.<domain>` over https, with three exceptions: the
|
|
40
|
+
* marketing site takes the bare apex of a hosted environment (so staging
|
|
41
|
+
* rehearses production's shape), and Git push and the internal handbook get
|
|
42
|
+
* their own apexes in prod.
|
|
18
43
|
*/
|
|
19
44
|
export function envDomainsFor(domain) {
|
|
20
45
|
const prod = domain === PROD_ENV_DOMAIN;
|
|
46
|
+
const hosted = Object.values(HOSTED_ENV_DOMAINS).some((hostedDomain) => hostedDomain === domain);
|
|
21
47
|
return {
|
|
22
48
|
apex: domain,
|
|
23
49
|
cookieDomain: `.${domain}`,
|
|
24
50
|
protocol: "https",
|
|
25
51
|
hosts: {
|
|
26
|
-
www:
|
|
52
|
+
www: hosted ? domain : `www.${domain}`,
|
|
27
53
|
api: `api.${domain}`,
|
|
54
|
+
access: `access.${domain}`,
|
|
28
55
|
dashboard: `my.${domain}`,
|
|
29
56
|
superadmin: `superadmin.${domain}`,
|
|
30
57
|
cast: `cast.${domain}`,
|
|
@@ -35,13 +62,11 @@ export function envDomainsFor(domain) {
|
|
|
35
62
|
};
|
|
36
63
|
}
|
|
37
64
|
const PROD = envDomainsFor(PROD_ENV_DOMAIN);
|
|
38
|
-
// Local dev runs every service under *.sf.localhost via portless
|
|
39
|
-
// ports
|
|
40
|
-
// portless records` per the migration spec: sf.localhost is the only TLD.
|
|
65
|
+
// Local dev runs every service under *.sf.localhost via portless, which proxies
|
|
66
|
+
// the raw dev ports so URLs carry no port. sf.localhost is the only local TLD.
|
|
41
67
|
const DEV = envDomainsFor(DEV_ENV_DOMAIN);
|
|
42
|
-
// The test stack
|
|
43
|
-
//
|
|
44
|
-
// can run side-by-side with dev. URL host scheme is identical to dev.
|
|
68
|
+
// The test stack uses dev's hostname scheme but its own isolated compose project
|
|
69
|
+
// and ports (see ports config), so the two run side by side.
|
|
45
70
|
const TEST = DEV;
|
|
46
71
|
const BY_ENV = {
|
|
47
72
|
dev: DEV,
|
|
@@ -53,8 +78,16 @@ export function serviceOrigin(service, env) {
|
|
|
53
78
|
const d = BY_ENV[env];
|
|
54
79
|
return `${d.protocol}://${d.hosts[service]}`;
|
|
55
80
|
}
|
|
56
|
-
/** Base URL every documentation link
|
|
81
|
+
/** Base URL every documentation link resolves against: catalog entries, skills, error pages. */
|
|
57
82
|
export const DOCS_BASE_URL = `${serviceOrigin("www", "prod")}/docs`;
|
|
83
|
+
/**
|
|
84
|
+
* The API origin every first-party client falls back to when nothing else names
|
|
85
|
+
* a provider: no `--api-url`, no `SPACEFAST_API_URL`, no saved profile or
|
|
86
|
+
* checkout link. The CLI and the MCP server share this one literal so a caller
|
|
87
|
+
* cannot be pointed at two different "defaults" depending on which surface it
|
|
88
|
+
* came through.
|
|
89
|
+
*/
|
|
90
|
+
export const DEFAULT_API_URL = serviceOrigin("api", "prod");
|
|
58
91
|
// --- Apexes that are NOT spacefast.com subdomains -------------------------------
|
|
59
92
|
/**
|
|
60
93
|
* The production infrastructure apex. Committed artifacts and anything
|
|
@@ -65,18 +98,84 @@ export const DOCS_BASE_URL = `${serviceOrigin("www", "prod")}/docs`;
|
|
|
65
98
|
export const PROD_VIEW_FAST_APEX = "view.fast";
|
|
66
99
|
/** The local-dev infrastructure apex. Never appears in committed artifacts. */
|
|
67
100
|
export const DEV_VIEW_FAST_APEX = "preview.space";
|
|
68
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the infrastructure apex, allowing an explicit deployment override.
|
|
103
|
+
*
|
|
104
|
+
* Only production has an apex here. Every other hosted environment names its own
|
|
105
|
+
* through `SPACEFAST_VIEW_FAST_APEX`, because a space apex is a zone that must
|
|
106
|
+
* exist, be delegated, and be attached at the provider. Two environments
|
|
107
|
+
* allocating from one apex race for the same hostname, and the loser is a
|
|
108
|
+
* customer's site.
|
|
109
|
+
*
|
|
110
|
+
* When nothing names the apex this falls back to the local one: a caller that
|
|
111
|
+
* cannot say which environment it is has no claim on production's namespace. The
|
|
112
|
+
* control plane's env schema is what holds deployments to naming it; builds and
|
|
113
|
+
* browser bundles import this registry only for its host scheme.
|
|
114
|
+
*/
|
|
69
115
|
export function resolveViewFastApex(environment) {
|
|
116
|
+
const explicit = environment.SPACEFAST_VIEW_FAST_APEX?.trim().toLowerCase();
|
|
117
|
+
if (explicit)
|
|
118
|
+
return explicit;
|
|
119
|
+
// SPACEFAST_ENV names a hosted environment only on a deployed service, where
|
|
120
|
+
// NODE_ENV is production. `dev` is also the local stack's name, and a local
|
|
121
|
+
// stack must keep the local apex rather than allocate under hosted dev's.
|
|
70
122
|
const nodeEnvironment = environment.NODE_ENV?.trim().toLowerCase();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
123
|
+
if (!["prod", "production"].includes(nodeEnvironment ?? ""))
|
|
124
|
+
return DEV_VIEW_FAST_APEX;
|
|
125
|
+
return hostedEnvFor(environment.SPACEFAST_ENV) === "prod"
|
|
126
|
+
? PROD_VIEW_FAST_APEX
|
|
127
|
+
: DEV_VIEW_FAST_APEX;
|
|
75
128
|
}
|
|
76
129
|
/** Infrastructure apex used for public managed/custom domains. */
|
|
77
130
|
export const VIEW_FAST_APEX = resolveViewFastApex(typeof process === "undefined" ? {} : process.env);
|
|
78
|
-
// Default space hostnames are `{hostLabel}.{VIEW_FAST_APEX}
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
131
|
+
// Default space hostnames are `{hostLabel}.{VIEW_FAST_APEX}`.
|
|
132
|
+
// --- Test-mode (simulated) apex -------------------------------------------------
|
|
133
|
+
/**
|
|
134
|
+
* The Spacefast-owned apex under which a LINKED TEST TENANT's simulated sites and
|
|
135
|
+
* hostname namespaces materialize (partner-operations spec §1). It is never
|
|
136
|
+
* served by real infrastructure — nothing points DNS at it and no cert is
|
|
137
|
+
* provisioned — so it exists purely as a collision-free namespace: a test
|
|
138
|
+
* tenant's hostnames are `{label}.{tenant}.{TEST_APEX}` (per-tenant, see
|
|
139
|
+
* `testApexForTenant`), which is also the marker the runtime-API transport uses
|
|
140
|
+
* to recognize a simulated box and short-circuit rather than reach a real one.
|
|
141
|
+
*
|
|
142
|
+
* Defaults to `test.{VIEW_FAST_APEX}` so every environment gets a distinct test
|
|
143
|
+
* apex for free; a deployment can override it with `SPACEFAST_TEST_APEX`.
|
|
144
|
+
*/
|
|
145
|
+
export function resolveTestApex(environment) {
|
|
146
|
+
const explicit = environment.SPACEFAST_TEST_APEX?.trim().toLowerCase();
|
|
147
|
+
if (explicit)
|
|
148
|
+
return explicit;
|
|
149
|
+
return `test.${resolveViewFastApex(environment)}`;
|
|
150
|
+
}
|
|
151
|
+
/** The resolved test-mode apex for this process. */
|
|
152
|
+
// oxlint-disable-next-line anti-slop/no-runtime-typeof -- isomorphic process guard: this registry is imported by browser bundles where `process` is undefined (same guard as VIEW_FAST_APEX above).
|
|
153
|
+
export const TEST_APEX = resolveTestApex(typeof process === "undefined" ? {} : process.env);
|
|
154
|
+
// A tenant id (`ten_<rand>`) as a DNS label: lowercase, `_`→`-`, non-label chars
|
|
155
|
+
// dropped. Every test tenant gets its OWN apex under `TEST_APEX` so the GLOBAL
|
|
156
|
+
// `hostname_namespace_apex_uq` never collides at the second test tenant, and so
|
|
157
|
+
// a test label can never steer a live space.
|
|
158
|
+
export function testApexForTenant(tenantId, testApex = TEST_APEX) {
|
|
159
|
+
const label = tenantId
|
|
160
|
+
.trim()
|
|
161
|
+
.toLowerCase()
|
|
162
|
+
.replaceAll("_", "-")
|
|
163
|
+
.replace(/[^a-z0-9-]/g, "")
|
|
164
|
+
.replace(/^-+|-+$/g, "");
|
|
165
|
+
return `${label || "tenant"}.${testApex}`;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Whether a hostname belongs to the test-mode apex (a simulated box).
|
|
169
|
+
*
|
|
170
|
+
* STRICTLY a suffix check — the apex ITSELF is never a simulated hostname. Every
|
|
171
|
+
* simulated hostname is `{label}.{tenant}.{TEST_APEX}` (two labels deep), while
|
|
172
|
+
* the bare apex is reachable as a LIVE space's hostname: `TEST_APEX` defaults to
|
|
173
|
+
* `test.{VIEW_FAST_APEX}`, so a live space slugged `test` is served at exactly
|
|
174
|
+
* `test.view.fast`. Matching the apex would make every Runtime-API call for that
|
|
175
|
+
* space short-circuit to a synthetic response — engine installs reporting
|
|
176
|
+
* `installed` and publishes reporting success while nothing ever reaches the box.
|
|
177
|
+
*/
|
|
178
|
+
export function isTestApexHostname(hostname, testApex = TEST_APEX) {
|
|
179
|
+
const normalized = hostname.trim().toLowerCase().replace(/\.$/, "");
|
|
180
|
+
return normalized.endsWith(`.${testApex}`);
|
|
181
|
+
}
|
package/dist/config/index.js
CHANGED
package/dist/contracts/abuse.js
CHANGED
|
@@ -4,20 +4,13 @@ export const abuseReportCategorySchema = z
|
|
|
4
4
|
.enum(["malware", "phishing", "spam", "copyright", "illegal_content", "other"])
|
|
5
5
|
.describe("Reported abuse category.");
|
|
6
6
|
export const abuseReportCreateSchema = z.object({
|
|
7
|
-
url: z.string().url().describe("
|
|
7
|
+
url: z.string().url().describe("URL of the reported content."),
|
|
8
8
|
category: abuseReportCategorySchema,
|
|
9
|
-
description: z
|
|
10
|
-
.string()
|
|
11
|
-
.trim()
|
|
12
|
-
.min(10)
|
|
13
|
-
.max(5000)
|
|
14
|
-
.describe("What the reporter saw; 10-5000 characters."),
|
|
9
|
+
description: z.string().trim().min(10).max(5000).describe("What the reporter saw."),
|
|
15
10
|
});
|
|
16
11
|
export const abuseReportStatusSchema = z
|
|
17
12
|
.enum(["new", "reviewing", "actioned", "closed"])
|
|
18
13
|
.describe("Triage status of the report.");
|
|
19
|
-
// Triage state machine: reports move forward through review; `closed` reports
|
|
20
|
-
// can be reopened into `reviewing` (and only there) when new evidence lands.
|
|
21
14
|
export const ABUSE_REPORT_STATUS_TRANSITIONS = {
|
|
22
15
|
new: ["reviewing", "actioned", "closed"],
|
|
23
16
|
reviewing: ["actioned", "closed"],
|
|
@@ -29,33 +22,25 @@ export const abuseReportSchema = z.object({
|
|
|
29
22
|
status: abuseReportStatusSchema,
|
|
30
23
|
category: abuseReportCategorySchema,
|
|
31
24
|
url: z.string().describe("Reported URL."),
|
|
32
|
-
spaceId: z
|
|
33
|
-
.string()
|
|
34
|
-
.nullable()
|
|
35
|
-
.describe("Space the reported hostname resolved to, when resolution succeeds."),
|
|
25
|
+
spaceId: z.string().nullable().describe("Space the reported hostname resolved to."),
|
|
36
26
|
createdAt: z.string().datetime().describe("When the report was filed."),
|
|
37
27
|
});
|
|
38
|
-
//
|
|
28
|
+
// Intake details the public receipt omits.
|
|
39
29
|
export const superadminAbuseReportSchema = abuseReportSchema.extend({
|
|
40
|
-
hostname: z.string().describe("Normalized hostname
|
|
41
|
-
tenantId: z.string().nullable().describe("Tenant owning the resolved space
|
|
30
|
+
hostname: z.string().describe("Normalized hostname from the reported URL."),
|
|
31
|
+
tenantId: z.string().nullable().describe("Tenant owning the resolved space."),
|
|
42
32
|
description: z.string().describe("Reporter-supplied description."),
|
|
43
33
|
updatedAt: z.string().datetime().describe("Last triage update."),
|
|
44
34
|
});
|
|
45
35
|
export const superadminAbuseReportListQuerySchema = cursorListQuerySchema.extend({
|
|
46
|
-
status: abuseReportStatusSchema.optional().describe("Filter
|
|
47
|
-
spaceId: z
|
|
48
|
-
.string()
|
|
49
|
-
.trim()
|
|
50
|
-
.min(1)
|
|
51
|
-
.optional()
|
|
52
|
-
.describe("Filter to reports that resolved to one Space."),
|
|
36
|
+
status: abuseReportStatusSchema.optional().describe("Filter by status."),
|
|
37
|
+
spaceId: z.string().trim().min(1).optional().describe("Filter to reports for this Space."),
|
|
53
38
|
hostname: z
|
|
54
39
|
.string()
|
|
55
40
|
.trim()
|
|
56
41
|
.min(1)
|
|
57
42
|
.optional()
|
|
58
|
-
.describe("Filter by
|
|
43
|
+
.describe("Filter by normalized hostname, case-insensitively."),
|
|
59
44
|
});
|
|
60
45
|
export const superadminAbuseReportPatchSchema = z.object({
|
|
61
46
|
status: abuseReportStatusSchema.describe("New triage status; must be a valid transition from the current status."),
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The scopes any interactive authorization may grant — THE list every surface
|
|
4
|
+
* derives from. The device approval page, OAuth consent, the CLI receipt, and
|
|
5
|
+
* the server's approval validator all import this instead of keeping copies.
|
|
6
|
+
*
|
|
7
|
+
* This is the shared API set — NOT the granular `spaces:publish` /
|
|
8
|
+
* `builds:trigger` scopes, whose actions `publish:write` already covers. A
|
|
9
|
+
* person approving a device never sees two checkboxes that mean the same grant,
|
|
10
|
+
* and this set equals the hosted-MCP resource's scopes so the two converge.
|
|
11
|
+
*/
|
|
12
|
+
export declare const APPROVABLE_SCOPES: readonly ["teams:read", "teams:create", "spaces:read", "spaces:write", "spaces:rename", "publish:write", "domains:read", "domains:write"];
|
|
13
|
+
export type ApprovableScope = (typeof APPROVABLE_SCOPES)[number];
|
|
14
|
+
/**
|
|
15
|
+
* Whether a scope, when approved, is bounded by the holder's team role. This is
|
|
16
|
+
* DERIVED from `isTenantAdmissionAction` — the same declaration the credential
|
|
17
|
+
* compiler mints from — so it can never disagree with what actually gets minted:
|
|
18
|
+
*
|
|
19
|
+
* - "team": grants at least one team-role action — authority OVER a team's
|
|
20
|
+
* resources, bounded by the approver's LIVE role there on every request.
|
|
21
|
+
* - "account": every action is tenant-admission (never role-bounded), so no
|
|
22
|
+
* team role gates it and any member can approve it (creating a
|
|
23
|
+
* not-yet-existing team).
|
|
24
|
+
*
|
|
25
|
+
* Drives grantability (`scopeGrantableByRole`); the compiler owns the resource.
|
|
26
|
+
*/
|
|
27
|
+
export type ScopePlane = "team" | "account";
|
|
28
|
+
export declare const SCOPE_PLANES: Record<ApprovableScope, ScopePlane>;
|
|
29
|
+
export declare const ACCESS_PROFILE_IDS: readonly ["read", "agent"];
|
|
30
|
+
export type AccessProfileId = (typeof ACCESS_PROFILE_IDS)[number];
|
|
31
|
+
export declare const AccessProfileIdSchema: z.ZodEnum<{
|
|
32
|
+
read: "read";
|
|
33
|
+
agent: "agent";
|
|
34
|
+
}>;
|
|
35
|
+
export type AccessProfile = Readonly<{
|
|
36
|
+
id: AccessProfileId;
|
|
37
|
+
/** The ONE name every surface prints: CLI request line and receipt, approval
|
|
38
|
+
* page heading, consent screen, ledger row, email. */
|
|
39
|
+
label: string;
|
|
40
|
+
/** One sentence a person reads INSTEAD of the scope list. Reviewed copy —
|
|
41
|
+
* the derived scope list under it stays authoritative, so this can only ever
|
|
42
|
+
* be stale, never wrong about what is granted. */
|
|
43
|
+
blurb: string;
|
|
44
|
+
/** The negative space: notable things this profile deliberately can't do,
|
|
45
|
+
* shown under the blurb. Optional — omit when there's nothing worth calling
|
|
46
|
+
* out (a read-only profile speaks for itself). */
|
|
47
|
+
limits?: string;
|
|
48
|
+
scopes: readonly ApprovableScope[];
|
|
49
|
+
}>;
|
|
50
|
+
/**
|
|
51
|
+
* Named grant profiles. A profile is REQUEST INTENT — one word on device start
|
|
52
|
+
* with a server-owned meaning — never authority. Scopes are the grant
|
|
53
|
+
* vocabulary; actions are authority. The default is deliberately the full
|
|
54
|
+
* approvable set, matching what a `vercel login`/`gh auth login` grants:
|
|
55
|
+
* everything the credential's holder can do, capped per team by their live
|
|
56
|
+
* role at request time.
|
|
57
|
+
*/
|
|
58
|
+
export declare const ACCESS_PROFILES: {
|
|
59
|
+
readonly read: {
|
|
60
|
+
readonly id: "read";
|
|
61
|
+
readonly label: "Read-only access";
|
|
62
|
+
readonly blurb: "Read your teams, sites, and domains. Changes nothing.";
|
|
63
|
+
readonly scopes: readonly ["teams:read", "spaces:read", "domains:read"];
|
|
64
|
+
};
|
|
65
|
+
readonly agent: {
|
|
66
|
+
readonly id: "agent";
|
|
67
|
+
readonly label: "Agent access";
|
|
68
|
+
readonly blurb: "Create and publish sites, manage domains and DNS, create teams, and use Spacefast tools.";
|
|
69
|
+
readonly limits: "It can't delete sites, manage team members, change billing, or mint credentials.";
|
|
70
|
+
readonly scopes: readonly ["teams:read", "teams:create", "spaces:read", "spaces:write", "spaces:rename", "publish:write", "domains:read", "domains:write"];
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
export declare const DEFAULT_ACCESS_PROFILE_ID: AccessProfileId;
|
|
74
|
+
/** Parse an untyped scope list (wire, storage) into approvable scopes, in
|
|
75
|
+
* checklist order, deduplicated. Unknown entries grant nothing and are dropped. */
|
|
76
|
+
export declare function approvableScopesFrom(scopes: readonly string[] | null | undefined): ApprovableScope[];
|
|
77
|
+
/** The profile a scope set IS — exact set equality — or null once edited.
|
|
78
|
+
* Profiles are pinned distinct by test, so this inverse is total. */
|
|
79
|
+
export declare function accessProfileForScopes(scopes: readonly string[] | null | undefined): AccessProfile | null;
|
|
80
|
+
/**
|
|
81
|
+
* What every surface prints for a granted scope set: a profile's label, or
|
|
82
|
+
* `Custom (Agent access, minus …)` when the set is a strict subset of a
|
|
83
|
+
* profile, or plain `Custom`. Derived, never stored — a profile redefinition
|
|
84
|
+
* can never make an old receipt lie about its scopes.
|
|
85
|
+
*/
|
|
86
|
+
export declare function accessGrantLabel(scopes: readonly string[] | null | undefined, scopeLabel?: (scope: ApprovableScope) => string): string;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { OAUTH_SHARED_API_RESOURCE_SCOPES } from "./oauth-resources.js";
|
|
3
|
+
import { isTenantAdmissionAction, oauthScopeActions } from "./oauth-scope-actions.js";
|
|
4
|
+
/**
|
|
5
|
+
* The scopes any interactive authorization may grant — THE list every surface
|
|
6
|
+
* derives from. The device approval page, OAuth consent, the CLI receipt, and
|
|
7
|
+
* the server's approval validator all import this instead of keeping copies.
|
|
8
|
+
*
|
|
9
|
+
* This is the shared API set — NOT the granular `spaces:publish` /
|
|
10
|
+
* `builds:trigger` scopes, whose actions `publish:write` already covers. A
|
|
11
|
+
* person approving a device never sees two checkboxes that mean the same grant,
|
|
12
|
+
* and this set equals the hosted-MCP resource's scopes so the two converge.
|
|
13
|
+
*/
|
|
14
|
+
export const APPROVABLE_SCOPES = [...OAUTH_SHARED_API_RESOURCE_SCOPES];
|
|
15
|
+
export const SCOPE_PLANES =
|
|
16
|
+
// SAFETY: mapped over every APPROVABLE_SCOPES entry, so the record is total.
|
|
17
|
+
Object.fromEntries(APPROVABLE_SCOPES.map((scope) => [
|
|
18
|
+
scope,
|
|
19
|
+
oauthScopeActions([scope]).every(isTenantAdmissionAction) ? "account" : "team",
|
|
20
|
+
]));
|
|
21
|
+
export const ACCESS_PROFILE_IDS = ["read", "agent"];
|
|
22
|
+
export const AccessProfileIdSchema = z.enum(ACCESS_PROFILE_IDS);
|
|
23
|
+
/**
|
|
24
|
+
* Named grant profiles. A profile is REQUEST INTENT — one word on device start
|
|
25
|
+
* with a server-owned meaning — never authority. Scopes are the grant
|
|
26
|
+
* vocabulary; actions are authority. The default is deliberately the full
|
|
27
|
+
* approvable set, matching what a `vercel login`/`gh auth login` grants:
|
|
28
|
+
* everything the credential's holder can do, capped per team by their live
|
|
29
|
+
* role at request time.
|
|
30
|
+
*/
|
|
31
|
+
export const ACCESS_PROFILES = {
|
|
32
|
+
// Read-only. It can open a hosted MCP session and invoke tools (each tool call
|
|
33
|
+
// is re-gated by its own API scope, so only reads succeed), and it can observe
|
|
34
|
+
// the MCP console. It cannot MANAGE the console — closing sessions and
|
|
35
|
+
// approving paused executions are gated on `spaces:write` (see mcp/service.ts)
|
|
36
|
+
// — so it still changes nothing.
|
|
37
|
+
read: {
|
|
38
|
+
id: "read",
|
|
39
|
+
label: "Read-only access",
|
|
40
|
+
blurb: "Read your teams, sites, and domains. Changes nothing.",
|
|
41
|
+
scopes: ["teams:read", "spaces:read", "domains:read"],
|
|
42
|
+
},
|
|
43
|
+
agent: {
|
|
44
|
+
id: "agent",
|
|
45
|
+
label: "Agent access",
|
|
46
|
+
blurb: "Create and publish sites, manage domains and DNS, create teams, and use Spacefast tools.",
|
|
47
|
+
limits: "It can't delete sites, manage team members, change billing, or mint credentials.",
|
|
48
|
+
scopes: [...APPROVABLE_SCOPES],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
export const DEFAULT_ACCESS_PROFILE_ID = "agent";
|
|
52
|
+
function isApprovableScope(scope) {
|
|
53
|
+
return APPROVABLE_SCOPES.some((known) => known === scope);
|
|
54
|
+
}
|
|
55
|
+
/** Parse an untyped scope list (wire, storage) into approvable scopes, in
|
|
56
|
+
* checklist order, deduplicated. Unknown entries grant nothing and are dropped. */
|
|
57
|
+
export function approvableScopesFrom(scopes) {
|
|
58
|
+
const requested = new Set(scopes ?? []);
|
|
59
|
+
return APPROVABLE_SCOPES.filter((scope) => requested.has(scope));
|
|
60
|
+
}
|
|
61
|
+
/** The profile a scope set IS — exact set equality — or null once edited.
|
|
62
|
+
* Profiles are pinned distinct by test, so this inverse is total. */
|
|
63
|
+
export function accessProfileForScopes(scopes) {
|
|
64
|
+
const granted = new Set(approvableScopesFrom(scopes));
|
|
65
|
+
return (Object.values(ACCESS_PROFILES).find((profile) => profile.scopes.length === granted.size &&
|
|
66
|
+
profile.scopes.every((scope) => granted.has(scope))) ?? null);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* What every surface prints for a granted scope set: a profile's label, or
|
|
70
|
+
* `Custom (Agent access, minus …)` when the set is a strict subset of a
|
|
71
|
+
* profile, or plain `Custom`. Derived, never stored — a profile redefinition
|
|
72
|
+
* can never make an old receipt lie about its scopes.
|
|
73
|
+
*/
|
|
74
|
+
export function accessGrantLabel(scopes, scopeLabel = (scope) => scope) {
|
|
75
|
+
const granted = approvableScopesFrom(scopes);
|
|
76
|
+
const exact = accessProfileForScopes(granted);
|
|
77
|
+
if (exact) {
|
|
78
|
+
return exact.label;
|
|
79
|
+
}
|
|
80
|
+
const grantedSet = new Set(granted);
|
|
81
|
+
// Narrowest containing profile names the diff; `read` precedes `agent`.
|
|
82
|
+
const profiles = Object.values(ACCESS_PROFILES);
|
|
83
|
+
const superset = profiles.find((profile) => granted.length > 0 && granted.every((scope) => profile.scopes.includes(scope)));
|
|
84
|
+
if (!superset) {
|
|
85
|
+
return "Custom";
|
|
86
|
+
}
|
|
87
|
+
const missing = superset.scopes.filter((scope) => !grantedSet.has(scope));
|
|
88
|
+
return `Custom (${superset.label}, minus ${missing.map((scope) => scopeLabel(scope)).join(", ")})`;
|
|
89
|
+
}
|