@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.
- package/dist/agents/connect-targets.d.ts +4 -4
- package/dist/agents/connect-targets.js +3 -3
- package/dist/agents/private-key-oauth.d.ts +37 -0
- package/dist/agents/private-key-oauth.js +158 -0
- package/dist/config/domains.js +1 -1
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +1 -0
- package/dist/config/postgres-budget.d.ts +126 -0
- package/dist/config/postgres-budget.js +135 -0
- package/dist/config/postgres-budget.json +50 -0
- package/dist/contracts/access.d.ts +386 -1183
- package/dist/contracts/access.js +240 -1018
- package/dist/contracts/activity.d.ts +1 -1
- package/dist/contracts/activity.js +11 -19
- package/dist/contracts/api-keys.d.ts +69 -45
- package/dist/contracts/api-keys.js +3 -0
- package/dist/contracts/archives.d.ts +9 -10
- package/dist/contracts/archives.js +4 -6
- package/dist/contracts/builds.d.ts +8 -8
- package/dist/contracts/builds.js +28 -48
- package/dist/contracts/channels.js +1 -2
- package/dist/contracts/comments.d.ts +102 -93
- package/dist/contracts/comments.js +121 -63
- package/dist/contracts/common.d.ts +4 -3
- package/dist/contracts/common.js +2 -0
- package/dist/contracts/countries.js +2 -7
- package/dist/contracts/data-source-routes.d.ts +20 -0
- package/dist/contracts/data-source-routes.js +15 -0
- package/dist/contracts/deployments.d.ts +3 -3
- package/dist/contracts/device-auth.d.ts +21 -15
- package/dist/contracts/docs.d.ts +5 -5
- package/dist/contracts/domains.d.ts +261 -1330
- package/dist/contracts/domains.js +39 -301
- package/dist/contracts/email-preferences.d.ts +50 -0
- package/dist/contracts/email-preferences.js +44 -0
- package/dist/contracts/enums.d.ts +3 -5
- package/dist/contracts/enums.js +3 -8
- package/dist/contracts/error-code-meta.d.ts +99 -33
- package/dist/contracts/error-code-meta.js +36 -9
- package/dist/contracts/error-codes.d.ts +1 -1
- package/dist/contracts/error-codes.js +28 -11
- package/dist/contracts/execution.d.ts +212 -0
- package/dist/contracts/execution.js +288 -0
- package/dist/contracts/feature-lifecycle.d.ts +498 -2
- package/dist/contracts/feature-lifecycle.js +539 -2
- package/dist/contracts/features.d.ts +184 -74
- package/dist/contracts/features.js +4 -303
- package/dist/contracts/functions.d.ts +729 -0
- package/dist/contracts/functions.js +485 -0
- package/dist/contracts/git.d.ts +1 -1
- package/dist/contracts/grants.d.ts +1158 -0
- package/dist/contracts/grants.js +696 -0
- package/dist/contracts/ids.d.ts +6 -5
- package/dist/contracts/ids.js +6 -5
- package/dist/contracts/internal.d.ts +3 -3
- package/dist/contracts/mcp.d.ts +12 -12
- package/dist/contracts/me.js +1 -1
- package/dist/contracts/mounts.d.ts +142 -0
- package/dist/contracts/mounts.js +56 -0
- package/dist/contracts/oauth-resources.d.ts +5 -5
- package/dist/contracts/oauth-resources.js +2 -0
- package/dist/contracts/operations.d.ts +4 -4
- package/dist/contracts/pages.d.ts +6 -14
- package/dist/contracts/pages.js +11 -23
- package/dist/contracts/plan-policy.d.ts +2 -9
- package/dist/contracts/plan-policy.js +1 -4
- package/dist/contracts/platform.d.ts +80 -32
- package/dist/contracts/platform.js +59 -12
- package/dist/contracts/push-new.d.ts +26 -2
- package/dist/contracts/push-new.js +8 -0
- package/dist/contracts/quotas.d.ts +0 -1
- package/dist/contracts/quotas.js +0 -5
- package/dist/contracts/repository-connections.d.ts +1 -1
- package/dist/contracts/resources.d.ts +21 -85
- package/dist/contracts/resources.js +17 -47
- package/dist/contracts/runtime-api.d.ts +799 -192
- package/dist/contracts/runtime-api.js +231 -100
- package/dist/contracts/runtime-app.d.ts +138 -0
- package/dist/contracts/runtime-app.js +21 -0
- package/dist/contracts/sf-config-v1.d.ts +5 -0
- package/dist/contracts/sf-config-v1.js +1 -1
- package/dist/contracts/sites.d.ts +17 -12
- package/dist/contracts/sites.js +30 -12
- package/dist/contracts/space-config.d.ts +46 -117
- package/dist/contracts/space-config.js +323 -25
- package/dist/contracts/spaces.d.ts +392 -256
- package/dist/contracts/spaces.js +163 -83
- package/dist/contracts/superadmin-emails.d.ts +27 -3
- package/dist/contracts/superadmin-emails.js +10 -15
- package/dist/contracts/superadmin-runtime.d.ts +2 -2
- package/dist/contracts/superadmin-spaces.d.ts +19 -142
- package/dist/contracts/superadmin-spaces.js +7 -0
- package/dist/contracts/tags.d.ts +66 -36
- package/dist/contracts/tags.js +195 -11
- package/dist/contracts/teams.d.ts +9 -9
- package/dist/contracts/transfers.d.ts +1 -2
- package/dist/contracts/variables.d.ts +4 -4
- package/dist/contracts/variables.js +4 -3
- package/dist/contracts/webhooks.d.ts +2 -2
- package/dist/contracts/zero.d.ts +437 -193
- package/dist/contracts/zero.js +149 -158
- package/dist/dashboard-paths/index.d.ts +3 -3
- package/dist/dashboard-paths/index.js +1 -0
- package/dist/docs/agent-prose.js +10 -8
- package/dist/docs/agent-setup.d.ts +6 -4
- package/dist/docs/agent-setup.js +26 -16
- package/dist/docs/agent-solutions.js +1 -1
- package/dist/docs/catalog.d.ts +35 -37
- package/dist/docs/catalog.js +36 -38
- package/dist/docs/error-docs.js +108 -44
- package/dist/utils/asset-fingerprint.d.ts +11 -0
- package/dist/utils/asset-fingerprint.js +46 -0
- package/dist/utils/auth-redirect.d.ts +1 -3
- package/dist/utils/auth-redirect.js +1 -21
- package/dist/utils/build-settings.d.ts +2 -0
- package/dist/utils/build-settings.js +61 -9
- package/dist/utils/credential-policy.d.ts +1 -1
- package/dist/utils/credential-policy.js +4 -0
- package/dist/utils/dns-instructions.d.ts +29 -0
- package/dist/utils/dns-instructions.js +78 -0
- package/dist/utils/domain-diagnostics.d.ts +29 -0
- package/dist/utils/domain-diagnostics.js +63 -0
- package/dist/utils/egress-policy.fixtures.json +51 -0
- package/dist/utils/grants.d.ts +53 -0
- package/dist/utils/grants.js +160 -0
- package/dist/utils/id-hints.d.ts +1 -0
- package/dist/utils/id-hints.js +55 -13
- package/dist/utils/local-space-state.js +1 -0
- package/dist/utils/page-colors.d.ts +50 -0
- package/dist/utils/page-colors.js +167 -0
- package/dist/utils/page-fonts.d.ts +2 -0
- package/dist/utils/page-fonts.js +4 -0
- package/dist/utils/pages.d.ts +19 -6
- package/dist/utils/pages.js +122 -49
- package/dist/utils/production-runtime.d.ts +18 -0
- package/dist/utils/production-runtime.js +28 -0
- package/dist/utils/publish-policy.d.ts +34 -4
- package/dist/utils/publish-policy.js +132 -61
- package/dist/utils/query-keys.d.ts +7 -13
- package/dist/utils/query-keys.js +12 -18
- package/dist/utils/source-archive-policy.d.ts +2 -0
- package/dist/utils/source-archive-policy.js +2 -0
- package/dist/utils/space-config.d.ts +5 -1
- package/dist/utils/space-config.js +35 -0
- package/dist/utils/static-runtime-policy.d.ts +5 -0
- package/dist/utils/static-runtime-policy.fixtures.json +57 -0
- package/dist/utils/static-runtime-policy.js +54 -10
- package/dist/utils/upload-session.d.ts +3 -0
- package/dist/utils/upload-session.js +31 -0
- package/package.json +6 -4
- package/dist/contracts/annotations.d.ts +0 -327
- package/dist/contracts/annotations.js +0 -235
- package/dist/contracts/generated-feature-launch-entries.d.ts +0 -2
- package/dist/contracts/generated-feature-launch-entries.js +0 -518
- package/dist/contracts/publishes.d.ts +0 -135
- package/dist/contracts/publishes.js +0 -162
- package/dist/utils/access-match.d.ts +0 -39
- package/dist/utils/access-match.js +0 -296
- package/dist/utils/sf-config-v1.d.ts +0 -2
- package/dist/utils/sf-config-v1.js +0 -542
package/dist/contracts/zero.js
CHANGED
|
@@ -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 =
|
|
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-
|
|
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/"
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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:
|
|
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 =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
177
|
+
runId: z.string().min(1).max(256),
|
|
178
|
+
source: z
|
|
149
179
|
.string()
|
|
150
180
|
.min(1)
|
|
151
|
-
.
|
|
152
|
-
|
|
153
|
-
|
|
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
|
|
187
|
+
export const zeroRuntimeEndpointSourceSchema = z
|
|
183
188
|
.object({
|
|
184
|
-
|
|
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:
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
531
|
-
artifactId: z.string().min(1)
|
|
532
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
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
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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" | "
|
|
21
|
-
export type TeamSettingsDestination = "access" | "audit" | "developer" | "developer/agents" | "developer/environment-variables" | "developer/handoffs" | "developer/
|
|
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
|
package/dist/docs/agent-prose.js
CHANGED
|
@@ -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
|
-
"
|
|
83
|
-
"
|
|
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
|
|
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.
|
|
220
|
-
" It is
|
|
221
|
-
"
|
|
222
|
-
"
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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.
|
|
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. */
|
package/dist/docs/agent-setup.js
CHANGED
|
@@ -47,10 +47,12 @@ function isSafeHttpUrl(url) {
|
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
49
|
* The single shared grammar for handoff links:
|
|
50
|
-
*
|
|
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
|
|
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
|
|
63
|
+
let expectedUrl;
|
|
62
64
|
try {
|
|
63
|
-
|
|
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 (
|
|
78
|
-
url.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
"`
|
|
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
|
-
"-
|
|
325
|
-
" the claim link with its expiry.
|
|
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
|
|
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."
|
|
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}`,
|