@spacefast/common 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/dist/agents/connect-targets.d.ts +4 -4
  2. package/dist/agents/connect-targets.js +3 -3
  3. package/dist/agents/private-key-oauth.d.ts +37 -0
  4. package/dist/agents/private-key-oauth.js +158 -0
  5. package/dist/config/domains.js +1 -1
  6. package/dist/config/index.d.ts +1 -0
  7. package/dist/config/index.js +1 -0
  8. package/dist/config/postgres-budget.d.ts +126 -0
  9. package/dist/config/postgres-budget.js +135 -0
  10. package/dist/config/postgres-budget.json +50 -0
  11. package/dist/contracts/access.d.ts +386 -1183
  12. package/dist/contracts/access.js +240 -1018
  13. package/dist/contracts/activity.d.ts +1 -1
  14. package/dist/contracts/activity.js +11 -19
  15. package/dist/contracts/api-keys.d.ts +69 -45
  16. package/dist/contracts/api-keys.js +3 -0
  17. package/dist/contracts/archives.d.ts +9 -10
  18. package/dist/contracts/archives.js +4 -6
  19. package/dist/contracts/builds.d.ts +8 -8
  20. package/dist/contracts/builds.js +28 -48
  21. package/dist/contracts/channels.js +1 -2
  22. package/dist/contracts/comments.d.ts +102 -93
  23. package/dist/contracts/comments.js +121 -63
  24. package/dist/contracts/common.d.ts +4 -3
  25. package/dist/contracts/common.js +2 -0
  26. package/dist/contracts/countries.js +2 -7
  27. package/dist/contracts/data-source-routes.d.ts +20 -0
  28. package/dist/contracts/data-source-routes.js +15 -0
  29. package/dist/contracts/deployments.d.ts +3 -3
  30. package/dist/contracts/device-auth.d.ts +21 -15
  31. package/dist/contracts/docs.d.ts +5 -5
  32. package/dist/contracts/domains.d.ts +261 -1330
  33. package/dist/contracts/domains.js +39 -301
  34. package/dist/contracts/email-preferences.d.ts +50 -0
  35. package/dist/contracts/email-preferences.js +44 -0
  36. package/dist/contracts/enums.d.ts +3 -5
  37. package/dist/contracts/enums.js +3 -8
  38. package/dist/contracts/error-code-meta.d.ts +99 -33
  39. package/dist/contracts/error-code-meta.js +36 -9
  40. package/dist/contracts/error-codes.d.ts +1 -1
  41. package/dist/contracts/error-codes.js +28 -11
  42. package/dist/contracts/execution.d.ts +212 -0
  43. package/dist/contracts/execution.js +288 -0
  44. package/dist/contracts/feature-lifecycle.d.ts +498 -2
  45. package/dist/contracts/feature-lifecycle.js +539 -2
  46. package/dist/contracts/features.d.ts +184 -74
  47. package/dist/contracts/features.js +4 -303
  48. package/dist/contracts/functions.d.ts +729 -0
  49. package/dist/contracts/functions.js +485 -0
  50. package/dist/contracts/git.d.ts +1 -1
  51. package/dist/contracts/grants.d.ts +1158 -0
  52. package/dist/contracts/grants.js +696 -0
  53. package/dist/contracts/ids.d.ts +6 -5
  54. package/dist/contracts/ids.js +6 -5
  55. package/dist/contracts/internal.d.ts +3 -3
  56. package/dist/contracts/mcp.d.ts +12 -12
  57. package/dist/contracts/me.js +1 -1
  58. package/dist/contracts/mounts.d.ts +142 -0
  59. package/dist/contracts/mounts.js +56 -0
  60. package/dist/contracts/oauth-resources.d.ts +5 -5
  61. package/dist/contracts/oauth-resources.js +2 -0
  62. package/dist/contracts/operations.d.ts +4 -4
  63. package/dist/contracts/pages.d.ts +6 -14
  64. package/dist/contracts/pages.js +11 -23
  65. package/dist/contracts/plan-policy.d.ts +2 -9
  66. package/dist/contracts/plan-policy.js +1 -4
  67. package/dist/contracts/platform.d.ts +80 -32
  68. package/dist/contracts/platform.js +59 -12
  69. package/dist/contracts/push-new.d.ts +26 -2
  70. package/dist/contracts/push-new.js +8 -0
  71. package/dist/contracts/quotas.d.ts +0 -1
  72. package/dist/contracts/quotas.js +0 -5
  73. package/dist/contracts/repository-connections.d.ts +1 -1
  74. package/dist/contracts/resources.d.ts +21 -85
  75. package/dist/contracts/resources.js +17 -47
  76. package/dist/contracts/runtime-api.d.ts +799 -192
  77. package/dist/contracts/runtime-api.js +231 -100
  78. package/dist/contracts/runtime-app.d.ts +138 -0
  79. package/dist/contracts/runtime-app.js +21 -0
  80. package/dist/contracts/sf-config-v1.d.ts +5 -0
  81. package/dist/contracts/sf-config-v1.js +1 -1
  82. package/dist/contracts/sites.d.ts +17 -12
  83. package/dist/contracts/sites.js +30 -12
  84. package/dist/contracts/space-config.d.ts +46 -117
  85. package/dist/contracts/space-config.js +323 -25
  86. package/dist/contracts/spaces.d.ts +392 -256
  87. package/dist/contracts/spaces.js +163 -83
  88. package/dist/contracts/superadmin-emails.d.ts +27 -3
  89. package/dist/contracts/superadmin-emails.js +10 -15
  90. package/dist/contracts/superadmin-runtime.d.ts +2 -2
  91. package/dist/contracts/superadmin-spaces.d.ts +19 -142
  92. package/dist/contracts/superadmin-spaces.js +7 -0
  93. package/dist/contracts/tags.d.ts +66 -36
  94. package/dist/contracts/tags.js +195 -11
  95. package/dist/contracts/teams.d.ts +9 -9
  96. package/dist/contracts/transfers.d.ts +1 -2
  97. package/dist/contracts/variables.d.ts +4 -4
  98. package/dist/contracts/variables.js +4 -3
  99. package/dist/contracts/webhooks.d.ts +2 -2
  100. package/dist/contracts/zero.d.ts +437 -193
  101. package/dist/contracts/zero.js +149 -158
  102. package/dist/dashboard-paths/index.d.ts +3 -3
  103. package/dist/dashboard-paths/index.js +1 -0
  104. package/dist/docs/agent-prose.js +10 -8
  105. package/dist/docs/agent-setup.d.ts +6 -4
  106. package/dist/docs/agent-setup.js +26 -16
  107. package/dist/docs/agent-solutions.js +1 -1
  108. package/dist/docs/catalog.d.ts +35 -37
  109. package/dist/docs/catalog.js +36 -38
  110. package/dist/docs/error-docs.js +108 -44
  111. package/dist/utils/asset-fingerprint.d.ts +11 -0
  112. package/dist/utils/asset-fingerprint.js +46 -0
  113. package/dist/utils/auth-redirect.d.ts +1 -3
  114. package/dist/utils/auth-redirect.js +1 -21
  115. package/dist/utils/build-settings.d.ts +2 -0
  116. package/dist/utils/build-settings.js +61 -9
  117. package/dist/utils/credential-policy.d.ts +1 -1
  118. package/dist/utils/credential-policy.js +4 -0
  119. package/dist/utils/dns-instructions.d.ts +29 -0
  120. package/dist/utils/dns-instructions.js +78 -0
  121. package/dist/utils/domain-diagnostics.d.ts +29 -0
  122. package/dist/utils/domain-diagnostics.js +63 -0
  123. package/dist/utils/egress-policy.fixtures.json +51 -0
  124. package/dist/utils/grants.d.ts +53 -0
  125. package/dist/utils/grants.js +160 -0
  126. package/dist/utils/id-hints.d.ts +1 -0
  127. package/dist/utils/id-hints.js +55 -13
  128. package/dist/utils/local-space-state.js +1 -0
  129. package/dist/utils/page-colors.d.ts +50 -0
  130. package/dist/utils/page-colors.js +167 -0
  131. package/dist/utils/page-fonts.d.ts +2 -0
  132. package/dist/utils/page-fonts.js +4 -0
  133. package/dist/utils/pages.d.ts +19 -6
  134. package/dist/utils/pages.js +122 -49
  135. package/dist/utils/production-runtime.d.ts +18 -0
  136. package/dist/utils/production-runtime.js +28 -0
  137. package/dist/utils/publish-policy.d.ts +34 -4
  138. package/dist/utils/publish-policy.js +132 -61
  139. package/dist/utils/query-keys.d.ts +7 -13
  140. package/dist/utils/query-keys.js +12 -18
  141. package/dist/utils/source-archive-policy.d.ts +2 -0
  142. package/dist/utils/source-archive-policy.js +2 -0
  143. package/dist/utils/space-config.d.ts +5 -1
  144. package/dist/utils/space-config.js +35 -0
  145. package/dist/utils/static-runtime-policy.d.ts +5 -0
  146. package/dist/utils/static-runtime-policy.fixtures.json +57 -0
  147. package/dist/utils/static-runtime-policy.js +54 -10
  148. package/dist/utils/upload-session.d.ts +3 -0
  149. package/dist/utils/upload-session.js +31 -0
  150. package/package.json +6 -4
  151. package/dist/contracts/annotations.d.ts +0 -327
  152. package/dist/contracts/annotations.js +0 -235
  153. package/dist/contracts/generated-feature-launch-entries.d.ts +0 -2
  154. package/dist/contracts/generated-feature-launch-entries.js +0 -518
  155. package/dist/contracts/publishes.d.ts +0 -135
  156. package/dist/contracts/publishes.js +0 -162
  157. package/dist/utils/access-match.d.ts +0 -39
  158. package/dist/utils/access-match.js +0 -296
  159. package/dist/utils/sf-config-v1.d.ts +0 -2
  160. package/dist/utils/sf-config-v1.js +0 -542
@@ -1,8 +1,20 @@
1
1
  import { z } from "zod";
2
2
  import { dataEnvelope } from "./common.js";
3
+ import { appRoutePathSchema, EXECUTION_ENV_MAX_KEYS, executionBundleSchema, executionCapabilitiesSchema, executionEnvNameSchema, executionIdentifierSchema, executionInspectPolicySchema, executionLogEntrySchema, executionLogListQuerySchema, executionLogListResponseSchema, executionSha256Schema, executionStaticFileSchema, httpMethodSchema, PLATFORM_ROUTE_PREFIX, } from "./execution.js";
4
+ /**
5
+ * Spacefast Zero: capsules — schema, queries, mutations and endpoints that run
6
+ * together inside one parent-owned transaction — executed by the native runner
7
+ * on a dedicated wp.cloud site, against that site's own MySQL.
8
+ *
9
+ * The transaction is what makes this a separate product rather than a
10
+ * placement option for `./functions.js`. A capsule handler's semantics depend
11
+ * on the transaction wrapped around it, and an isolate reaching the database
12
+ * over a relay cannot offer one. See `endpointIsPortable` in `./execution.js`
13
+ * for the narrow surface both products can host.
14
+ */
3
15
  export const ZERO_SOURCE_METADATA_KEY = "statticZero";
4
16
  export const ZERO_RUNTIME_KIND = "zero";
5
- export const ZERO_PLATFORM_ROUTE_PREFIX = "/__spacefast/zero";
17
+ export const ZERO_PLATFORM_ROUTE_PREFIX = `${PLATFORM_ROUTE_PREFIX}/zero`;
6
18
  export const ZERO_CLIENT_ENTRY = "client/index.tsx";
7
19
  export const ZERO_SERVER_ENTRY = "server/index.ts";
8
20
  export const ZERO_RUNTIME_ROUTE_PATHS = {
@@ -18,7 +30,7 @@ export const ZERO_RUNTIME_ROUTE_PATHS = {
18
30
  export const ZERO_RUNTIME_STORAGE_ROOT = "__spacefast/zero";
19
31
  export const ZERO_RUNTIME_ARTIFACT_PATH = `${ZERO_RUNTIME_STORAGE_ROOT}/artifact.json`;
20
32
  export const ZERO_RUNTIME_CONFIG_PATH = `${ZERO_RUNTIME_STORAGE_ROOT}/config.json`;
21
- export const ZERO_RUNTIME_BINARY_PATH = `${ZERO_RUNTIME_STORAGE_ROOT}/bin/stattic-zero-runner`;
33
+ export const ZERO_RUNTIME_BINARY_PATH = `${ZERO_RUNTIME_STORAGE_ROOT}/bin/stattic-runtime`;
22
34
  export const ZERO_RUNTIME_BINARY_COMPATIBILITY_TARGET = "wpcloud-linux-x86_64-gnu-v1";
23
35
  export const ZERO_RUNTIME_BUNDLE_ROOT = `${ZERO_RUNTIME_STORAGE_ROOT}/bundles`;
24
36
  export const ZERO_RUNTIME_CLIENT_BUNDLE_PATH = `${ZERO_RUNTIME_BUNDLE_ROOT}/client.js`;
@@ -27,68 +39,74 @@ export const ZERO_SOURCE_ENV_FILE = ".env.zero.server";
27
39
  export const ZERO_CONFIG_FILE = "zero.json";
28
40
  export const ZERO_DEPLOY_FILE = ".zero/deploy.json";
29
41
  export const ZERO_RUNTIME_BUNDLE_MAX_BYTES = 786_432;
30
- export const ZERO_ENV_MAX_KEYS = 64;
31
- export const ZERO_ENV_NAME_MAX_BYTES = 128;
32
- export const ZERO_ENV_VALUE_MAX_BYTES = 16 * 1024;
33
- export const ZERO_ENV_TOTAL_VALUE_MAX_BYTES = 64 * 1024;
34
- export const ZERO_ENV_RESERVED_PREFIXES = ["SPACEFAST_", "ZERO_"];
35
42
  export const ZERO_MANAGED_TABLE_COLUMNS = ["id", "createdAt", "updatedAt"];
43
+ /**
44
+ * Paths the Zero app shell itself answers, on top of the platform-reserved set
45
+ * in `./execution.js`. A capsule cannot claim these because its own client
46
+ * router and auth flow already do.
47
+ */
36
48
  export const ZERO_RESERVED_APP_ROUTE_PATHS = [
37
49
  "/",
38
50
  "/index.html",
39
51
  "/client.js",
40
52
  "/auth/callback",
41
- "/__spacefast",
42
- "/__span",
43
53
  ];
44
- export const ZERO_RESERVED_APP_ROUTE_PREFIXES = ["/auth/", "/__spacefast/", "/__span/"];
45
- export const zeroRuntimeKindSchema = z.enum(["static", "zero", "php", "node", "workers"]);
46
- const zeroNameSchema = z
47
- .string()
48
- .min(1)
49
- .max(128)
50
- .regex(/^[A-Za-z_][A-Za-z0-9_]*$/);
51
- const zeroEnvNameSchema = z
52
- .string()
53
- .min(1)
54
- .max(ZERO_ENV_NAME_MAX_BYTES)
55
- .regex(/^[A-Za-z_][A-Za-z0-9_]*$/)
56
- .refine((name) => !ZERO_ENV_RESERVED_PREFIXES.some((prefix) => name.toUpperCase().startsWith(prefix)), { message: "Zero env variable name uses a reserved prefix." });
57
- const zeroRoutePathSchema = z
58
- .string()
59
- .min(1)
60
- .max(2048)
61
- .startsWith("/")
62
- .refine((path) => !path.startsWith("//") && !path.includes("\\") && !path.includes("?") && !path.includes("#"), {
63
- message: "Zero capsule routes must be absolute app paths without //, \\, ?, or #.",
64
- })
65
- .refine((path) => !ZERO_RESERVED_APP_ROUTE_PATHS.some((reservedPath) => path === reservedPath), {
66
- message: "Zero capsule route is reserved by the Zero app shell.",
67
- })
68
- .refine((path) => !ZERO_RESERVED_APP_ROUTE_PREFIXES.some((reservedPrefix) => path.startsWith(reservedPrefix)), {
69
- message: "Zero capsule route prefix is reserved by the Zero app shell.",
70
- })
71
- .refine((path) => !path.startsWith(ZERO_PLATFORM_ROUTE_PREFIX), {
72
- message: `Zero capsule routes cannot use ${ZERO_PLATFORM_ROUTE_PREFIX}.`,
73
- })
74
- .refine((path) => path !== "/__stattic" && !path.startsWith("/__stattic/"), {
75
- message: "Zero capsule routes cannot use Spacefast private control paths.",
54
+ export const ZERO_RESERVED_APP_ROUTE_PREFIXES = ["/auth/"];
55
+ const zeroNameSchema = executionIdentifierSchema;
56
+ const zeroRoutePathSchema = appRoutePathSchema({
57
+ subject: "Zero capsule routes",
58
+ reservedPaths: ZERO_RESERVED_APP_ROUTE_PATHS,
59
+ reservedPrefixes: ZERO_RESERVED_APP_ROUTE_PREFIXES,
76
60
  });
77
- export const zeroHttpMethodSchema = z.enum([
78
- "GET",
79
- "HEAD",
80
- "POST",
81
- "PUT",
82
- "PATCH",
83
- "DELETE",
84
- "OPTIONS",
85
- ]);
86
61
  export const zeroEndpointSchema = z
87
62
  .object({
88
- method: zeroHttpMethodSchema,
63
+ method: httpMethodSchema,
89
64
  path: zeroRoutePathSchema,
90
65
  })
91
66
  .strict();
67
+ function zeroEndpointRouteMatchShape(path) {
68
+ const segments = [];
69
+ let score = 0;
70
+ let splat = false;
71
+ for (const segment of path.replace(/^\/+|\/+$/g, "").split("/")) {
72
+ if (!segment)
73
+ continue;
74
+ if (segment === ":splat") {
75
+ score += 1;
76
+ splat = true;
77
+ break;
78
+ }
79
+ if (segment.startsWith(":")) {
80
+ segments.push(null);
81
+ score += 2;
82
+ }
83
+ else {
84
+ segments.push(segment);
85
+ score += 10;
86
+ }
87
+ }
88
+ return { score, segments, splat };
89
+ }
90
+ export function zeroEndpointRoutesAmbiguous(left, right) {
91
+ const methodsOverlap = left.method === right.method ||
92
+ (left.method === "GET" && right.method === "HEAD") ||
93
+ (left.method === "HEAD" && right.method === "GET");
94
+ if (!methodsOverlap)
95
+ return false;
96
+ const leftShape = zeroEndpointRouteMatchShape(left.path);
97
+ const rightShape = zeroEndpointRouteMatchShape(right.path);
98
+ if (leftShape.score !== rightShape.score)
99
+ return false;
100
+ if (!leftShape.splat && leftShape.segments.length < rightShape.segments.length)
101
+ return false;
102
+ if (!rightShape.splat && rightShape.segments.length < leftShape.segments.length)
103
+ return false;
104
+ return Array.from({ length: Math.min(leftShape.segments.length, rightShape.segments.length) }, (_, index) => index).every((index) => {
105
+ const segment = leftShape.segments[index];
106
+ const other = rightShape.segments[index];
107
+ return segment === null || other === null || segment === other;
108
+ });
109
+ }
92
110
  function zeroDefaultMatchesColumnType(column, ctx) {
93
111
  if (!Object.prototype.hasOwnProperty.call(column, "default") || column.default === null) {
94
112
  return;
@@ -139,65 +157,44 @@ export const zeroTableSchema = z
139
157
  })
140
158
  .strict();
141
159
  export const zeroDatabaseSchema = z.record(z.string(), zeroTableSchema);
142
- const zeroSha256Schema = z.string().regex(/^sha256:[a-f0-9]{64}$/);
143
- const zeroBase64Schema = z
144
- .string()
145
- .regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/);
146
- export const zeroRuntimeBundleSchema = z
160
+ const zeroSha256Schema = executionSha256Schema;
161
+ /**
162
+ * Zero's bundles travel inline in the finalize payload — a capsule's compiled
163
+ * client and server fit comfortably under the ceiling, and inlining keeps the
164
+ * whole version one atomic write. Functions bundles do the opposite and land
165
+ * in the content store first; see `functionsBundleRefSchema`.
166
+ */
167
+ export const zeroRuntimeBundleSchema = executionBundleSchema({
168
+ bundleRoot: ZERO_RUNTIME_BUNDLE_ROOT,
169
+ maxBytes: ZERO_RUNTIME_BUNDLE_MAX_BYTES,
170
+ });
171
+ export const zeroRuntimeStaticFileSchema = executionStaticFileSchema({
172
+ storageRoot: ZERO_RUNTIME_STORAGE_ROOT,
173
+ maxBytes: ZERO_RUNTIME_BUNDLE_MAX_BYTES,
174
+ });
175
+ export const zeroRuntimeRunSourceSchema = z
147
176
  .object({
148
- path: z
177
+ runId: z.string().min(1).max(256),
178
+ source: z
149
179
  .string()
150
180
  .min(1)
151
- .startsWith(`${ZERO_RUNTIME_BUNDLE_ROOT}/`)
152
- .refine((bundlePath) => !bundlePath.split("/").includes(".."), {
153
- message: "Zero bundle paths cannot traverse outside the runtime bundle root.",
154
- }),
155
- sha256: zeroSha256Schema,
156
- size: z.number().int().nonnegative().max(ZERO_RUNTIME_BUNDLE_MAX_BYTES),
157
- contentBase64: zeroBase64Schema,
158
- })
159
- .strict();
160
- const zeroPublicStaticPathSchema = z
161
- .string()
162
- .min(1)
163
- .max(2048)
164
- .refine((path) => !path.startsWith("/") && !path.split("/").includes(".."), {
165
- message: "Zero static file paths must be relative and cannot traverse.",
166
- })
167
- .refine((path) => !path.startsWith(ZERO_RUNTIME_STORAGE_ROOT + "/"), {
168
- message: `Zero static files cannot use ${ZERO_RUNTIME_STORAGE_ROOT}.`,
169
- })
170
- .refine((path) => !path.startsWith("_") && !path.startsWith("."), {
171
- message: "Zero static file paths cannot use private root prefixes.",
172
- });
173
- export const zeroRuntimeStaticFileSchema = z
174
- .object({
175
- path: zeroPublicStaticPathSchema,
176
- contentType: z.string().min(1).max(255),
177
- sha256: zeroSha256Schema,
178
- size: z.number().int().nonnegative().max(ZERO_RUNTIME_BUNDLE_MAX_BYTES),
179
- contentBase64: zeroBase64Schema,
181
+ .max(2 * 1024 * 1024),
182
+ schemaHash: z.string().min(1).max(256).optional(),
183
+ capabilities: executionCapabilitiesSchema,
184
+ db: z.record(z.string(), z.unknown()).optional(),
180
185
  })
181
186
  .strict();
182
- export const zeroRuntimeRunSourceSchema = z
187
+ export const zeroRuntimeEndpointSourceSchema = z
183
188
  .object({
184
- runId: z.string().min(1).max(256),
189
+ method: httpMethodSchema,
190
+ path: zeroRoutePathSchema,
191
+ endpointId: z.string().min(1).max(256),
185
192
  source: z
186
193
  .string()
187
194
  .min(1)
188
195
  .max(2 * 1024 * 1024),
189
196
  schemaHash: z.string().min(1).max(256).optional(),
190
- capabilities: z
191
- .object({
192
- db: z.boolean().default(false),
193
- fetch: z.boolean().default(false),
194
- auth: z.boolean().default(false),
195
- env: z.boolean().default(false),
196
- realtime: z.boolean().default(false),
197
- logging: z.boolean().default(false),
198
- })
199
- .partial()
200
- .default({}),
197
+ capabilities: zeroRuntimeRunSourceSchema.shape.capabilities,
201
198
  db: z.record(z.string(), z.unknown()).optional(),
202
199
  })
203
200
  .strict();
@@ -273,7 +270,7 @@ const zeroRuntimePublicRealtimeCanonicalModeSchema = z.enum([
273
270
  ]);
274
271
  export const zeroRealtimeModeSchema = zeroRealtimeCanonicalModeSchema;
275
272
  export const zeroRuntimePublicRealtimeModeSchema = zeroRuntimePublicRealtimeCanonicalModeSchema;
276
- export const zeroInspectPolicySchema = z.enum(["private", "public"]);
273
+ export const zeroInspectPolicySchema = executionInspectPolicySchema;
277
274
  const zeroRuntimePublicPathSchema = z.string().min(1).startsWith("/");
278
275
  const zeroHttpUrlSchema = z
279
276
  .string()
@@ -398,7 +395,7 @@ export const zeroSourceManifestSchema = z
398
395
  env: z
399
396
  .object({
400
397
  file: z.literal(ZERO_SOURCE_ENV_FILE),
401
- names: z.array(zeroEnvNameSchema).max(ZERO_ENV_MAX_KEYS).default([]),
398
+ names: z.array(executionEnvNameSchema).max(EXECUTION_ENV_MAX_KEYS).default([]),
402
399
  })
403
400
  .nullable()
404
401
  .default(null),
@@ -470,6 +467,7 @@ export const zeroRuntimeFinalizePayloadSchema = z
470
467
  artifact: zeroCapsuleArtifactSchema,
471
468
  bundles: z.array(zeroRuntimeBundleSchema).max(8).default([]),
472
469
  staticFiles: z.array(zeroRuntimeStaticFileSchema).max(16).default([]),
470
+ endpointSources: z.array(zeroRuntimeEndpointSourceSchema).max(128).default([]),
473
471
  runSources: z.array(zeroRuntimeRunSourceSchema).max(128).default([]),
474
472
  migrations: zeroMigrationPlanSchema.nullable().default(null),
475
473
  variables: z
@@ -519,44 +517,61 @@ export const zeroRuntimeFinalizePayloadSchema = z
519
517
  .default({ policy: "private" }),
520
518
  })
521
519
  .strict();
520
+ export const zeroRuntimeConfigSchema = zeroRuntimeFinalizePayloadSchema
521
+ .pick({
522
+ runtimeKind: true,
523
+ install: true,
524
+ artifact: true,
525
+ migrations: true,
526
+ variables: true,
527
+ realtime: true,
528
+ auth: true,
529
+ inspect: true,
530
+ })
531
+ .extend({
532
+ variableValues: z.record(z.string(), z.string()).default({}),
533
+ })
534
+ .strict();
522
535
  export const zeroDeployMetadataSchema = z
523
536
  .object({
524
537
  artifactId: z.string().min(1).optional(),
525
538
  finalize: zeroRuntimeFinalizePayloadSchema,
526
539
  })
527
540
  .strict();
528
- export const zeroInspectResponseSchema = z
541
+ /**
542
+ * What a live Zero capsule is, as the `app` member of a space's runtime status.
543
+ *
544
+ * There is no `versionId` here: this only ever describes the version the status
545
+ * response already names as live, and repeating it would invite the two to
546
+ * disagree. `artifactId` stays because it identifies the built capsule rather
547
+ * than the publish that carries it.
548
+ */
549
+ export const zeroAppSchema = z
529
550
  .object({
530
- versionId: z.string().min(1).nullable(),
531
- artifactId: z.string().min(1).nullable(),
532
- data: z
551
+ runtimeKind: z.literal(ZERO_RUNTIME_KIND),
552
+ artifactId: z.string().min(1),
553
+ appName: z.string().min(1),
554
+ serverRuntime: z.literal("quickjs-rust"),
555
+ inspect: z
533
556
  .object({
534
- runtimeKind: z.literal(ZERO_RUNTIME_KIND),
535
- appName: z.string().min(1),
536
- serverRuntime: z.literal("quickjs-rust"),
537
- inspect: z
538
- .object({
539
- policy: zeroInspectPolicySchema,
540
- })
541
- .strict(),
542
- install: zeroRuntimeInstallProfileSchema,
543
- tables: z.array(z.string()).default([]),
544
- queries: z.array(z.string()).default([]),
545
- mutations: z.array(z.string()).default([]),
546
- endpoints: z.array(zeroEndpointSchema).default([]),
547
- sockets: z.array(z.string()).default([]),
548
- variables: z
549
- .object({
550
- source: z.literal("stattic-variables"),
551
- names: z.array(z.string()).default([]),
552
- })
553
- .strict(),
554
- realtime: zeroRuntimePublicRealtimeConfigSchema,
557
+ policy: zeroInspectPolicySchema,
555
558
  })
556
- .nullable(),
559
+ .strict(),
560
+ install: zeroRuntimeInstallProfileSchema,
561
+ tables: z.array(z.string()).default([]),
562
+ queries: z.array(z.string()).default([]),
563
+ mutations: z.array(z.string()).default([]),
564
+ endpoints: z.array(zeroEndpointSchema).default([]),
565
+ sockets: z.array(z.string()).default([]),
566
+ variables: z
567
+ .object({
568
+ source: z.literal("stattic-variables"),
569
+ names: z.array(z.string()).default([]),
570
+ })
571
+ .strict(),
572
+ realtime: zeroRuntimePublicRealtimeConfigSchema,
557
573
  })
558
574
  .strict();
559
- export const zeroInspectPublicResponseSchema = dataEnvelope(zeroInspectResponseSchema);
560
575
  export const zeroDbInspectResponseSchema = z
561
576
  .object({
562
577
  versionId: z.string().min(1).nullable(),
@@ -590,36 +605,11 @@ export const zeroDbDumpResponseSchema = z
590
605
  })
591
606
  .strict();
592
607
  export const zeroDbDumpPublicResponseSchema = dataEnvelope(zeroDbDumpResponseSchema);
593
- export const zeroLogListQuerySchema = z
594
- .object({
595
- limit: z.coerce.number().int().min(1).max(100).default(50),
596
- cursor: z.string().min(1).optional(),
597
- requestId: z.string().min(1).optional(),
598
- mutationName: z.string().min(1).optional(),
599
- })
600
- .strict();
601
- export const zeroLogEntrySchema = z
602
- .object({
603
- id: z.string().min(1),
604
- timestamp: z.string().datetime(),
605
- level: z.enum(["debug", "info", "warn", "error"]),
606
- requestId: z.string().min(1).nullable(),
607
- versionId: z.string().min(1).nullable(),
608
- mutationName: z.string().min(1).nullable(),
609
- message: z.string(),
610
- metadata: z.record(z.string(), z.unknown()).default({}),
611
- })
612
- .strict();
613
- export const zeroLogListResponseSchema = z
614
- .object({
615
- data: z.array(zeroLogEntrySchema),
616
- pagination: z
617
- .object({
618
- nextCursor: z.string().nullable(),
619
- })
620
- .strict(),
621
- })
622
- .strict();
608
+ // Both products emit the same log shape; `handlerName` carries the mutation
609
+ // name here and the worker handler name on Functions.
610
+ export const zeroLogListQuerySchema = executionLogListQuerySchema;
611
+ export const zeroLogEntrySchema = executionLogEntrySchema;
612
+ export const zeroLogListResponseSchema = executionLogListResponseSchema;
623
613
  export const zeroRealtimeEventIntakeResponseSchema = z
624
614
  .object({
625
615
  accepted: z.literal(true),
@@ -735,6 +725,7 @@ export function createZeroRuntimeFinalizePayload(input) {
735
725
  artifact,
736
726
  bundles: input.bundles ?? [],
737
727
  staticFiles: input.staticFiles ?? [],
728
+ endpointSources: input.endpointSources ?? [],
738
729
  runSources: input.runSources ?? [],
739
730
  migrations: input.migrations ?? null,
740
731
  variables: {
@@ -4,7 +4,7 @@
4
4
  * Append-only: add a new top-level static dashboard route's first segment here
5
5
  * before its route module lands. A repository structural test enforces this.
6
6
  */
7
- export declare const TEAM_SLUG_ROUTE_RESERVATIONS: readonly ["accept-beta-invite", "accept-invitation", "account", "agent", "claim", "device", "drop", "new-team", "oauth", "sign-in", "start", "terminal", "two-factor"];
7
+ export declare const TEAM_SLUG_ROUTE_RESERVATIONS: readonly ["accept-beta-invite", "accept-invitation", "account", "access", "agent", "claim", "device", "drop", "new-team", "oauth", "sign-in", "start", "terminal", "two-factor"];
8
8
  /**
9
9
  * Dashboard route first-segments that cannot be used as space slugs.
10
10
  *
@@ -17,8 +17,8 @@ export type TeamSlugRouteReservation = (typeof TEAM_SLUG_ROUTE_RESERVATIONS)[num
17
17
  export type SpaceSlugRouteReservation = (typeof SPACE_SLUG_ROUTE_RESERVATIONS)[number];
18
18
  export declare const TEAM_SLUG_ROUTE_RESERVATION_SET: ReadonlySet<string>;
19
19
  export declare const SPACE_SLUG_ROUTE_RESERVATION_SET: ReadonlySet<string>;
20
- export type SpaceSettingsDestination = "access" | "advanced" | "appearance" | "builds" | "integrations" | "integrations/add" | "source-builds";
21
- export type TeamSettingsDestination = "access" | "audit" | "developer" | "developer/agents" | "developer/environment-variables" | "developer/handoffs" | "developer/service-tokens" | "developer/webhooks" | "integrations";
20
+ export type SpaceSettingsDestination = "access" | "advanced" | "builds" | "integrations" | "integrations/add" | "source-builds";
21
+ export type TeamSettingsDestination = "access" | "audit" | "developer" | "developer/agents" | "developer/environment-variables" | "developer/handoffs" | "developer/webhooks" | "integrations";
22
22
  export declare function teamPath(teamSlug: string): string;
23
23
  export declare function spacePath(teamSlug: string, spaceSlug: string): string;
24
24
  export declare function versionDetailPath(teamSlug: string, spaceSlug: string, versionRef: string): string;
@@ -8,6 +8,7 @@ export const TEAM_SLUG_ROUTE_RESERVATIONS = [
8
8
  "accept-beta-invite",
9
9
  "accept-invitation",
10
10
  "account",
11
+ "access",
11
12
  // Not a TanStack route module: /agent* is the edge-served handoff-document
12
13
  // surface (apps/my/public/_redirects + vite middleware, #417). Those rewrite
13
14
  // rules deliberately swallow every /agent-prefixed path, so a team named
@@ -79,9 +79,9 @@ export const POSITIONING_PROSE = [
79
79
  "optimizes for agent-safe one-shot publish, claim, versioning, rollback, and continuation.",
80
80
  ].join("\n");
81
81
  export const EXPLAIN_TO_USER_PROSE = [
82
- "Relay `data.shareBlurb` verbatim when present. State anonymous expiry in user terms:",
83
- "the space expires unless claimed by `data.claim.expiresAt`. After claim, the agent key is",
84
- "named and revocable in the dashboard.",
82
+ "Report the bare Live URL, repeatable expiring private Open URL, immutable Version URL, and Claim link",
83
+ "as separate actions. State anonymous expiry in user terms: the space expires unless claimed by",
84
+ "`data.claim.expiresAt`. After claim, the agent key is named and revocable in the dashboard.",
85
85
  ].join("\n");
86
86
  export const FAILURE_CONDUCT_PROSE = [
87
87
  "On failure, surface `error.code`, `error.docsUrl`, and `error.requestId`, then stop.",
@@ -181,7 +181,7 @@ export const CONTINUE_AFTER_CLAIM_PROSE = [
181
181
  "Save `data.credential.accessToken` into `.spacefast/state.json` (replace the `claimToken`",
182
182
  'field with `"accessToken"`), then retry the publish with the new bearer. The exchange works',
183
183
  "exactly once; if it returns `continuation_used` or `continuation_unavailable`, ask the user",
184
- "to mint an access token in the dashboard (Account → Access tokens).",
184
+ "to create an API key in the dashboard (Account → API keys).",
185
185
  ].join("\n");
186
186
  /**
187
187
  * The zero-install publish sequence: one curl for a file, a secret-preflighted
@@ -216,10 +216,12 @@ export const RECEIPT_SHAPE_PROSE = [
216
216
  "The JSON receipt is the source of truth:",
217
217
  "",
218
218
  "- `data.space.liveUrl` — the live URL.",
219
- "- `data.version.immutableUrl` — the permanent, frozen version URL; share it for reviews.",
220
- " It is reserved in the initial receipt. If the publish is still finalizing, poll `data.links.status`",
221
- " with the claim token until the status is `ready`.",
222
- "- `data.shareBlurb` a paste-ready one-liner with the URL and claim nudge; relay it verbatim when present.",
219
+ "- `data.open.url` and `data.open.expiresAt` a repeatable private author Link until expiry, claim, or revocation.",
220
+ " It is not a stable URL or recipient Link; clients must not persist it as Space state.",
221
+ "- `data.version.immutableUrl` — the permanent address of the frozen Version. The bare URL",
222
+ " carries no authority: use the receipt's Open URL for the author's browser or create a scoped",
223
+ " Link for a recipient. It is reserved in the initial receipt. If the publish is still",
224
+ " finalizing, poll `data.links.status` with the claim token until the status is `ready`.",
223
225
  "- `data.upload` — signed upload instructions for manifest publishes when file bytes were not sent",
224
226
  " with the request. Send each `data.upload.targets[]` instruction exactly as returned, treat",
225
227
  " authorization headers and upload tokens as secrets, then finalize.",
@@ -31,10 +31,12 @@ export type AgentHandoff = Readonly<{
31
31
  }>;
32
32
  /**
33
33
  * The single shared grammar for handoff links:
34
- * `https://<app-origin>/agent/<documentId>#<secret>`. The document ID must match
34
+ * `<app-origin>/agent/<documentId>#<secret>`. The document ID must match
35
35
  * `[A-Za-z0-9_-]{1,512}` and the secret `[A-Za-z0-9_-]{16,512}` (the minimum keeps secrets
36
36
  * high-entropy and makes the public-projection leak check meaningful); the two must differ;
37
- * queries and embedded credentials are forbidden; HTTPS is required except on loopback hosts.
37
+ * queries and embedded credentials are forbidden. The URL must sit exactly on the caller's
38
+ * configured HTTP(S) app origin; that origin pin enforces scheme, hostname, and port without a
39
+ * second host allowlist that would reject legitimate dev or container origins.
38
40
  * Normalization is part of the contract: the input is WHATWG-URL-normalized first (backslashes,
39
41
  * dot segments, stray tab/CR/LF, empty userinfo, case, default ports), the grammar is enforced on
40
42
  * the normalized URL, and only the reconstructed canonical URL ever leaves this function.
@@ -48,13 +50,13 @@ export declare function parseAgentHandoffUrl(value: string, expectedOrigin: stri
48
50
  }>;
49
51
  /** Connect targets whose handoff continues through the CLI stdin exchange. */
50
52
  export declare const AGENT_HANDOFF_CLI_TARGET_IDS: readonly ConnectTargetId[];
51
- export declare const AGENT_HANDOFF_PREAMBLE = "Talk to the user before installing tools or publishing. Confirm the file or folder they want to make public.";
53
+ export declare const AGENT_HANDOFF_PREAMBLE = "Talk to the user before installing tools or publishing. Confirm the file or folder they want to publish.";
52
54
  /** The fetch-safety contract: reading the document never consumes or leaks a handoff. */
53
55
  export declare const AGENT_HANDOFF_FETCH_SAFETY = "Keep the original handoff link private. Fetching this document did not use the link or send its fragment secret; the secret is spent only by one successful exchange. An expired, used, or revoked link fails safely, so ask the user for a fresh one from the dashboard.";
54
56
  export declare const AGENT_HANDOFF_FRAGMENT_HYGIENE = "Keep its fragment out of commands, request URLs, logs, and replies.";
55
57
  export declare const AGENT_HANDOFF_CLI_APPROVAL_GUIDANCE: string;
56
58
  export declare const AGENT_HANDOFF_BROWSER_APPROVAL_GUIDANCE = "When the client opens browser approval, ask the user to approve there before publishing.";
57
- export declare const AGENT_HANDOFF_PUBLISH_PROSE = "After approval, confirm the selected Space and permissions, then publish only the files the user approved. Share the live URL and immutable version URL from the receipt.";
59
+ export declare const AGENT_HANDOFF_PUBLISH_PROSE = "After approval, confirm the selected Space and permissions, then publish only the files the user approved. Report the bare Live and immutable Version addresses separately from the private Open URL; create a scoped Link when a recipient needs access.";
58
60
  /** One target's Connect prose: blurb, copyable setup blocks, deeplink/download lines. */
59
61
  export declare function agentHandoffConnectSections(target: ConnectTarget): string[];
60
62
  /** Builds every copy-ready handoff artifact from one validated URL and target catalog row. */
@@ -47,10 +47,12 @@ function isSafeHttpUrl(url) {
47
47
  }
48
48
  /**
49
49
  * The single shared grammar for handoff links:
50
- * `https://<app-origin>/agent/<documentId>#<secret>`. The document ID must match
50
+ * `<app-origin>/agent/<documentId>#<secret>`. The document ID must match
51
51
  * `[A-Za-z0-9_-]{1,512}` and the secret `[A-Za-z0-9_-]{16,512}` (the minimum keeps secrets
52
52
  * high-entropy and makes the public-projection leak check meaningful); the two must differ;
53
- * queries and embedded credentials are forbidden; HTTPS is required except on loopback hosts.
53
+ * queries and embedded credentials are forbidden. The URL must sit exactly on the caller's
54
+ * configured HTTP(S) app origin; that origin pin enforces scheme, hostname, and port without a
55
+ * second host allowlist that would reject legitimate dev or container origins.
54
56
  * Normalization is part of the contract: the input is WHATWG-URL-normalized first (backslashes,
55
57
  * dot segments, stray tab/CR/LF, empty userinfo, case, default ports), the grammar is enforced on
56
58
  * the normalized URL, and only the reconstructed canonical URL ever leaves this function.
@@ -58,13 +60,17 @@ function isSafeHttpUrl(url) {
58
60
  * grammar can never fork.
59
61
  */
60
62
  export function parseAgentHandoffUrl(value, expectedOrigin) {
61
- let canonicalExpectedOrigin;
63
+ let expectedUrl;
62
64
  try {
63
- canonicalExpectedOrigin = new URL(expectedOrigin).origin;
65
+ expectedUrl = new URL(expectedOrigin);
64
66
  }
65
67
  catch {
66
68
  throw new TypeError("Agent handoff expectedOrigin must be a valid URL origin.");
67
69
  }
70
+ if (expectedUrl.protocol !== "https:" && expectedUrl.protocol !== "http:") {
71
+ throw new TypeError("Agent handoff expectedOrigin must be an HTTP(S) origin.");
72
+ }
73
+ const canonicalExpectedOrigin = expectedUrl.origin;
68
74
  let url;
69
75
  try {
70
76
  url = new URL(value);
@@ -74,14 +80,15 @@ export function parseAgentHandoffUrl(value, expectedOrigin) {
74
80
  }
75
81
  const documentId = /^\/agent\/([^/]+)$/.exec(url.pathname)?.[1];
76
82
  const secret = url.hash.slice(1);
77
- if (!isSafeHttpUrl(url) ||
78
- url.origin !== canonicalExpectedOrigin ||
83
+ if (url.origin !== canonicalExpectedOrigin ||
84
+ url.username !== "" ||
85
+ url.password !== "" ||
79
86
  value.includes("?") ||
80
87
  !documentId ||
81
88
  !opaqueHandoffDocumentId.test(documentId) ||
82
89
  !opaqueHandoffSecret.test(secret) ||
83
90
  documentId === secret) {
84
- throw new TypeError("Agent handoff URL must be an HTTPS /agent/<id> URL with one opaque fragment secret (16-512 chars, distinct from the id) and no credentials or query (HTTP is allowed on loopback hosts).");
91
+ throw new TypeError("Agent handoff URL must be an /agent/<id> URL on the app origin with one opaque fragment secret (16-512 chars, distinct from the id) and no credentials or query.");
85
92
  }
86
93
  return { documentId, documentUrl: `${url.origin}${url.pathname}`, secret };
87
94
  }
@@ -105,13 +112,13 @@ function validateMcpEndpoint(value) {
105
112
  */
106
113
  /** Connect targets whose handoff continues through the CLI stdin exchange. */
107
114
  export const AGENT_HANDOFF_CLI_TARGET_IDS = ["claude-code", "codex"];
108
- export const AGENT_HANDOFF_PREAMBLE = "Talk to the user before installing tools or publishing. Confirm the file or folder they want to make public.";
115
+ export const AGENT_HANDOFF_PREAMBLE = "Talk to the user before installing tools or publishing. Confirm the file or folder they want to publish.";
109
116
  /** The fetch-safety contract: reading the document never consumes or leaks a handoff. */
110
117
  export const AGENT_HANDOFF_FETCH_SAFETY = "Keep the original handoff link private. Fetching this document did not use the link or send its fragment secret; the secret is spent only by one successful exchange. An expired, used, or revoked link fails safely, so ask the user for a fresh one from the dashboard.";
111
118
  export const AGENT_HANDOFF_FRAGMENT_HYGIENE = "Keep its fragment out of commands, request URLs, logs, and replies.";
112
119
  export const AGENT_HANDOFF_CLI_APPROVAL_GUIDANCE = `Run \`sf setup agent\` to initiate the handoff exchange; if \`sf\` is missing, install it first (macOS/Linux: \`${spacefastInstallCurlCommand}\`; Windows PowerShell: \`${spacefastInstallPowerShellCommand}\`). When it asks for the handoff link on stdin, paste the original link into that prompt; never pass the link as an argv value. Ask the user to approve in the browser, then wait for the CLI's approval result before publishing.`;
113
120
  export const AGENT_HANDOFF_BROWSER_APPROVAL_GUIDANCE = "When the client opens browser approval, ask the user to approve there before publishing.";
114
- export const AGENT_HANDOFF_PUBLISH_PROSE = "After approval, confirm the selected Space and permissions, then publish only the files the user approved. Share the live URL and immutable version URL from the receipt.";
121
+ export const AGENT_HANDOFF_PUBLISH_PROSE = "After approval, confirm the selected Space and permissions, then publish only the files the user approved. Report the bare Live and immutable Version addresses separately from the private Open URL; create a scoped Link when a recipient needs access.";
115
122
  /** One target's Connect prose: blurb, copyable setup blocks, deeplink/download lines. */
116
123
  export function agentHandoffConnectSections(target) {
117
124
  const setupSections = [target.primary, target.config].flatMap((setup) => setup ? [[setup.label, `\`\`\`${setup.language}`, setup.value, "```"].join("\n")] : []);
@@ -243,7 +250,7 @@ export function aiInstructionsText() {
243
250
  "## Install the CLI (On-Device files, updates, versions, rollback, and the rest)",
244
251
  "",
245
252
  "The CLI is the full surface: incremental publishes, version history, rollback,",
246
- "passwords, domains, logs, and diagnostics. Install it when the user will keep",
253
+ "sharing, domains, logs, and diagnostics. Install it when the user will keep",
247
254
  "working on the space:",
248
255
  "",
249
256
  "```bash",
@@ -282,8 +289,10 @@ export function aiInstructionsText() {
282
289
  "```",
283
290
  "",
284
291
  "Request `mcp:tools` plus the API scopes the tools need: `teams:read` for team context,",
292
+ "`teams:create` for creating teams,",
285
293
  "`spaces:read` for status, versions, diffs, and logs, `spaces:write` for claim/create/update,",
286
- "`publish:write` for publish and rollback, and `domains:read`/`domains:write` for domain",
294
+ "`spaces:rename` for slug and managed-address renames, `publish:write` for publish and rollback,",
295
+ "and `domains:read`/`domains:write` for domain",
287
296
  "inventory, DNS diagnostics, assignment, verification, and mutation. Hosted MCP does not read",
288
297
  "local paths; its workspace tools operate on a durable virtual workspace attached to the MCP",
289
298
  "session.",
@@ -321,8 +330,10 @@ export function aiInstructionsText() {
321
330
  "",
322
331
  "## After publishing",
323
332
  "",
324
- "- Share the live URL, the immutable version URL when ready, and for anonymous spaces —",
325
- " the claim link with its expiry. If the receipt's immutable URL is null, poll the",
333
+ "- Report the bare Live URL, the immutable Version URL when ready, the private Open URL, and",
334
+ " — for anonymous spaces — the claim link with its expiry. Bare Live and Version URLs carry",
335
+ " no authority for a private Space; create a scoped Link when a recipient needs access.",
336
+ " If the receipt's immutable URL is null, poll the",
326
337
  " operation and version links with the claim token as bearer auth until the version is ready.",
327
338
  "- Never print access tokens, claim tokens, auth files, or `.spacefast/state.json`.",
328
339
  "",
@@ -334,7 +345,7 @@ export function aiInstructionsText() {
334
345
  "",
335
346
  "- `_redirects` — redirects, rewrites, SPA fallbacks, custom 404 rules, and external",
336
347
  " `200` proxy rules.",
337
- "- `_headers` — response headers and Basic Auth rules.",
348
+ "- `_headers` — supported response headers.",
338
349
  "- `404.html` — custom not-found page.",
339
350
  "- `sf.jsonc` — serving config (index, fallback, listing, meta) and the optional",
340
351
  " `space` link. JSON Schema: https://spacefast.com/schemas/sf.json",
@@ -342,8 +353,7 @@ export function aiInstructionsText() {
342
353
  " agent-readable too.",
343
354
  "- Plan limits surface as diagnostics, not failures you should guess at: publish the",
344
355
  " intended artifact, then read and report what the CLI or API says. When a feature",
345
- ' is plan-gated, say: "Not available on Free. Available on Personal and Work." For',
346
- " simple gated access, `sf password set` needs no plan and no `_headers` rule.",
356
+ ' is plan-gated, say: "Not available on Free. Available on Personal and Work."',
347
357
  "",
348
358
  "## Agent-aware routing",
349
359
  "",
@@ -22,7 +22,7 @@ function hostedMcpText(label, guideUrl) {
22
22
  "Transport: Streamable HTTP",
23
23
  "Resource: https://mcp.spacefast.com",
24
24
  "Minimum scope: mcp:tools",
25
- "Common scopes: teams:read spaces:read spaces:write publish:write domains:read domains:write",
25
+ "Common scopes: teams:read teams:create spaces:read spaces:write spaces:rename publish:write domains:read domains:write",
26
26
  "",
27
27
  "Hosted MCP is for cloud/browser agents. It does not read local paths; use the hosted workspace tools or signed uploads for artifacts.",
28
28
  `Guide: ${guideUrl}`,