@spacefast/common 0.0.23 → 0.0.24
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/config/domains.d.ts +1 -1
- package/dist/config/domains.js +1 -0
- package/dist/config/index.d.ts +0 -1
- package/dist/config/index.js +0 -1
- package/dist/contracts/abuse.d.ts +2 -0
- package/dist/contracts/abuse.js +12 -0
- package/dist/contracts/access.d.ts +121 -9
- package/dist/contracts/access.js +73 -9
- package/dist/contracts/activity.d.ts +1 -1
- package/dist/contracts/activity.js +0 -9
- package/dist/contracts/api-keys.d.ts +21 -21
- package/dist/contracts/beta.js +1 -1
- package/dist/contracts/billing.d.ts +1 -34
- package/dist/contracts/billing.js +4 -53
- package/dist/contracts/builds.d.ts +8 -8
- package/dist/contracts/builds.js +10 -3
- package/dist/contracts/cli.d.ts +10 -0
- package/dist/contracts/cli.js +10 -0
- package/dist/contracts/collab-public.d.ts +63 -0
- package/dist/contracts/collab-public.js +46 -0
- package/dist/contracts/comments.d.ts +283 -54
- package/dist/contracts/comments.js +173 -52
- package/dist/contracts/common.d.ts +3 -3
- package/dist/contracts/device-auth.d.ts +6 -6
- package/dist/contracts/device-auth.js +1 -1
- package/dist/contracts/docs.d.ts +5 -5
- package/dist/contracts/domains.d.ts +36 -36
- package/dist/contracts/enums.d.ts +33 -30
- package/dist/contracts/enums.js +52 -38
- package/dist/contracts/error-code-meta.d.ts +37 -126
- package/dist/contracts/error-code-meta.js +12 -41
- package/dist/contracts/error-codes.d.ts +1 -1
- package/dist/contracts/error-codes.js +10 -32
- package/dist/contracts/execution.d.ts +35 -57
- package/dist/contracts/execution.js +41 -40
- package/dist/contracts/feature-lifecycle-core.d.ts +0 -6
- package/dist/contracts/feature-lifecycle-core.js +2 -31
- package/dist/contracts/feature-lifecycle.d.ts +8 -38
- package/dist/contracts/feature-lifecycle.js +16 -48
- package/dist/contracts/features.d.ts +15 -13
- package/dist/contracts/features.js +39 -38
- package/dist/contracts/functions.d.ts +97 -70
- package/dist/contracts/functions.js +101 -34
- package/dist/contracts/git.d.ts +1 -1
- package/dist/contracts/grants.d.ts +7 -3
- package/dist/contracts/grants.js +18 -9
- package/dist/contracts/internal.d.ts +14 -1
- package/dist/contracts/internal.js +17 -7
- package/dist/contracts/oauth-resources.d.ts +4 -3
- package/dist/contracts/oauth-resources.js +12 -3
- package/dist/contracts/operations.d.ts +90 -5
- package/dist/contracts/operations.js +75 -1
- package/dist/contracts/pages.d.ts +6 -1
- package/dist/contracts/pages.js +24 -1
- package/dist/contracts/plan-policy.d.ts +1 -3
- package/dist/contracts/plan-policy.js +1 -2
- package/dist/contracts/platform.d.ts +61 -148
- package/dist/contracts/platform.js +70 -126
- package/dist/contracts/publish-archive.d.ts +498 -0
- package/dist/contracts/publish-archive.js +25 -0
- package/dist/contracts/resources.d.ts +12 -24
- package/dist/contracts/resources.js +32 -12
- package/dist/contracts/runtime-api.d.ts +369 -175
- package/dist/contracts/runtime-api.js +372 -348
- package/dist/contracts/runtime-app.d.ts +127 -120
- package/dist/contracts/runtime-app.js +19 -10
- package/dist/contracts/runtime-db.d.ts +1 -1
- package/dist/contracts/runtime-services.d.ts +219 -0
- package/dist/contracts/runtime-services.js +437 -0
- package/dist/contracts/runtime-storage.d.ts +45 -32
- package/dist/contracts/runtime-storage.js +30 -8
- package/dist/contracts/space-config.d.ts +10 -16
- package/dist/contracts/space-config.js +14 -10
- package/dist/contracts/spaces.d.ts +241 -436
- package/dist/contracts/spaces.js +128 -145
- package/dist/contracts/superadmin-activity.d.ts +57 -4
- package/dist/contracts/superadmin-activity.js +48 -2
- package/dist/contracts/superadmin-search.d.ts +126 -0
- package/dist/contracts/superadmin-search.js +61 -0
- package/dist/contracts/superadmin-spaces.d.ts +358 -89
- package/dist/contracts/superadmin-spaces.js +97 -24
- package/dist/contracts/superadmin-teams.d.ts +28 -10
- package/dist/contracts/superadmin-teams.js +5 -8
- package/dist/contracts/superadmin.d.ts +0 -32
- package/dist/contracts/superadmin.js +3 -11
- package/dist/contracts/tags.d.ts +9 -9
- package/dist/contracts/teams.d.ts +38 -0
- package/dist/contracts/teams.js +34 -16
- package/dist/contracts/transfers.d.ts +0 -49
- package/dist/contracts/transfers.js +2 -6
- package/dist/contracts/usage.d.ts +61 -0
- package/dist/contracts/usage.js +64 -0
- package/dist/contracts/variables.d.ts +2 -7
- package/dist/contracts/variables.js +6 -5
- package/dist/contracts/webhooks.d.ts +2 -2
- package/dist/contracts/zero.d.ts +87 -56
- package/dist/contracts/zero.js +82 -20
- package/dist/dashboard-paths/index.d.ts +1 -1
- package/dist/dashboard-paths/index.js +1 -0
- package/dist/docs/agent-prose.d.ts +1 -1
- package/dist/docs/agent-prose.js +57 -61
- package/dist/docs/agent-setup.js +40 -13
- package/dist/docs/catalog.d.ts +19 -19
- package/dist/docs/catalog.js +30 -19
- package/dist/docs/error-docs.js +63 -151
- package/dist/test-helpers/fetch-stub.d.ts +16 -0
- package/dist/test-helpers/fetch-stub.js +24 -0
- package/dist/utils/build-settings.js +9 -3
- package/dist/utils/canonical-json.d.ts +10 -0
- package/dist/utils/canonical-json.js +21 -0
- package/dist/utils/cast-transport.d.ts +28 -0
- package/dist/utils/cast-transport.js +52 -0
- package/dist/utils/claim-token.d.ts +0 -8
- package/dist/utils/claim-token.js +3 -15
- package/dist/utils/credential-policy.d.ts +1 -1
- package/dist/utils/env-file.d.ts +17 -0
- package/dist/utils/env-file.js +96 -0
- package/dist/utils/error-code.d.ts +2 -0
- package/dist/utils/error-code.js +7 -0
- package/dist/utils/generate-space-name.d.ts +8 -10
- package/dist/utils/generate-space-name.js +206 -21
- package/dist/utils/grant-decision.fixtures.json +2938 -0
- package/dist/utils/grants.d.ts +2 -0
- package/dist/utils/grants.js +25 -2
- package/dist/utils/gravatar.d.ts +9 -0
- package/dist/utils/gravatar.js +31 -0
- package/dist/utils/idempotency.d.ts +6 -0
- package/dist/utils/idempotency.js +28 -0
- package/dist/utils/local-space-state.d.ts +1 -5
- package/dist/utils/local-space-state.js +7 -26
- package/dist/utils/page-fonts.d.ts +2 -2
- package/dist/utils/page-fonts.js +22 -4
- package/dist/utils/pages.d.ts +1 -2
- package/dist/utils/pages.js +21 -5
- package/dist/utils/publish-form-data.d.ts +12 -0
- package/dist/utils/publish-form-data.js +10 -0
- package/dist/utils/publish-policy.d.ts +20 -0
- package/dist/utils/publish-policy.fixtures.json +397 -0
- package/dist/utils/publish-policy.js +36 -19
- package/dist/utils/push-new.d.ts +7 -0
- package/dist/utils/push-new.js +7 -0
- package/dist/utils/query-keys.d.ts +0 -1
- package/dist/utils/query-keys.js +0 -1
- package/dist/utils/runtime-paths.d.ts +5 -0
- package/dist/utils/runtime-paths.js +5 -0
- package/dist/utils/runtime-upload-batch.d.ts +0 -5
- package/dist/utils/runtime-upload-batch.js +1 -27
- package/dist/utils/runtime-upload.d.ts +3 -13
- package/dist/utils/runtime-upload.js +4 -14
- package/dist/utils/space-config.d.ts +0 -33
- package/dist/utils/space-config.js +5 -326
- package/dist/utils/storage-policy.d.ts +1 -1
- package/dist/utils/storage-policy.js +3 -1
- package/dist/utils/structured-error.d.ts +14 -0
- package/dist/utils/structured-error.js +22 -0
- package/dist/utils/version-path.js +12 -3
- package/dist/vocabulary.d.ts +5 -3
- package/dist/vocabulary.js +13 -10
- package/package.json +10 -5
- package/dist/config/postgres-budget.d.ts +0 -129
- package/dist/config/postgres-budget.js +0 -140
- package/dist/config/postgres-budget.json +0 -51
- package/dist/contracts/archives.d.ts +0 -331
- package/dist/contracts/archives.js +0 -120
- package/dist/contracts/deployments.d.ts +0 -574
- package/dist/contracts/deployments.js +0 -97
- package/dist/contracts/sites.d.ts +0 -416
- package/dist/contracts/sites.js +0 -168
- package/dist/contracts/superadmin-queues.d.ts +0 -414
- package/dist/contracts/superadmin-queues.js +0 -202
- package/dist/contracts/superadmin-runtime.d.ts +0 -27
- package/dist/contracts/superadmin-runtime.js +0 -38
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/utils/api.d.ts +0 -4
- package/dist/utils/api.js +0 -13
package/dist/contracts/zero.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { appRoutePathSchema, EXECUTION_ENV_MAX_KEYS, executionBundleSchema, executionCapabilitiesSchema, executionEnvNameSchema, executionIdentifierSchema, executionInspectPolicySchema,
|
|
2
|
+
import { appRoutePathSchema, EXECUTION_ENV_MAX_KEYS, executionBundleSchema, executionCapabilitiesSchema, executionEnvNameSchema, executionIdentifierSchema, executionInspectPolicySchema, executionSha256Schema, executionStaticFileSchema, httpMethodSchema, LAKEBED_SERVER_ENV_FILE, PLATFORM_ROUTE_PREFIX, runtimeRealtimeModeSchema, SERVER_ENV_FILE, } from "./execution.js";
|
|
3
3
|
/**
|
|
4
4
|
* Spacefast Zero: capsules — schema, queries, mutations and endpoints that run
|
|
5
5
|
* together inside one parent-owned transaction — executed by the native runner
|
|
@@ -12,6 +12,13 @@ import { appRoutePathSchema, EXECUTION_ENV_MAX_KEYS, executionBundleSchema, exec
|
|
|
12
12
|
* for the narrow surface both products can host.
|
|
13
13
|
*/
|
|
14
14
|
export const ZERO_SOURCE_METADATA_KEY = "statticZero";
|
|
15
|
+
/**
|
|
16
|
+
* The tag a compiled capsule artifact carries — in the finalize payload, the
|
|
17
|
+
* runtime config, the install profile, the client config the browser reads, and
|
|
18
|
+
* every realtime event — so PHP and the Rust runner can tell what they were
|
|
19
|
+
* handed. It is a property of the artifact the tree produced, not a kind a
|
|
20
|
+
* space declares: a publish can carry this beside a worker.
|
|
21
|
+
*/
|
|
15
22
|
export const ZERO_RUNTIME_KIND = "zero";
|
|
16
23
|
export const ZERO_PLATFORM_ROUTE_PREFIX = `${PLATFORM_ROUTE_PREFIX}/zero`;
|
|
17
24
|
/**
|
|
@@ -37,9 +44,57 @@ export const ZERO_RUNTIME_CONFIG_PATH = `${ZERO_RUNTIME_STORAGE_ROOT}/config.jso
|
|
|
37
44
|
export const ZERO_RUNTIME_BINARY_PATH = `${ZERO_RUNTIME_STORAGE_ROOT}/bin/stattic-runtime`;
|
|
38
45
|
export const ZERO_RUNTIME_BINARY_COMPATIBILITY_TARGET = "wpcloud-linux-x86_64-gnu-v1";
|
|
39
46
|
export const ZERO_RUNTIME_BUNDLE_ROOT = `${ZERO_RUNTIME_STORAGE_ROOT}/bundles`;
|
|
40
|
-
export const ZERO_RUNTIME_CLIENT_BUNDLE_PATH = `${ZERO_RUNTIME_BUNDLE_ROOT}/client.js`;
|
|
41
47
|
export const ZERO_RUNTIME_SERVER_BUNDLE_PATH = `${ZERO_RUNTIME_BUNDLE_ROOT}/server.qjs.mjs`;
|
|
42
|
-
|
|
48
|
+
/** The compiled capsule client, served at the app root. */
|
|
49
|
+
export const ZERO_PUBLIC_CLIENT_BUNDLE_PATH = "client.js";
|
|
50
|
+
/**
|
|
51
|
+
* Where platform-owned client bytes serve from: same origin as the space, one
|
|
52
|
+
* content-addressed directory per platform build.
|
|
53
|
+
*
|
|
54
|
+
* Same origin is the whole point. Browsers have partitioned the HTTP cache by
|
|
55
|
+
* top-level site since 2020, so a shared third-party CDN buys nothing a
|
|
56
|
+
* same-origin URL does not — and costs a `script-src` hole. What a stable,
|
|
57
|
+
* content-addressed URL does buy is real: the digest changes only when the
|
|
58
|
+
* platform bytes change, so publishing a capsule a hundred times re-downloads
|
|
59
|
+
* the capsule's own bundle a hundred times and the platform's exactly once.
|
|
60
|
+
*
|
|
61
|
+
* Underscore-prefixed by framework convention (`_next/static`, `_astro`), which
|
|
62
|
+
* is also how the runtime recognises it as immutable — see `immutable_path` in
|
|
63
|
+
* `crates/stattic-runtime-core/src/finalize.rs`.
|
|
64
|
+
*/
|
|
65
|
+
export const ZERO_PLATFORM_ASSET_NAMESPACE = "_spacefast";
|
|
66
|
+
export const ZERO_PLATFORM_ASSET_ROOT = `${ZERO_PLATFORM_ASSET_NAMESPACE}/platform`;
|
|
67
|
+
/** The serving path of one file in the platform build identified by `digest`. */
|
|
68
|
+
export function zeroPlatformAssetPath(digest, fileName) {
|
|
69
|
+
return `${ZERO_PLATFORM_ASSET_ROOT}/${digest}/${fileName}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The compiled capsule server, which the QuickJS runner instantiates whole.
|
|
73
|
+
*
|
|
74
|
+
* 768 KiB is inherited, not measured: nobody has yet established what the
|
|
75
|
+
* runner actually tolerates, and DS77 keeps the number only until someone does.
|
|
76
|
+
* It binds a real resource — the runner holds the module graph in the site's
|
|
77
|
+
* PHP-worker memory before executing anything — so it stays low deliberately;
|
|
78
|
+
* server code is handlers, not an application.
|
|
79
|
+
*/
|
|
80
|
+
export const ZERO_SERVER_BUNDLE_MAX_BYTES = 786_432;
|
|
81
|
+
/**
|
|
82
|
+
* The tenant client bundle, and every other file the compile ships as content.
|
|
83
|
+
*
|
|
84
|
+
* This measures what the tenant publishes, and only that: the SDK, the kit, the
|
|
85
|
+
* chart layer, preact, recharts and lucide are externals resolved through the
|
|
86
|
+
* shell's import map to {@link ZERO_PLATFORM_ASSET_ROOT}, so platform bytes are
|
|
87
|
+
* never weighed here (DS42). What remains is author code plus whatever npm the
|
|
88
|
+
* author chose to bundle.
|
|
89
|
+
*
|
|
90
|
+
* 8 MiB matches the Functions per-entry cap (DS77) because the same reasoning
|
|
91
|
+
* applies: a client bundle is static bytes to a browser, so nothing on the
|
|
92
|
+
* serving side binds it, and the cap exists to fail an accidental
|
|
93
|
+
* hundred-megabyte dependency in our tooling — where the build can say which
|
|
94
|
+
* modules are large — rather than at upload. It is a guardrail, not a budget;
|
|
95
|
+
* the size report is what an author should be reading.
|
|
96
|
+
*/
|
|
97
|
+
export const ZERO_CLIENT_BUNDLE_MAX_BYTES = 8 * 1024 * 1024;
|
|
43
98
|
/**
|
|
44
99
|
* A capsule's compiled server travels as version metadata, not as content, so a
|
|
45
100
|
* server-only change leaves the published app shell byte-identical — and the
|
|
@@ -60,10 +115,16 @@ export const ZERO_MANAGED_TABLE_COLUMNS = ["id", "createdAt", "updatedAt"];
|
|
|
60
115
|
export const ZERO_RESERVED_APP_ROUTE_PATHS = [
|
|
61
116
|
"/",
|
|
62
117
|
"/index.html",
|
|
63
|
-
|
|
118
|
+
`/${ZERO_PUBLIC_CLIENT_BUNDLE_PATH}`,
|
|
64
119
|
"/auth/callback",
|
|
65
120
|
];
|
|
66
|
-
|
|
121
|
+
// The whole platform namespace is reserved, not just the one directory a build
|
|
122
|
+
// happens to write: an endpoint claiming a path under it would shadow bytes the
|
|
123
|
+
// shell's import map already told the browser to fetch.
|
|
124
|
+
export const ZERO_RESERVED_APP_ROUTE_PREFIXES = [
|
|
125
|
+
"/auth/",
|
|
126
|
+
`/${ZERO_PLATFORM_ASSET_NAMESPACE}/`,
|
|
127
|
+
];
|
|
67
128
|
const zeroNameSchema = executionIdentifierSchema;
|
|
68
129
|
const zeroRoutePathSchema = appRoutePathSchema({
|
|
69
130
|
subject: "Zero capsule routes",
|
|
@@ -203,18 +264,21 @@ export const zeroTableSchema = z
|
|
|
203
264
|
export const zeroDatabaseSchema = z.record(z.string(), zeroTableSchema);
|
|
204
265
|
const zeroSha256Schema = executionSha256Schema;
|
|
205
266
|
/**
|
|
206
|
-
*
|
|
207
|
-
* client and server fit comfortably under the ceiling, and inlining keeps the
|
|
267
|
+
* The compiled server travels inline in the finalize payload; inlining keeps the
|
|
208
268
|
* whole version one atomic write. Functions bundles do the opposite and land
|
|
209
269
|
* in the content store first; see `functionsBundleRefSchema`.
|
|
270
|
+
*
|
|
271
|
+
* The client never travels this way. It is content — the browser fetches it
|
|
272
|
+
* from the version tree like any other asset — so it ships as a static file.
|
|
210
273
|
*/
|
|
211
274
|
export const zeroRuntimeBundleSchema = executionBundleSchema({
|
|
212
275
|
bundleRoot: ZERO_RUNTIME_BUNDLE_ROOT,
|
|
213
|
-
maxBytes:
|
|
276
|
+
maxBytes: ZERO_SERVER_BUNDLE_MAX_BYTES,
|
|
214
277
|
});
|
|
215
278
|
export const zeroRuntimeStaticFileSchema = executionStaticFileSchema({
|
|
216
279
|
storageRoot: ZERO_RUNTIME_STORAGE_ROOT,
|
|
217
|
-
maxBytes:
|
|
280
|
+
maxBytes: ZERO_CLIENT_BUNDLE_MAX_BYTES,
|
|
281
|
+
platformAssetRoot: ZERO_PLATFORM_ASSET_ROOT,
|
|
218
282
|
});
|
|
219
283
|
export const zeroRuntimeRunSourceSchema = z
|
|
220
284
|
.object({
|
|
@@ -493,7 +557,9 @@ export const zeroRuntimeFinalizePayloadSchema = z
|
|
|
493
557
|
install: zeroRuntimeInstallProfileSchema,
|
|
494
558
|
artifact: zeroCapsuleArtifactSchema,
|
|
495
559
|
bundles: z.array(zeroRuntimeBundleSchema).max(8).default([]),
|
|
496
|
-
|
|
560
|
+
// The shell, the client bundle, the utility stylesheet, and the platform
|
|
561
|
+
// build's own entries and shared chunks.
|
|
562
|
+
staticFiles: z.array(zeroRuntimeStaticFileSchema).max(64).default([]),
|
|
497
563
|
endpointSources: z.array(zeroRuntimeEndpointSourceSchema).max(128).default([]),
|
|
498
564
|
runSources: z.array(zeroRuntimeRunSourceSchema).max(128).default([]),
|
|
499
565
|
migrations: zeroMigrationPlanSchema.nullable().default(null),
|
|
@@ -567,23 +633,24 @@ export const zeroDeployMetadataSchema = z
|
|
|
567
633
|
})
|
|
568
634
|
.strict();
|
|
569
635
|
/**
|
|
570
|
-
* What a live Zero capsule is, as the `
|
|
636
|
+
* What a live Zero capsule is, as the `capsule` member of a space's runtime
|
|
637
|
+
* status.
|
|
571
638
|
*
|
|
572
639
|
* There is no `versionId` here: this only ever describes the version the status
|
|
573
640
|
* response already names as live, and repeating it would invite the two to
|
|
574
641
|
* disagree. `artifactId` stays because it identifies the built capsule rather
|
|
575
|
-
* than the publish that carries it.
|
|
642
|
+
* than the publish that carries it. There is no kind tag either — the member it
|
|
643
|
+
* rides in already says which product answered.
|
|
576
644
|
*
|
|
577
645
|
* Every field is derived from the version's own finalize metadata, which is
|
|
578
646
|
* what makes this safe to answer for a space whose runtime is asleep,
|
|
579
647
|
* mid-move, or failing: nothing here costs a call to the space's origin. It is
|
|
580
648
|
* also why there are no counts of things listed here — a caller that wants
|
|
581
649
|
* `tables.length` can take it — and why the database is not a field: a capsule
|
|
582
|
-
* always owns one, so
|
|
650
|
+
* always owns one, so its presence already said so.
|
|
583
651
|
*/
|
|
584
652
|
export const zeroAppSchema = z
|
|
585
653
|
.object({
|
|
586
|
-
runtimeKind: z.literal(ZERO_RUNTIME_KIND),
|
|
587
654
|
artifactId: z.string().min(1),
|
|
588
655
|
appName: z.string().min(1),
|
|
589
656
|
serverRuntime: z.literal("quickjs-rust"),
|
|
@@ -616,11 +683,6 @@ export const zeroAppSchema = z
|
|
|
616
683
|
realtime: zeroRuntimePublicRealtimeConfigSchema,
|
|
617
684
|
})
|
|
618
685
|
.strict();
|
|
619
|
-
// Both products emit the same log shape; `handlerName` carries the mutation
|
|
620
|
-
// name here and the worker handler name on Functions.
|
|
621
|
-
export const zeroLogListQuerySchema = executionLogListQuerySchema;
|
|
622
|
-
export const zeroLogEntrySchema = executionLogEntrySchema;
|
|
623
|
-
export const zeroLogListResponseSchema = executionLogListResponseSchema;
|
|
624
686
|
export const zeroRealtimeEventIntakeResponseSchema = z
|
|
625
687
|
.object({
|
|
626
688
|
accepted: z.literal(true),
|
|
@@ -651,7 +713,7 @@ export function createZeroCapsuleArtifact(input) {
|
|
|
651
713
|
appName: input.appName,
|
|
652
714
|
serverRuntime: "quickjs-rust",
|
|
653
715
|
client: {
|
|
654
|
-
bundlePath: input.clientBundlePath ??
|
|
716
|
+
bundlePath: input.clientBundlePath ?? ZERO_PUBLIC_CLIENT_BUNDLE_PATH,
|
|
655
717
|
basePathAware: true,
|
|
656
718
|
},
|
|
657
719
|
server: {
|
|
@@ -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", "access", "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", "push", "sign-in", "start", "terminal", "two-factor"];
|
|
8
8
|
/**
|
|
9
9
|
* Dashboard route first-segments that cannot be used as space slugs.
|
|
10
10
|
*
|
|
@@ -40,7 +40,7 @@ export declare const SAVE_STATE_MCP_PROSE: string;
|
|
|
40
40
|
export declare const UPDATE_SPACE_PROSE: string;
|
|
41
41
|
/**
|
|
42
42
|
* The CLAIM / CONTINUE verb: show the claim link, and after the user claims,
|
|
43
|
-
* self-correct by exchanging the
|
|
43
|
+
* self-correct by exchanging the space key once for a durable credential.
|
|
44
44
|
*/
|
|
45
45
|
export declare const CONTINUE_AFTER_CLAIM_PROSE: string;
|
|
46
46
|
/**
|
package/dist/docs/agent-prose.js
CHANGED
|
@@ -16,7 +16,7 @@ import { serviceOrigin } from "../config/domains.js";
|
|
|
16
16
|
// the receipt shape or secret handling changes, change it here once.
|
|
17
17
|
const API_ORIGIN = serviceOrigin("api", "prod");
|
|
18
18
|
const PUBLISH_ENDPOINT = `${API_ORIGIN}/v1/publish`;
|
|
19
|
-
const EXCHANGE_ENDPOINT = `${API_ORIGIN}/v1/
|
|
19
|
+
const EXCHANGE_ENDPOINT = `${API_ORIGIN}/v1/claim/exchange`;
|
|
20
20
|
// Centralized secret/credential patterns excluded from a publish archive. The
|
|
21
21
|
// `find` preflight and the `zip -x` exclusions are derived from the same list so
|
|
22
22
|
// they can never disagree, and the no-install / skill copies stay identical.
|
|
@@ -38,6 +38,21 @@ export const ARCHIVE_SECRET_GLOBS = [
|
|
|
38
38
|
"*.crt",
|
|
39
39
|
"*id_rsa*",
|
|
40
40
|
];
|
|
41
|
+
const ARCHIVE_REJECTED_GLOBS = [
|
|
42
|
+
...ARCHIVE_SECRET_GLOBS,
|
|
43
|
+
".git",
|
|
44
|
+
".spacefast",
|
|
45
|
+
".ssh",
|
|
46
|
+
".aws",
|
|
47
|
+
".kube",
|
|
48
|
+
".docker",
|
|
49
|
+
"node_modules",
|
|
50
|
+
".next",
|
|
51
|
+
".nuxt",
|
|
52
|
+
".cache",
|
|
53
|
+
".turbo",
|
|
54
|
+
"coverage",
|
|
55
|
+
];
|
|
41
56
|
function findExpr(globs) {
|
|
42
57
|
return globs
|
|
43
58
|
.map((glob) => (glob.includes("/") ? `-path "${glob}"` : `-name "${glob}"`))
|
|
@@ -48,7 +63,7 @@ function findExpr(globs) {
|
|
|
48
63
|
* them out of logs and transcripts, and never echo them back.
|
|
49
64
|
*/
|
|
50
65
|
export const SECRET_RULES_PROSE = [
|
|
51
|
-
"Treat
|
|
66
|
+
"Treat space keys, upload tokens, device codes, and API keys as credentials. Do not print",
|
|
52
67
|
"them, paste them into chat, commit them, archive them, or include them in shared logs. Avoid",
|
|
53
68
|
"`set -x`, persistent shell history, CI log echoing, and command transcripts when secrets are",
|
|
54
69
|
"present. On shared hosts, prefer one-off environment variables or restrictive temporary files,",
|
|
@@ -56,13 +71,15 @@ export const SECRET_RULES_PROSE = [
|
|
|
56
71
|
].join("\n");
|
|
57
72
|
export const POSITIONING_PROSE = [
|
|
58
73
|
"Spacefast is a static host for AI-made web artifacts: permanent live URLs, immutable version",
|
|
59
|
-
"URLs, anonymous publish with a claim link, and agent continuation after claim.
|
|
60
|
-
"static files
|
|
61
|
-
"anonymous lane.",
|
|
74
|
+
"URLs, anonymous publish with a claim link, and agent continuation after claim. Spaces serve",
|
|
75
|
+
"static files; app code runs only through a declared Zero or Functions runtime.",
|
|
62
76
|
"",
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
77
|
+
"Do not claim that no-account publish followed by claim is unique to Spacefast. here.now and",
|
|
78
|
+
"Netlify offer native anonymous publish-and-claim flows; Cloudflare offers temporary accounts",
|
|
79
|
+
"and claim deployments; Vercel lets agents and platforms transfer deployments to users.",
|
|
80
|
+
"Spacefast's narrower lane is direct static HTTP publishing with separate Access and Claim",
|
|
81
|
+
"authority, immutable versions, rollback, and agent continuation after claim. GitHub Pages",
|
|
82
|
+
"remains repository-native and has no equivalent anonymous ownership-handoff flow documented.",
|
|
66
83
|
].join("\n");
|
|
67
84
|
export const EXPLAIN_TO_USER_PROSE = [
|
|
68
85
|
"Present Access and Claim as the only actions. State anonymous expiry in user terms: the Space",
|
|
@@ -186,16 +203,16 @@ export const SAVE_STATE_PROSE = [
|
|
|
186
203
|
].join("\n");
|
|
187
204
|
/** Curl/API skill: no CLI/MCP required to continue in-session. */
|
|
188
205
|
export const SAVE_STATE_API_PROSE = [
|
|
189
|
-
"After the first publish, keep `data.space.id` and `data.claim.
|
|
206
|
+
"After the first publish, keep `data.space.id` and `data.claim.key` (when present) in agent",
|
|
190
207
|
"memory for this session so later publishes update the same space. You may write non-secret",
|
|
191
208
|
'`.spacefast/space.json` as `{"space":"<spc_id>"}` when helpful. Do not recreate credential',
|
|
192
|
-
"state with shell redirections. Keep
|
|
209
|
+
"state with shell redirections. Keep space keys and `.spacefast/state.json` out of publish",
|
|
193
210
|
"archives, commits, logs, and chat.",
|
|
194
211
|
].join("\n");
|
|
195
212
|
export const SAVE_STATE_MCP_PROSE = [
|
|
196
213
|
"After the first publish, On-Device MCP persists `.spacefast/` automatically so the next",
|
|
197
214
|
"`publish` updates the same space. Hosted MCP has no local filesystem state; keep the space id",
|
|
198
|
-
"in the current agent session or pass it explicitly. Never ask the user to paste
|
|
215
|
+
"in the current agent session or pass it explicitly. Never ask the user to paste space keys",
|
|
199
216
|
"or access tokens into chat.",
|
|
200
217
|
].join("\n");
|
|
201
218
|
/**
|
|
@@ -207,30 +224,30 @@ export const UPDATE_SPACE_PROSE = [
|
|
|
207
224
|
"never re-run the first-publish recipe when saved state exists:",
|
|
208
225
|
"",
|
|
209
226
|
"```bash",
|
|
210
|
-
'curl -q -sS -H "Authorization: Bearer <
|
|
227
|
+
'curl -q -sS -H "Authorization: Bearer <space-key-or-access-token>" \\',
|
|
211
228
|
" --form-string 'spaceId=<spc_id>' \\",
|
|
212
229
|
' -F "files=@index.html" \\',
|
|
213
230
|
` ${PUBLISH_ENDPOINT}`,
|
|
214
231
|
"```",
|
|
215
232
|
"",
|
|
216
|
-
"Use the saved
|
|
217
|
-
"spaces. Send a folder as `-F archive=@site.zip` exactly like a first publish
|
|
218
|
-
"
|
|
233
|
+
"Use the saved space key for an unclaimed anonymous space, or an access token for owned",
|
|
234
|
+
"spaces. Send a folder or project as `-F archive=@site.zip` exactly like a first publish.",
|
|
235
|
+
"Archive uploads return a durable Build receipt; follow `data.next` until the version is ready.",
|
|
219
236
|
].join("\n");
|
|
220
237
|
/**
|
|
221
238
|
* The CLAIM / CONTINUE verb: show the claim link, and after the user claims,
|
|
222
|
-
* self-correct by exchanging the
|
|
239
|
+
* self-correct by exchanging the space key once for a durable credential.
|
|
223
240
|
*/
|
|
224
241
|
export const CONTINUE_AFTER_CLAIM_PROSE = [
|
|
225
|
-
"Always show the user the claim link (`data.claim.
|
|
226
|
-
'If the user says "claimed", check claim status and exchange the saved
|
|
242
|
+
"Always show the user the claim link (`data.claim.claimUrl`) and its expiry (`data.claim.expiresAt`).",
|
|
243
|
+
'If the user says "claimed", check claim status and exchange the saved space key; do not wait',
|
|
227
244
|
"for a publish failure before continuing.",
|
|
228
|
-
"After the user claims the space in the browser, the saved
|
|
245
|
+
"After the user claims the space in the browser, the saved space key stops authorizing",
|
|
229
246
|
"publishes: the next publish fails with error code `space_claimed_credential_available`. That",
|
|
230
247
|
"failure is the signal to upgrade your credential — call the exchange endpoint exactly once:",
|
|
231
248
|
"",
|
|
232
249
|
"```bash",
|
|
233
|
-
'curl -q -sS -X POST -H "Authorization: Bearer <
|
|
250
|
+
'curl -q -sS -X POST -H "Authorization: Bearer <space-key>" \\',
|
|
234
251
|
` ${EXCHANGE_ENDPOINT}`,
|
|
235
252
|
"```",
|
|
236
253
|
"",
|
|
@@ -311,8 +328,7 @@ export const PUBLISH_SEQUENCE_PROSE = [
|
|
|
311
328
|
"Publish a single file after rejecting symbolic links in the file or any existing ancestor:",
|
|
312
329
|
"Set `SPACEFAST_PUBLISH_TARGET` through the agent runtime/environment, not by interpolating",
|
|
313
330
|
"a repository path into this source. The recipe passes it as a separately quoted argv element.",
|
|
314
|
-
"Fresh serving capacity can
|
|
315
|
-
"stable idempotency identity, follows `Retry-After`, and stops after ten attempts:",
|
|
331
|
+
"Fresh serving capacity can make the first request take longer while Spacefast provisions it:",
|
|
316
332
|
"",
|
|
317
333
|
"```bash",
|
|
318
334
|
': "${SPACEFAST_PUBLISH_TARGET:?set this through the runtime environment}"',
|
|
@@ -327,24 +343,10 @@ export const PUBLISH_SEQUENCE_PROSE = [
|
|
|
327
343
|
"idempotency_key=\"publish-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \\n')\"",
|
|
328
344
|
"idempotency_principal=\"$(od -An -N32 -tx1 /dev/urandom | tr -d ' \\n')\"",
|
|
329
345
|
'api_origin="${SPACEFAST_API_ORIGIN:-https://api.spacefast.com}"',
|
|
330
|
-
'
|
|
331
|
-
|
|
332
|
-
"
|
|
333
|
-
"
|
|
334
|
-
' response="$(curl -q -sS -D "$headers_file" -F "files=@\\"$escaped_path\\"" \\',
|
|
335
|
-
' -H "Idempotency-Key: $idempotency_key" \\',
|
|
336
|
-
' -H "x-spacefast-idempotency-principal: $idempotency_principal" \\',
|
|
337
|
-
' "$api_origin/v1/publish")"',
|
|
338
|
-
' if ! printf \'%s\' "$response" | grep -Eq \'"code"[[:space:]]*:[[:space:]]*"space_capacity_warming"\'; then',
|
|
339
|
-
" printf '%s\\n' \"$response\"",
|
|
340
|
-
" break",
|
|
341
|
-
" fi",
|
|
342
|
-
' [ "$attempt" -lt 10 ] || { printf \'%s\\n\' "$response"; exit 1; }',
|
|
343
|
-
' retry_after="$(awk \'tolower($1) == "retry-after:" { gsub("\\r", "", $2); print $2; exit }\' "$headers_file")"',
|
|
344
|
-
' case "$retry_after" in ""|*[!0-9]*) retry_after=20 ;; esac',
|
|
345
|
-
" attempt=$((attempt + 1))",
|
|
346
|
-
' sleep "$retry_after"',
|
|
347
|
-
"done",
|
|
346
|
+
'curl -q -sS -F "files=@\\"$escaped_path\\"" \\',
|
|
347
|
+
' -H "Idempotency-Key: $idempotency_key" \\',
|
|
348
|
+
' -H "x-spacefast-idempotency-principal: $idempotency_principal" \\',
|
|
349
|
+
' "$api_origin/v1/publish"',
|
|
348
350
|
"SPACEFAST_PUBLISH",
|
|
349
351
|
"```",
|
|
350
352
|
"",
|
|
@@ -361,14 +363,17 @@ export const PUBLISH_SEQUENCE_PROSE = [
|
|
|
361
363
|
'publish_target="$1"',
|
|
362
364
|
'case "$publish_target" in -*) publish_target="./$publish_target" ;; esac',
|
|
363
365
|
...LEXICAL_PUBLISH_PATH_PREFLIGHT,
|
|
364
|
-
|
|
366
|
+
'[ -d "$publish_target" ] || { printf "error: publish folder is not a directory\\n" >&2; exit 2; }',
|
|
367
|
+
`unsafe_path="$(find "$publish_target" \\( ${findExpr(ARCHIVE_REJECTED_GLOBS)} \\) -print -quit)"`,
|
|
368
|
+
'[ -z "$unsafe_path" ] || { printf "error: unsafe entry in publish root: %s\\n" "$unsafe_path" >&2; exit 2; }',
|
|
365
369
|
'! find "$publish_target" -type l -print -quit | grep -q . || { printf "error: symbolic link in publish root\\n" >&2; exit 2; }',
|
|
366
370
|
...SECURE_ARCHIVE_RECIPE,
|
|
367
371
|
"SPACEFAST_PUBLISH",
|
|
368
372
|
"```",
|
|
369
373
|
"",
|
|
370
|
-
"
|
|
371
|
-
"
|
|
374
|
+
"The preflight exits before upload when it finds a credential-like file, repository/auth state,",
|
|
375
|
+
"an unrelated build/cache directory, or a symbolic link. Choose a narrower generated output",
|
|
376
|
+
"directory or remove the unsafe entry before trying again.",
|
|
372
377
|
].join("\n");
|
|
373
378
|
/**
|
|
374
379
|
* What the JSON publish receipt contains and how to read it. The receipt is the
|
|
@@ -378,19 +383,21 @@ export const RECEIPT_SHAPE_PROSE = [
|
|
|
378
383
|
"The JSON receipt is the source of truth:",
|
|
379
384
|
"",
|
|
380
385
|
"- `data.space.liveUrl` — the live URL.",
|
|
381
|
-
"- `data.access.url` and `data.access.expiresAt` — reusable access
|
|
386
|
+
"- `data.access.url` and `data.access.expiresAt` — reusable access when present on an owned publish receipt.",
|
|
382
387
|
" It is not a stable URL or recipient Link; clients must not persist it as Space state.",
|
|
383
388
|
"- `data.version.immutableUrl` — the permanent address of the frozen Version. The bare URL",
|
|
384
389
|
" carries no authority: use the receipt's Access URL for the author's browser or create a scoped",
|
|
385
390
|
" Link for a recipient. It is reserved in the initial receipt. If the publish is still",
|
|
386
|
-
" finalizing, poll `data.links.status` with the
|
|
391
|
+
" finalizing, poll `data.links.status` with the space key until the status is `ready`.",
|
|
387
392
|
"- `data.upload` — signed upload instructions for manifest publishes when file bytes were not sent",
|
|
388
393
|
" with the request. Send each `data.upload.targets[]` instruction exactly as returned, treat",
|
|
389
394
|
" authorization headers and upload tokens as secrets, then finalize.",
|
|
390
|
-
"- `data.claim.
|
|
395
|
+
"- `data.claim.claimUrl` and `data.claim.expiresAt` — the space is anonymous and expires unless claimed;",
|
|
391
396
|
" always show the user the claim link and the deadline so the space becomes theirs.",
|
|
392
|
-
"- `data.claim.
|
|
393
|
-
"
|
|
397
|
+
"- `data.claim.url` — the site door: the live URL carrying the key in `/__/<key>` form. Anonymous",
|
|
398
|
+
" receipts have no `data.access`; this is the link that opens the private site in a browser.",
|
|
399
|
+
"- `data.claim.key` — the space key, a secret capability. For anonymous spaces, use it as",
|
|
400
|
+
" `Authorization: Bearer <space-key>` when polling `data.links.status`, finalizing uploads,",
|
|
394
401
|
" or updating the same `spaceId`. Save it to `.spacefast/state.json` so the next publish",
|
|
395
402
|
" updates this space. After the user claims the space, exchange it once at",
|
|
396
403
|
` \`POST ${EXCHANGE_ENDPOINT}\` for a durable credential.`,
|
|
@@ -420,19 +427,8 @@ export const SIGNED_UPLOAD_PROSE = [
|
|
|
420
427
|
"local file bytes for `target.path`. Then call `data.links.finalize` and share the final receipt.",
|
|
421
428
|
"If the session expires or only a page of files is returned, follow `data.links.resume`.",
|
|
422
429
|
"",
|
|
423
|
-
"
|
|
424
|
-
"",
|
|
425
|
-
"```bash",
|
|
426
|
-
"curl -q -sS https://api.spacefast.com/v1/spaces/{spaceId}/imports \\",
|
|
427
|
-
' -H "content-type: application/json" \\',
|
|
428
|
-
' -H "Authorization: Bearer $SPACEFAST_TOKEN" \\',
|
|
429
|
-
' -d \'{"source":{"kind":"url","url":"https://example.com/site.zip"}}\'',
|
|
430
|
-
"```",
|
|
431
|
-
"",
|
|
432
|
-
"If the archive is only local to the agent, create an upload-sourced import, PUT the archive to",
|
|
433
|
-
"`{archiveUpload.baseUrl}/{archiveUpload.files[0]}` with the returned bearer token, then finalize",
|
|
434
|
-
"the import. If the token expires, fetch/resume the import for fresh `archiveUpload` instructions.",
|
|
435
|
-
"Do not base64 large archives into chat or MCP tool arguments.",
|
|
430
|
+
"A manifest entry may name a `sourceUrl`, so bytes that already live on a public HTTPS URL never",
|
|
431
|
+
"have to round-trip through the agent. Do not base64 large archives into chat or MCP tool arguments.",
|
|
436
432
|
].join("\n");
|
|
437
433
|
/**
|
|
438
434
|
* Named fragments for the skill-template generator. The templates carry
|
package/dist/docs/agent-setup.js
CHANGED
|
@@ -17,7 +17,19 @@ export const agentSetupHostLabel = agentSetupParsedUrl.host + agentSetupParsedUr
|
|
|
17
17
|
export const agentSetupPrompt = `Fetch ${agentSetupUrl}`;
|
|
18
18
|
// The no-install publish: one POST, no account. Single-sourced so the
|
|
19
19
|
// marketing pages and the setup doc can never show different commands.
|
|
20
|
-
|
|
20
|
+
//
|
|
21
|
+
// `?wait=1` is load-bearing, not decoration. Without it the request takes the
|
|
22
|
+
// default finalize budget (SPACEFAST_PUBLISH_DEFAULT_WAIT_MS, 1.5s), so the 201
|
|
23
|
+
// comes back with `activation.outcome: "pending"` plus a `next.action: "poll"`
|
|
24
|
+
// pointer — measured in production at ~4.2s to a receipt describing a site that
|
|
25
|
+
// is not serving yet, settling ~37s later. A one-shot agent then has to run a
|
|
26
|
+
// poll loop against an endpoint that requires the space key as bearer. With
|
|
27
|
+
// `?wait=1` the same publish returns `activation.outcome: "activated"` and
|
|
28
|
+
// `next.action: "done"`, so the documented happy path is genuinely one command.
|
|
29
|
+
//
|
|
30
|
+
// The URL is quoted because `?` is a glob character in zsh: unquoted, the
|
|
31
|
+
// documented command dies with "no matches found" before curl ever runs.
|
|
32
|
+
export const anonymousPublishCurlCommand = `curl -F "files=@index.html" "${serviceOrigin("api", "prod")}/v1/publish?wait=1"`;
|
|
21
33
|
/*
|
|
22
34
|
* Agent handoff contract: private links carry a one-use path token on
|
|
23
35
|
* https://<access-origin>/agent/<documentId>/<token>; durable agent credentials stay encrypted at
|
|
@@ -366,8 +378,8 @@ export function aiInstructionsText() {
|
|
|
366
378
|
" — for anonymous spaces — the claim link with its expiry. Bare Live and Version URLs carry",
|
|
367
379
|
" no authority for a private Space; create a scoped Link when a recipient needs access.",
|
|
368
380
|
" If the receipt's immutable URL is null, poll the",
|
|
369
|
-
" operation and version links with the
|
|
370
|
-
"- Never print access tokens,
|
|
381
|
+
" operation and version links with the space key as bearer auth until the version is ready.",
|
|
382
|
+
"- Never print access tokens, space keys, auth files, or `.spacefast/state.json`.",
|
|
371
383
|
"",
|
|
372
384
|
"## After the user claims",
|
|
373
385
|
"",
|
|
@@ -452,14 +464,26 @@ export function agentSetupMarkdown() {
|
|
|
452
464
|
"endpoint. Fetch the full contract when a publish goes beyond the happy path:",
|
|
453
465
|
`${agentSetupTextUrl}.`,
|
|
454
466
|
"",
|
|
455
|
-
"The JSON receipt is the result
|
|
456
|
-
"",
|
|
457
|
-
"
|
|
458
|
-
"
|
|
459
|
-
"- `data.claim.url`
|
|
460
|
-
"
|
|
461
|
-
"
|
|
462
|
-
" never
|
|
467
|
+
"The JSON receipt is the result. **An anonymous space is private.** Until someone",
|
|
468
|
+
"claims it, the only way in is the space key, so treat every link below as a",
|
|
469
|
+
"credential unless this page says otherwise:",
|
|
470
|
+
"",
|
|
471
|
+
"- `data.claim.url` — the door. Open this yourself to check the page, and give it to",
|
|
472
|
+
" the user, because it is the one link that renders the site. It is `/__/<key>`: the",
|
|
473
|
+
" key is *in the URL*, so anyone who has it can also publish over this space and claim",
|
|
474
|
+
" it. Hand it to the person you are working for, never to a wider audience, and keep",
|
|
475
|
+
" it out of commits, logs, and anything public.",
|
|
476
|
+
"- `data.claim.key` — the same secret, bare. Use it as the bearer to update this",
|
|
477
|
+
" `spaceId`. Never print it on its own; the user has it already inside `data.claim.url`.",
|
|
478
|
+
"- `data.claim.claimUrl` and `data.claim.expiresAt` — an anonymous space expires unless",
|
|
479
|
+
" claimed. Always show the claim link and its deadline: claiming is what makes the",
|
|
480
|
+
" space permanent and lets the owner share it with people who should not get the key.",
|
|
481
|
+
"- `data.space.liveUrl` — the space's permanent address, and what it will serve from",
|
|
482
|
+
" once claimed. Do not lead with it and do not hand it to anyone yet: while the space",
|
|
483
|
+
" is anonymous this URL answers 403 for every visitor who has not been through the",
|
|
484
|
+
" door above.",
|
|
485
|
+
"- `data.version.immutableUrl` — the permanent address of this exact version. Same",
|
|
486
|
+
" rule: it carries no authority of its own while the space is anonymous.",
|
|
463
487
|
"",
|
|
464
488
|
"## If you can't act on this page",
|
|
465
489
|
"",
|
|
@@ -586,8 +610,11 @@ export function agentSetupMarkdown() {
|
|
|
586
610
|
"",
|
|
587
611
|
"## After publishing",
|
|
588
612
|
"",
|
|
589
|
-
"-
|
|
590
|
-
" its expiry.",
|
|
613
|
+
"- Lead with `data.claim.url` — it is the only link that renders an anonymous space —",
|
|
614
|
+
" then the claim link with its expiry. Mention the live and immutable URLs as the",
|
|
615
|
+
" addresses the space will serve from once it is claimed, not as links to open now.",
|
|
616
|
+
"- Say plainly that the door link carries the key, so it is for the user and not for",
|
|
617
|
+
" a public post. Claiming is what turns the space into something shareable.",
|
|
591
618
|
"- Persist the space id and credential so the next publish updates the same space instead",
|
|
592
619
|
" of creating a new one. The CLI and On-Device MCP do this automatically in `.spacefast/`.",
|
|
593
620
|
" Hosted MCP is stateless between publishes: retain the receipt's `spaceId` in conversation",
|