@spacefast/common 0.0.24 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/brand-assets/LICENSE-Haskoy.txt +93 -0
- package/brand-assets/haskoy-latin-variable.woff2 +0 -0
- package/brand-assets/spacefast-favicon.svg +4 -4
- package/brand-assets/spacefast-sf-full-bleed.svg +3 -3
- package/brand-assets/spacefast-wordmark.svg +2 -4
- package/dist/agents/connect-targets.d.ts +6 -146
- package/dist/agents/connect-targets.js +63 -295
- package/dist/agents/private-key-oauth.d.ts +31 -2
- package/dist/agents/private-key-oauth.js +166 -92
- package/dist/agents/registry/core.d.ts +292 -0
- package/dist/agents/registry/core.js +1388 -0
- package/dist/agents/registry/deeplink-probes.d.ts +23 -0
- package/dist/agents/registry/deeplink-probes.js +18 -0
- package/dist/agents/registry/node.d.ts +56 -0
- package/dist/agents/registry/node.js +339 -0
- package/dist/brand-assets-build.js +3 -6
- package/dist/brand-assets.d.ts +5 -15
- package/dist/brand-assets.js +12 -14
- package/dist/brand.d.ts +17 -0
- package/dist/brand.js +24 -4
- package/dist/config/domains.d.ts +79 -12
- package/dist/config/domains.js +128 -29
- package/dist/config/index.js +1 -2
- package/dist/contracts/abuse.js +9 -24
- package/dist/contracts/access-profiles.d.ts +86 -0
- package/dist/contracts/access-profiles.js +89 -0
- package/dist/contracts/access.d.ts +24 -30
- package/dist/contracts/access.js +33 -64
- package/dist/contracts/activity.d.ts +18 -1
- package/dist/contracts/activity.js +72 -26
- package/dist/contracts/analytics.js +8 -10
- package/dist/contracts/api-keys.d.ts +77 -52
- package/dist/contracts/api-keys.js +44 -24
- package/dist/contracts/application-journal.d.ts +216 -0
- package/dist/contracts/application-journal.js +167 -0
- package/dist/contracts/auth.d.ts +90 -17
- package/dist/contracts/auth.js +86 -17
- package/dist/contracts/beta.d.ts +9 -4
- package/dist/contracts/beta.js +20 -16
- package/dist/contracts/billing.d.ts +2 -1
- package/dist/contracts/billing.js +37 -11
- package/dist/contracts/bootstrap.d.ts +791 -0
- package/dist/contracts/bootstrap.js +51 -0
- package/dist/contracts/builds.d.ts +209 -18
- package/dist/contracts/builds.js +93 -27
- package/dist/contracts/channels.d.ts +43 -0
- package/dist/contracts/channels.js +25 -9
- package/dist/contracts/cli.d.ts +7 -0
- package/dist/contracts/cli.js +6 -0
- package/dist/contracts/collab-public.d.ts +1 -1
- package/dist/contracts/collab-public.js +5 -8
- package/dist/contracts/comments.d.ts +44 -34
- package/dist/contracts/comments.js +107 -77
- package/dist/contracts/commerce.d.ts +165 -0
- package/dist/contracts/commerce.js +146 -0
- package/dist/contracts/common.d.ts +3 -3
- package/dist/contracts/common.js +20 -31
- package/dist/contracts/content-contract-verification.d.ts +521 -0
- package/dist/contracts/content-contract-verification.js +320 -0
- package/dist/contracts/content-platform-fixture.d.ts +1278 -0
- package/dist/contracts/content-platform-fixture.js +44 -0
- package/dist/contracts/content-program.d.ts +994 -0
- package/dist/contracts/content-program.js +658 -0
- package/dist/contracts/content-sync.d.ts +290 -0
- package/dist/contracts/content-sync.js +167 -0
- package/dist/contracts/content.d.ts +432 -0
- package/dist/contracts/content.js +303 -0
- package/dist/contracts/continuation.d.ts +17 -1
- package/dist/contracts/continuation.js +23 -23
- package/dist/contracts/countries.js +3 -4
- package/dist/contracts/crons.d.ts +52 -0
- package/dist/contracts/crons.js +60 -0
- package/dist/contracts/dashboard-prefs.d.ts +56 -0
- package/dist/contracts/dashboard-prefs.js +51 -0
- package/dist/contracts/device-auth.d.ts +234 -37
- package/dist/contracts/device-auth.js +267 -53
- package/dist/contracts/docs.d.ts +1 -5
- package/dist/contracts/docs.js +7 -15
- package/dist/contracts/domains.d.ts +501 -93
- package/dist/contracts/domains.js +193 -55
- package/dist/contracts/email-preferences.d.ts +3 -3
- package/dist/contracts/email-preferences.js +6 -7
- package/dist/contracts/enums.d.ts +27 -26
- package/dist/contracts/enums.js +68 -61
- package/dist/contracts/error-code-meta.d.ts +283 -7
- package/dist/contracts/error-code-meta.js +97 -5
- package/dist/contracts/error-codes.d.ts +5 -5
- package/dist/contracts/error-codes.js +82 -17
- package/dist/contracts/events.d.ts +100 -20
- package/dist/contracts/events.js +107 -20
- package/dist/contracts/execution.d.ts +48 -64
- package/dist/contracts/execution.js +52 -62
- package/dist/contracts/feature-lifecycle.d.ts +73 -27
- package/dist/contracts/feature-lifecycle.js +86 -26
- package/dist/contracts/features.d.ts +68 -19
- package/dist/contracts/features.js +42 -41
- package/dist/contracts/fixtures/content-platform-v1.json +1108 -0
- package/dist/contracts/frame-session.d.ts +192 -0
- package/dist/contracts/frame-session.js +164 -0
- package/dist/contracts/functions.d.ts +338 -164
- package/dist/contracts/functions.js +367 -198
- package/dist/contracts/git.d.ts +27 -33
- package/dist/contracts/git.js +10 -11
- package/dist/contracts/grant-copy.d.ts +43 -0
- package/dist/contracts/grant-copy.js +44 -0
- package/dist/contracts/grants.d.ts +62 -54
- package/dist/contracts/grants.js +124 -23
- package/dist/contracts/ids.d.ts +4 -1
- package/dist/contracts/ids.js +6 -3
- package/dist/contracts/internal.d.ts +8 -18
- package/dist/contracts/internal.js +13 -14
- package/dist/contracts/mcp.d.ts +28 -0
- package/dist/contracts/mcp.js +28 -0
- package/dist/contracts/me.d.ts +6 -1
- package/dist/contracts/me.js +12 -6
- package/dist/contracts/notifications.d.ts +27 -29
- package/dist/contracts/notifications.js +9 -4
- package/dist/contracts/oauth-resources.d.ts +112 -10
- package/dist/contracts/oauth-resources.js +116 -11
- package/dist/contracts/oauth-scope-actions.d.ts +90 -0
- package/dist/contracts/oauth-scope-actions.js +126 -0
- package/dist/contracts/operations.d.ts +27 -4
- package/dist/contracts/operations.js +12 -6
- package/dist/contracts/pages.d.ts +31 -7
- package/dist/contracts/pages.js +22 -5
- package/dist/contracts/partner-notifications.d.ts +3 -0
- package/dist/contracts/partner-notifications.js +65 -0
- package/dist/contracts/{platform.d.ts → partner.d.ts} +29 -42
- package/dist/contracts/{platform.js → partner.js} +27 -40
- package/dist/contracts/plan-policy.js +1 -1
- package/dist/contracts/principal-assertion.d.ts +43 -0
- package/dist/contracts/principal-assertion.js +67 -0
- package/dist/contracts/principals.d.ts +73 -0
- package/dist/contracts/principals.js +59 -0
- package/dist/contracts/privacy.d.ts +11 -0
- package/dist/contracts/privacy.js +17 -0
- package/dist/contracts/problem-document.js +3 -4
- package/dist/contracts/publish-archive.d.ts +269 -161
- package/dist/contracts/publish-archive.js +11 -1
- package/dist/contracts/push-new.d.ts +8 -8
- package/dist/contracts/quotas.js +5 -5
- package/dist/contracts/realtime.d.ts +52 -0
- package/dist/contracts/realtime.js +54 -0
- package/dist/contracts/repository-connections.d.ts +44 -31
- package/dist/contracts/repository-connections.js +17 -5
- package/dist/contracts/resources.d.ts +105 -14
- package/dist/contracts/resources.js +150 -26
- package/dist/contracts/route-inventory.d.ts +358 -0
- package/dist/contracts/route-inventory.js +141 -0
- package/dist/contracts/runtime-api.d.ts +206 -82
- package/dist/contracts/runtime-api.js +131 -70
- package/dist/contracts/runtime-app.d.ts +13 -23
- package/dist/contracts/runtime-app.js +39 -24
- package/dist/contracts/runtime-components.d.ts +143 -0
- package/dist/contracts/runtime-components.js +199 -0
- package/dist/contracts/runtime-db.d.ts +49 -23
- package/dist/contracts/runtime-db.js +87 -22
- package/dist/contracts/runtime-purge.d.ts +8 -0
- package/dist/contracts/runtime-purge.js +5 -0
- package/dist/contracts/runtime-services.d.ts +43 -75
- package/dist/contracts/runtime-services.js +29 -49
- package/dist/contracts/runtime-storage.d.ts +15 -14
- package/dist/contracts/runtime-storage.js +30 -14
- package/dist/contracts/sf-config-v1.d.ts +48 -14
- package/dist/contracts/slugs.js +13 -4
- package/dist/contracts/space-config.d.ts +283 -19
- package/dist/contracts/space-config.js +430 -80
- package/dist/contracts/spaces.d.ts +376 -290
- package/dist/contracts/spaces.js +200 -135
- package/dist/contracts/superadmin-activity.d.ts +3 -43
- package/dist/contracts/superadmin-activity.js +6 -30
- package/dist/contracts/superadmin-emails.d.ts +1 -0
- package/dist/contracts/superadmin-emails.js +17 -1
- package/dist/contracts/superadmin-runtime.d.ts +199 -0
- package/dist/contracts/superadmin-runtime.js +141 -0
- package/dist/contracts/superadmin-search.d.ts +22 -2
- package/dist/contracts/superadmin-search.js +14 -6
- package/dist/contracts/superadmin-spaces.d.ts +438 -189
- package/dist/contracts/superadmin-spaces.js +131 -25
- package/dist/contracts/superadmin-teams.d.ts +5 -2
- package/dist/contracts/superadmin-teams.js +1 -2
- package/dist/contracts/superadmin-tenants.d.ts +627 -12
- package/dist/contracts/superadmin-tenants.js +32 -13
- package/dist/contracts/superadmin.d.ts +232 -37
- package/dist/contracts/superadmin.js +106 -22
- package/dist/contracts/tags.d.ts +85 -58
- package/dist/contracts/tags.js +47 -37
- package/dist/contracts/teams.d.ts +86 -26
- package/dist/contracts/teams.js +90 -31
- package/dist/contracts/tenants.d.ts +135 -0
- package/dist/contracts/tenants.js +120 -0
- package/dist/contracts/test-triggers.d.ts +70 -0
- package/dist/contracts/test-triggers.js +116 -0
- package/dist/contracts/theme-json.js +3 -8
- package/dist/contracts/transfers.d.ts +1 -0
- package/dist/contracts/transfers.js +3 -2
- package/dist/contracts/usage.d.ts +209 -0
- package/dist/contracts/usage.js +124 -12
- package/dist/contracts/variables.d.ts +2 -2
- package/dist/contracts/variables.js +9 -13
- package/dist/contracts/webhooks.d.ts +13 -5
- package/dist/contracts/webhooks.js +3 -1
- package/dist/contracts/zero.d.ts +72 -81
- package/dist/contracts/zero.js +80 -78
- package/dist/dashboard-paths/index.d.ts +24 -21
- package/dist/dashboard-paths/index.js +35 -47
- package/dist/dashboard-paths/route-reservations.generated.d.ts +2 -0
- package/dist/dashboard-paths/route-reservations.generated.js +36 -0
- package/dist/docs/agent-arrival.d.ts +92 -0
- package/dist/docs/agent-arrival.js +227 -0
- package/dist/docs/agent-output-policy.d.ts +6 -0
- package/dist/docs/agent-output-policy.js +22 -0
- package/dist/docs/agent-prose.d.ts +12 -18
- package/dist/docs/agent-prose.js +73 -51
- package/dist/docs/agent-setup.d.ts +72 -41
- package/dist/docs/agent-setup.js +339 -436
- package/dist/docs/catalog.d.ts +153 -95
- package/dist/docs/catalog.js +163 -48
- package/dist/docs/error-docs.d.ts +2098 -1
- package/dist/docs/error-docs.js +421 -144
- package/dist/docs/index-build.d.ts +0 -1
- package/dist/docs/index-build.js +2 -3
- package/dist/docs/page-context-prompt.d.ts +77 -0
- package/dist/docs/page-context-prompt.js +131 -0
- package/dist/docs/reference-urls.d.ts +46 -0
- package/dist/docs/reference-urls.js +79 -0
- package/dist/docs/search.d.ts +6 -8
- package/dist/docs/search.js +10 -13
- package/dist/docs/skill-distribution.d.ts +4 -1
- package/dist/docs/skill-distribution.js +10 -5
- package/dist/docs/skills.d.ts +120 -120
- package/dist/docs/skills.js +121 -73
- package/dist/docs/start-prompts.d.ts +45 -0
- package/dist/docs/start-prompts.js +62 -0
- package/dist/domain-exploration.js +9 -13
- package/dist/og-template.d.ts +56 -0
- package/dist/og-template.js +56 -0
- package/dist/slug-policy/index.js +5 -11
- package/dist/test-helpers/fetch-stub.js +1 -0
- package/dist/utils/browser-credential.d.ts +13 -6
- package/dist/utils/browser-credential.js +14 -7
- package/dist/utils/build-settings.d.ts +21 -1
- package/dist/utils/build-settings.js +133 -15
- package/dist/utils/canonical-json.d.ts +3 -5
- package/dist/utils/canonical-json.js +4 -5
- package/dist/utils/cast-transport.d.ts +5 -17
- package/dist/utils/cast-transport.js +6 -19
- package/dist/utils/claim-token.js +1 -2
- package/dist/utils/comment-avatars.js +4 -6
- package/dist/utils/concurrency.js +3 -3
- package/dist/utils/content-type.d.ts +7 -6
- package/dist/utils/content-type.js +73 -51
- package/dist/utils/credential-policy.d.ts +50 -6
- package/dist/utils/credential-policy.js +126 -59
- package/dist/utils/dns-instructions.js +9 -9
- package/dist/utils/egress-policy.fixtures.json +1 -1
- package/dist/utils/error-code.js +1 -0
- package/dist/utils/generate-space-name.d.ts +3 -8
- package/dist/utils/generate-space-name.js +3 -8
- package/dist/utils/grant-decision.fixtures.json +2 -2
- package/dist/utils/grants.d.ts +0 -1
- package/dist/utils/grants.js +7 -8
- package/dist/utils/gravatar.d.ts +2 -4
- package/dist/utils/gravatar.js +6 -12
- package/dist/utils/id-hints.js +14 -23
- package/dist/utils/idempotency.d.ts +3 -3
- package/dist/utils/idempotency.js +6 -7
- package/dist/utils/local-space-state.d.ts +23 -26
- package/dist/utils/local-space-state.js +46 -46
- package/dist/utils/oauth-signed-query.d.ts +3 -5
- package/dist/utils/oauth-signed-query.js +6 -9
- package/dist/utils/one-shot-replay.d.ts +7 -6
- package/dist/utils/one-shot-replay.js +3 -5
- package/dist/utils/page-colors.d.ts +15 -19
- package/dist/utils/page-colors.js +23 -45
- package/dist/utils/page-fonts.d.ts +1 -1
- package/dist/utils/page-fonts.js +8 -20
- package/dist/utils/page-preview.d.ts +0 -1
- package/dist/utils/page-preview.js +0 -1
- package/dist/utils/pages.d.ts +33 -3
- package/dist/utils/pages.js +83 -30
- package/dist/utils/privacy-regions.d.ts +19 -0
- package/dist/utils/privacy-regions.js +91 -0
- package/dist/utils/publish-form-data.d.ts +4 -5
- package/dist/utils/publish-form-data.js +2 -3
- package/dist/utils/publish-policy.d.ts +11 -17
- package/dist/utils/publish-policy.fixtures.json +11 -5
- package/dist/utils/publish-policy.js +65 -110
- package/dist/utils/query-client.d.ts +2 -0
- package/dist/utils/query-client.js +10 -1
- package/dist/utils/runtime-paths.d.ts +4 -4
- package/dist/utils/runtime-paths.js +6 -8
- package/dist/utils/runtime-upload.d.ts +9 -0
- package/dist/utils/runtime-upload.js +12 -0
- package/dist/utils/secure-local-file.d.ts +3 -3
- package/dist/utils/secure-local-file.js +23 -40
- package/dist/utils/space-config.d.ts +12 -15
- package/dist/utils/space-config.js +13 -16
- package/dist/utils/static-runtime-policy.d.ts +10 -7
- package/dist/utils/static-runtime-policy.fixtures.json +38 -41
- package/dist/utils/static-runtime-policy.generated.d.ts +16 -0
- package/dist/utils/static-runtime-policy.generated.js +70 -0
- package/dist/utils/static-runtime-policy.js +31 -132
- package/dist/utils/storage-policy.js +1 -2
- package/dist/utils/upload-session.d.ts +4 -1
- package/dist/utils/upload-session.js +41 -19
- package/dist/utils/version-path.js +11 -19
- package/dist/utils/wpcom-auth-redirects.d.ts +5 -14
- package/dist/utils/wpcom-auth-redirects.js +1 -4
- package/dist/vocabulary.d.ts +13 -13
- package/dist/vocabulary.js +52 -27
- package/package.json +15 -18
- package/dist/agents/client-registry.d.ts +0 -22
- package/dist/agents/client-registry.js +0 -41
- package/dist/docs/agent-handoff-document.d.ts +0 -15
- package/dist/docs/agent-handoff-document.js +0 -180
- package/dist/docs/agent-solutions.d.ts +0 -110
- package/dist/docs/agent-solutions.js +0 -183
- package/dist/utils/query-keys.d.ts +0 -84
- package/dist/utils/query-keys.js +0 -108
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ANONYMOUS_AGENT_HANDOFF_TTL_MS, AnonymousAgentHandoffSchema, AnonymousAgentHandoffStatusSchema, } from "../contracts/device-auth.js";
|
|
3
|
+
import { buildAnonymousAgentHandoff, parseAgentHandoffUrl } from "./agent-setup.js";
|
|
4
|
+
/*
|
|
5
|
+
* Agent arrival, signed out: the whole flow behind "your agent picked the
|
|
6
|
+
* prompt up", framework-free so the dashboard and the marketing site run one
|
|
7
|
+
* implementation instead of two that drift.
|
|
8
|
+
*
|
|
9
|
+
* The shape of the flow: mint an anonymous handoff, compose the copied prompt
|
|
10
|
+
* around its one-use URL, then poll that handoff's own fetch state until the
|
|
11
|
+
* counter leaves zero. The handoff carries no team, no Space, and nothing to
|
|
12
|
+
* redeem — its only job is to make the fetch observable — so the visitor learns
|
|
13
|
+
* their agent arrived without signing up first.
|
|
14
|
+
*
|
|
15
|
+
* Everything here is a plain function: no React, no query client, no clock but
|
|
16
|
+
* the one the caller passes. Cadence and reuse are policy, so they live here
|
|
17
|
+
* with the numbers rather than in whichever UI happens to render the wait.
|
|
18
|
+
*/
|
|
19
|
+
const ANONYMOUS_ARRIVAL_PATH = "/v1/auth/agent-handoffs/anonymous";
|
|
20
|
+
export const AGENT_HANDOFF_REQUEST_FAILED_MESSAGE = "Request failed.";
|
|
21
|
+
export const AGENT_HANDOFF_INVALID_RESPONSE_MESSAGE = "Invalid agent handoff response.";
|
|
22
|
+
/**
|
|
23
|
+
* The messages that say nothing a person can act on. A UI showing an error
|
|
24
|
+
* detail line suppresses these, so it never renders "failed: Request failed."
|
|
25
|
+
*/
|
|
26
|
+
export const AGENT_HANDOFF_GENERIC_MESSAGES = new Set([
|
|
27
|
+
AGENT_HANDOFF_REQUEST_FAILED_MESSAGE,
|
|
28
|
+
AGENT_HANDOFF_INVALID_RESPONSE_MESSAGE,
|
|
29
|
+
]);
|
|
30
|
+
export class AgentHandoffRequestError extends Error {
|
|
31
|
+
code;
|
|
32
|
+
constructor(message, code = null) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = "AgentHandoffRequestError";
|
|
35
|
+
this.code = code;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The parts of an RFC 9457 problem document that may reach a person. Anything
|
|
40
|
+
* else the server sent is dropped here rather than carried on an error: a
|
|
41
|
+
* malformed field costs the reader a detail line, never a leaked credential.
|
|
42
|
+
*/
|
|
43
|
+
const AgentHandoffProblemSchema = z.object({
|
|
44
|
+
code: z.string().optional().catch(undefined),
|
|
45
|
+
detail: z.string().optional().catch(undefined),
|
|
46
|
+
});
|
|
47
|
+
// Codes are lowercase snake tokens; anything else (including a secret-shaped
|
|
48
|
+
// blob smuggled into the field) is dropped rather than carried on the error.
|
|
49
|
+
const SAFE_ERROR_CODE = /^[a-z][a-z0-9_.-]{0,63}$/;
|
|
50
|
+
function safeProblemDetail(detail) {
|
|
51
|
+
if (detail === undefined || !detail.trim())
|
|
52
|
+
return AGENT_HANDOFF_REQUEST_FAILED_MESSAGE;
|
|
53
|
+
// Error copy must never echo a handoff URL or a secret-shaped opaque credential.
|
|
54
|
+
if (/https?:\/\/|[A-Za-z0-9_-]{16,}/i.test(detail))
|
|
55
|
+
return AGENT_HANDOFF_REQUEST_FAILED_MESSAGE;
|
|
56
|
+
return detail;
|
|
57
|
+
}
|
|
58
|
+
/** The failure a non-2xx answer becomes, sanitized at the boundary that read it. */
|
|
59
|
+
async function agentHandoffErrorFromResponse(response) {
|
|
60
|
+
const parsed = AgentHandoffProblemSchema.safeParse(await response.json().catch(() => null));
|
|
61
|
+
const problem = parsed.success ? parsed.data : {};
|
|
62
|
+
const code = problem.code !== undefined && SAFE_ERROR_CODE.test(problem.code) ? problem.code : null;
|
|
63
|
+
return new AgentHandoffRequestError(safeProblemDetail(problem.detail), code);
|
|
64
|
+
}
|
|
65
|
+
function invalidResponse() {
|
|
66
|
+
return new AgentHandoffRequestError(AGENT_HANDOFF_INVALID_RESPONSE_MESSAGE);
|
|
67
|
+
}
|
|
68
|
+
/*
|
|
69
|
+
* The success envelope, parsed as one shape. `{ data }` is the API's contract
|
|
70
|
+
* and the payload schema is the handoff's, so a response that is neither never
|
|
71
|
+
* reaches a caller as a half-parsed object.
|
|
72
|
+
*/
|
|
73
|
+
const MintEnvelopeSchema = z.object({ data: AnonymousAgentHandoffSchema });
|
|
74
|
+
const StatusEnvelopeSchema = z.object({ data: AnonymousAgentHandoffStatusSchema });
|
|
75
|
+
function endpoint(config, path) {
|
|
76
|
+
return `${config.apiOrigin.replace(/\/+$/, "")}${path}`;
|
|
77
|
+
}
|
|
78
|
+
function transport(config) {
|
|
79
|
+
return config.fetch ?? globalThis.fetch;
|
|
80
|
+
}
|
|
81
|
+
function clock(config) {
|
|
82
|
+
return config.now ? config.now() : Date.now();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Mints the signed-out handoff a copied prompt carries.
|
|
86
|
+
*
|
|
87
|
+
* The anonymous surface is anonymous by contract, so this deliberately sends no
|
|
88
|
+
* credentials: it works signed out, and a mint that carried a session would
|
|
89
|
+
* answer differently for gated accounts.
|
|
90
|
+
*/
|
|
91
|
+
export async function mintAnonymousArrival(config) {
|
|
92
|
+
const response = await transport(config)(endpoint(config, ANONYMOUS_ARRIVAL_PATH), {
|
|
93
|
+
method: "POST",
|
|
94
|
+
credentials: "omit",
|
|
95
|
+
headers: { accept: "application/json" },
|
|
96
|
+
});
|
|
97
|
+
if (!response.ok)
|
|
98
|
+
throw await agentHandoffErrorFromResponse(response);
|
|
99
|
+
const parsed = MintEnvelopeSchema.safeParse(await response.json().catch(() => null));
|
|
100
|
+
if (!parsed.success)
|
|
101
|
+
throw invalidResponse();
|
|
102
|
+
const data = parsed.data.data;
|
|
103
|
+
let handoffUrl = null;
|
|
104
|
+
let secret = null;
|
|
105
|
+
for (const origin of config.allowedOrigins ?? [config.apiOrigin]) {
|
|
106
|
+
try {
|
|
107
|
+
({ handoffUrl, secret } = parseAgentHandoffUrl(data.url, origin));
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Try the next explicitly allowed origin.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Sanitization boundary: the ledger id is rendered and polled on, so it must
|
|
115
|
+
// be provably free of the bearer whatever the server fed us.
|
|
116
|
+
if (handoffUrl === null || secret === null || data.id.includes(secret)) {
|
|
117
|
+
throw invalidResponse();
|
|
118
|
+
}
|
|
119
|
+
const url = handoffUrl;
|
|
120
|
+
const bearer = secret;
|
|
121
|
+
return {
|
|
122
|
+
id: data.id,
|
|
123
|
+
expiresAt: Date.parse(data.expiresAt),
|
|
124
|
+
promptFor: (context) => {
|
|
125
|
+
// The caller's text is authored before the mint, so it can only contain
|
|
126
|
+
// the bearer if something upstream is already broken.
|
|
127
|
+
if (context.includes(bearer))
|
|
128
|
+
throw invalidResponse();
|
|
129
|
+
return buildAnonymousAgentHandoff({
|
|
130
|
+
expectedOrigin: new URL(url).origin,
|
|
131
|
+
url,
|
|
132
|
+
context,
|
|
133
|
+
}).prompt;
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** Reads one anonymous handoff's fetch state by its non-secret ledger id. */
|
|
138
|
+
export async function readAnonymousArrivalStatus(id, config) {
|
|
139
|
+
const path = `${ANONYMOUS_ARRIVAL_PATH}/${encodeURIComponent(id)}`;
|
|
140
|
+
const response = await transport(config)(endpoint(config, path), {
|
|
141
|
+
method: "GET",
|
|
142
|
+
credentials: "omit",
|
|
143
|
+
headers: { accept: "application/json" },
|
|
144
|
+
});
|
|
145
|
+
if (!response.ok)
|
|
146
|
+
throw await agentHandoffErrorFromResponse(response);
|
|
147
|
+
const parsed = StatusEnvelopeSchema.safeParse(await response.json().catch(() => null));
|
|
148
|
+
if (!parsed.success)
|
|
149
|
+
throw invalidResponse();
|
|
150
|
+
return parsed.data.data;
|
|
151
|
+
}
|
|
152
|
+
/*
|
|
153
|
+
* Cadence, owned in one place with its numbers.
|
|
154
|
+
*
|
|
155
|
+
* The answer being waited on is one counter that leaves zero exactly once, and
|
|
156
|
+
* it changes from outside this browser, so polling is the whole mechanism.
|
|
157
|
+
* Eager while an agent is plausibly still landing, relaxed for the rest of the
|
|
158
|
+
* handoff's life, stopped once nothing can change.
|
|
159
|
+
*/
|
|
160
|
+
export const AGENT_ARRIVAL_EAGER_POLL_MS = 2_000;
|
|
161
|
+
export const AGENT_ARRIVAL_EAGER_WINDOW_MS = 2 * 60_000;
|
|
162
|
+
export const AGENT_ARRIVAL_RELAXED_POLL_MS = 10_000;
|
|
163
|
+
/**
|
|
164
|
+
* The handoff's own TTL is the clock: with no `since` to carry, "how much
|
|
165
|
+
* runway is left" says how long ago it was minted.
|
|
166
|
+
*/
|
|
167
|
+
export const AGENT_ARRIVAL_RELAXED_AFTER_MS = ANONYMOUS_AGENT_HANDOFF_TTL_MS - AGENT_ARRIVAL_EAGER_WINDOW_MS;
|
|
168
|
+
/**
|
|
169
|
+
* One live handoff per visit is reused while it still has this much runway.
|
|
170
|
+
* Every copy gesture minting its own would spend rows against the caller's
|
|
171
|
+
* budget for a signal that only has to flip once.
|
|
172
|
+
*/
|
|
173
|
+
export const AGENT_ARRIVAL_REFRESH_RUNWAY_MS = 2 * 60_000;
|
|
174
|
+
/** How long to wait before the next status read, or `false` to stop polling. */
|
|
175
|
+
export function anonymousArrivalPollDelayMs(status, now = Date.now()) {
|
|
176
|
+
if (status === undefined)
|
|
177
|
+
return AGENT_ARRIVAL_EAGER_POLL_MS;
|
|
178
|
+
if (hasAgentArrived(status))
|
|
179
|
+
return false;
|
|
180
|
+
const remaining = Date.parse(status.expiresAt) - now;
|
|
181
|
+
if (remaining <= 0)
|
|
182
|
+
return false;
|
|
183
|
+
return remaining > AGENT_ARRIVAL_RELAXED_AFTER_MS
|
|
184
|
+
? AGENT_ARRIVAL_EAGER_POLL_MS
|
|
185
|
+
: AGENT_ARRIVAL_RELAXED_POLL_MS;
|
|
186
|
+
}
|
|
187
|
+
/** The arrival itself: one fetch of the private URL is the whole signal. */
|
|
188
|
+
export function hasAgentArrived(status) {
|
|
189
|
+
return (status?.fetchCount ?? 0) > 0;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* One visit's arrival flow. Hold one of these for as long as the visitor is on
|
|
193
|
+
* the surface: the reuse discipline lives in the instance, so re-copies keep
|
|
194
|
+
* the same handoff instead of piling fresh rows behind one signal.
|
|
195
|
+
*/
|
|
196
|
+
export function createAnonymousArrival(config) {
|
|
197
|
+
let inflight = null;
|
|
198
|
+
const fresh = () => {
|
|
199
|
+
const pending = mintAnonymousArrival(config);
|
|
200
|
+
inflight = pending;
|
|
201
|
+
// A mint that failed is not the visit's handoff; the next copy mints anew.
|
|
202
|
+
void pending.catch(() => {
|
|
203
|
+
if (inflight === pending)
|
|
204
|
+
inflight = null;
|
|
205
|
+
});
|
|
206
|
+
return pending;
|
|
207
|
+
};
|
|
208
|
+
const ensureMinted = () => {
|
|
209
|
+
const cached = inflight;
|
|
210
|
+
if (!cached)
|
|
211
|
+
return fresh();
|
|
212
|
+
return cached.then((minted) => minted.expiresAt - clock(config) > AGENT_ARRIVAL_REFRESH_RUNWAY_MS ? minted : fresh());
|
|
213
|
+
};
|
|
214
|
+
return {
|
|
215
|
+
ensureMinted,
|
|
216
|
+
promptFor: (context) => ensureMinted().then((minted) => ({
|
|
217
|
+
id: minted.id,
|
|
218
|
+
// Composed from THIS copy's context, never the one the handoff was
|
|
219
|
+
// first minted for: one handoff spans the visit, the task does not.
|
|
220
|
+
prompt: minted.promptFor(context),
|
|
221
|
+
expiresAt: minted.expiresAt,
|
|
222
|
+
})),
|
|
223
|
+
readStatus: (id) => readAnonymousArrivalStatus(id, config),
|
|
224
|
+
nextPollDelayMs: (status) => anonymousArrivalPollDelayMs(status, clock(config)),
|
|
225
|
+
hasArrived: hasAgentArrived,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const COMMERCIAL_UPSELL_PATTERNS = [
|
|
2
|
+
/\bpricing\b/i,
|
|
3
|
+
/\bpaid plans?\b/i,
|
|
4
|
+
/\bupgrade (?:your|the|this) (?:account|plan|subscription)\b/i,
|
|
5
|
+
/\bupgrade to (?:a )?(?:paid|premium|pro)\b/i,
|
|
6
|
+
/\bsubscribe now\b/i,
|
|
7
|
+
/\bstart (?:a )?(?:free )?trial\b/i,
|
|
8
|
+
/\bbuy now\b/i,
|
|
9
|
+
/\bcontact sales\b/i,
|
|
10
|
+
/\bcheckout\b/i,
|
|
11
|
+
];
|
|
12
|
+
/** Reject commercial plan promotion in text that an agent can receive. */
|
|
13
|
+
export function assertNoCommercialUpsell(outputs) {
|
|
14
|
+
for (const output of outputs) {
|
|
15
|
+
for (const pattern of COMMERCIAL_UPSELL_PATTERNS) {
|
|
16
|
+
const match = pattern.exec(output.text);
|
|
17
|
+
if (match) {
|
|
18
|
+
throw new Error(`${output.source} contains commercial upsell text: ${match[0]}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
export declare const ARCHIVE_SECRET_GLOBS: readonly [".env*", ".npmrc", ".netrc", "*/.git/*", "*/.spacefast/*", "*/.ssh/*", "*/.aws/*", "*/.kube/*", "*/.docker/*", "credentials.json", "*.pem", "*.key", "*.p12", "*.pfx", "*.crt", "*id_rsa*"];
|
|
2
|
-
/**
|
|
3
|
-
* The credential rules every surface must repeat. Treat tokens as secrets, keep
|
|
4
|
-
* them out of logs and transcripts, and never echo them back.
|
|
5
|
-
*/
|
|
2
|
+
/** The credential rules every surface must repeat. */
|
|
6
3
|
export declare const SECRET_RULES_PROSE: string;
|
|
7
4
|
export declare const POSITIONING_PROSE: string;
|
|
8
5
|
export declare const EXPLAIN_TO_USER_PROSE: string;
|
|
6
|
+
export declare const FEEDBACK_PROSE = "With user approval, send feedback with `sf feedback`, MCP `execute` (search `send feedback`), or `POST /v1/feedback`. Never include credentials or private links.";
|
|
9
7
|
export declare const FAILURE_CONDUCT_PROSE: string;
|
|
10
8
|
export declare const LANE_PICKER_PROSE: string;
|
|
11
9
|
export declare const HOW_TO_RESEARCH_PROSE: string;
|
|
12
|
-
/** Curl/API skill lane: docs URLs only
|
|
10
|
+
/** Curl/API skill lane: docs URLs only, no MCP or CLI tools required. */
|
|
13
11
|
export declare const HOW_TO_RESEARCH_API_PROSE: string;
|
|
14
12
|
/** MCP plugin skill lane: prefer skills for workflows and execute for the OpenAPI catalog. */
|
|
15
13
|
export declare const HOW_TO_RESEARCH_MCP_PROSE: string;
|
|
16
14
|
export declare const ENV_NOTES_PROSE: string;
|
|
17
15
|
export declare const ENV_NOTES_API_PROSE: string;
|
|
18
16
|
/**
|
|
19
|
-
* The CHECK verb:
|
|
20
|
-
*
|
|
21
|
-
* anonymous space instead of a new version.
|
|
17
|
+
* The CHECK verb: look for an existing space before creating one, so a returning
|
|
18
|
+
* agent publishes a new version instead of a duplicate anonymous space.
|
|
22
19
|
*/
|
|
23
20
|
export declare const CHECK_BEFORE_PUBLISH_PROSE: string;
|
|
24
21
|
export declare const CHECK_BEFORE_PUBLISH_API_PROSE: string;
|
|
@@ -37,6 +34,7 @@ export declare const SAVE_STATE_MCP_PROSE: string;
|
|
|
37
34
|
* The UPDATE verb: a new version to the SAME space. Never re-run the
|
|
38
35
|
* first-publish recipe when saved state exists.
|
|
39
36
|
*/
|
|
37
|
+
export declare const PUSH_TO_DEPLOY_PROSE: string;
|
|
40
38
|
export declare const UPDATE_SPACE_PROSE: string;
|
|
41
39
|
/**
|
|
42
40
|
* The CLAIM / CONTINUE verb: show the claim link, and after the user claims,
|
|
@@ -45,7 +43,7 @@ export declare const UPDATE_SPACE_PROSE: string;
|
|
|
45
43
|
export declare const CONTINUE_AFTER_CLAIM_PROSE: string;
|
|
46
44
|
/**
|
|
47
45
|
* The zero-install publish sequence: a replay-safe curl loop for a file, a
|
|
48
|
-
* secret-preflighted zip for a folder. This is the load-bearing rung 0
|
|
46
|
+
* secret-preflighted zip for a folder. This is the load-bearing rung 0. An
|
|
49
47
|
* agent that stops here must still succeed.
|
|
50
48
|
*/
|
|
51
49
|
export declare const PUBLISH_SEQUENCE_PROSE: string;
|
|
@@ -53,19 +51,15 @@ export declare const PUBLISH_SEQUENCE_PROSE: string;
|
|
|
53
51
|
* What the JSON publish receipt contains and how to read it. The receipt is the
|
|
54
52
|
* source of truth; never invent a separate success signal.
|
|
55
53
|
*/
|
|
56
|
-
export declare const
|
|
54
|
+
export declare const RECEIPT_CONTRACT_PROSE: string;
|
|
57
55
|
/**
|
|
58
56
|
* The large/cloud-agent path. This keeps file bytes out of MCP JSON and chat
|
|
59
57
|
* transcripts while still giving agents a way to upload arbitrary artifacts.
|
|
60
58
|
*/
|
|
61
59
|
export declare const SIGNED_UPLOAD_PROSE: string;
|
|
62
60
|
/**
|
|
63
|
-
* Named fragments for the skill-template generator
|
|
64
|
-
* `{{prose
|
|
65
|
-
* `{{prose.receiptShape}}`, `{{prose.signedUpload}}`,
|
|
66
|
-
* `{{prose.checkBeforePublish}}`, `{{prose.saveState}}`,
|
|
67
|
-
* `{{prose.updateSpace}}`, and `{{prose.continueAfterClaim}}` placeholders;
|
|
68
|
-
* generate-skill.mjs substitutes these.
|
|
61
|
+
* Named fragments for the skill-template generator: the templates carry
|
|
62
|
+
* `{{prose.<key>}}` placeholders and generate-skill.mjs substitutes these.
|
|
69
63
|
*/
|
|
70
64
|
export declare const AGENT_PROSE: {
|
|
71
65
|
readonly positioning: string;
|
|
@@ -73,7 +67,7 @@ export declare const AGENT_PROSE: {
|
|
|
73
67
|
readonly explainToUser: string;
|
|
74
68
|
readonly failureConduct: string;
|
|
75
69
|
readonly publishSequence: string;
|
|
76
|
-
readonly
|
|
70
|
+
readonly receiptContract: string;
|
|
77
71
|
readonly signedUpload: string;
|
|
78
72
|
readonly checkBeforePublish: string;
|
|
79
73
|
readonly checkBeforePublishApi: string;
|
|
@@ -83,6 +77,7 @@ export declare const AGENT_PROSE: {
|
|
|
83
77
|
readonly saveStateApi: string;
|
|
84
78
|
readonly saveStateMcp: string;
|
|
85
79
|
readonly updateSpace: string;
|
|
80
|
+
readonly pushToDeploy: string;
|
|
86
81
|
readonly continueAfterClaim: string;
|
|
87
82
|
readonly lanePicker: string;
|
|
88
83
|
readonly howToResearch: string;
|
|
@@ -91,4 +86,3 @@ export declare const AGENT_PROSE: {
|
|
|
91
86
|
readonly envNotes: string;
|
|
92
87
|
readonly envNotesApi: string;
|
|
93
88
|
};
|
|
94
|
-
export type AgentProseKey = keyof typeof AGENT_PROSE;
|
package/dist/docs/agent-prose.js
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
|
+
import { SPACEFAST_POSITIONING } from "../brand.js";
|
|
1
2
|
import { serviceOrigin } from "../config/domains.js";
|
|
2
|
-
// Single home for the agent-facing prose
|
|
3
|
-
//
|
|
3
|
+
// Single home for the agent-facing prose the three publish-teaching surfaces
|
|
4
|
+
// share:
|
|
4
5
|
//
|
|
5
6
|
// - packages/cli/skill.template.md (the SKILL.md router + copies)
|
|
6
7
|
// - packages/common/src/docs/agent-setup.ts (the /ai and /ai.txt page body)
|
|
7
|
-
// -
|
|
8
|
+
// - spacefast/www (the agent-card / discovery prose)
|
|
8
9
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// placeholders during generation, and the TS surfaces import the strings.
|
|
14
|
-
//
|
|
15
|
-
// Keep these grounded in the real API receipt and the real /v1/publish flow. If
|
|
16
|
-
// the receipt shape or secret handling changes, change it here once.
|
|
10
|
+
// These are plain Markdown strings with no surface-specific framing: the skill
|
|
11
|
+
// template substitutes the `{{prose.*}}` placeholders during generation, and the
|
|
12
|
+
// TS surfaces import them. Keep them grounded in the real API receipt and the
|
|
13
|
+
// real /v1/publish flow; if either changes, change it here once.
|
|
17
14
|
const API_ORIGIN = serviceOrigin("api", "prod");
|
|
18
15
|
const PUBLISH_ENDPOINT = `${API_ORIGIN}/v1/publish`;
|
|
19
16
|
const EXCHANGE_ENDPOINT = `${API_ORIGIN}/v1/claim/exchange`;
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
17
|
+
// Secret/credential patterns excluded from a publish archive. The `find`
|
|
18
|
+
// preflight and the `zip -x` exclusions both derive from this list, so the
|
|
19
|
+
// no-install and skill copies cannot disagree.
|
|
23
20
|
export const ARCHIVE_SECRET_GLOBS = [
|
|
24
21
|
".env*",
|
|
25
22
|
".npmrc",
|
|
@@ -58,10 +55,7 @@ function findExpr(globs) {
|
|
|
58
55
|
.map((glob) => (glob.includes("/") ? `-path "${glob}"` : `-name "${glob}"`))
|
|
59
56
|
.join(" -o ");
|
|
60
57
|
}
|
|
61
|
-
/**
|
|
62
|
-
* The credential rules every surface must repeat. Treat tokens as secrets, keep
|
|
63
|
-
* them out of logs and transcripts, and never echo them back.
|
|
64
|
-
*/
|
|
58
|
+
/** The credential rules every surface must repeat. */
|
|
65
59
|
export const SECRET_RULES_PROSE = [
|
|
66
60
|
"Treat space keys, upload tokens, device codes, and API keys as credentials. Do not print",
|
|
67
61
|
"them, paste them into chat, commit them, archive them, or include them in shared logs. Avoid",
|
|
@@ -70,14 +64,17 @@ export const SECRET_RULES_PROSE = [
|
|
|
70
64
|
"clear them after use, and avoid durable auth unless the user explicitly asks.",
|
|
71
65
|
].join("\n");
|
|
72
66
|
export const POSITIONING_PROSE = [
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
// What the product is, before any claim about it. One constant, shared with
|
|
68
|
+
// the dashboard's copied page context (see docs/page-context-prompt.ts).
|
|
69
|
+
SPACEFAST_POSITIONING,
|
|
70
|
+
"",
|
|
71
|
+
"Spacefast is the best place to share AI artifacts.",
|
|
72
|
+
"Spaces serve files directly; app code runs only through a declared Zero or Functions runtime.",
|
|
76
73
|
"",
|
|
77
74
|
"Do not claim that no-account publish followed by claim is unique to Spacefast. here.now and",
|
|
78
75
|
"Netlify offer native anonymous publish-and-claim flows; Cloudflare offers temporary accounts",
|
|
79
76
|
"and claim deployments; Vercel lets agents and platforms transfer deployments to users.",
|
|
80
|
-
"Spacefast's narrower lane is direct
|
|
77
|
+
"Spacefast's narrower lane is direct HTTP publishing with separate Access and Claim",
|
|
81
78
|
"authority, immutable versions, rollback, and agent continuation after claim. GitHub Pages",
|
|
82
79
|
"remains repository-native and has no equivalent anonymous ownership-handoff flow documented.",
|
|
83
80
|
].join("\n");
|
|
@@ -87,22 +84,26 @@ export const EXPLAIN_TO_USER_PROSE = [
|
|
|
87
84
|
"Keep live and immutable Version addresses as receipt metadata. After claim, the agent key is named",
|
|
88
85
|
"and revocable in the dashboard.",
|
|
89
86
|
].join("\n");
|
|
87
|
+
export const FEEDBACK_PROSE = "With user approval, send feedback with `sf feedback`, MCP `execute` (search `send feedback`), or `POST /v1/feedback`. Never include credentials or private links.";
|
|
90
88
|
export const FAILURE_CONDUCT_PROSE = [
|
|
91
89
|
"On failure, surface the problem document's `code`, `type`, and `requestId`, then stop.",
|
|
92
|
-
"Do not invent undocumented endpoints. Do not read credentials out of auth files.
|
|
93
|
-
"
|
|
90
|
+
"Do not invent undocumented endpoints. Do not read credentials out of auth files. Retrying a",
|
|
91
|
+
"failed publish with the same files is safe: it returns the Space the first attempt created.",
|
|
92
|
+
FEEDBACK_PROSE,
|
|
94
93
|
].join("\n");
|
|
95
94
|
export const LANE_PICKER_PROSE = [
|
|
96
|
-
"Pick the lane by environment:
|
|
97
|
-
"filesystem means
|
|
98
|
-
"
|
|
95
|
+
"Pick the lane by environment: a one-off publish means curl; a git checkout means the push.new",
|
|
96
|
+
"remote; repeat work with a filesystem means the CLI — the best setup, `npx -y spacefast` runs it",
|
|
97
|
+
"with no install, and `sf plugins` installs your client's plugin; no filesystem means hosted MCP;",
|
|
98
|
+
"CI means a machine OAuth client. After success, recommend the best next-time setup for the",
|
|
99
|
+
"user's client.",
|
|
99
100
|
].join("\n");
|
|
100
101
|
export const HOW_TO_RESEARCH_PROSE = [
|
|
101
102
|
"For an error, fetch the problem document's `type` URL. For capability questions, search the OpenAPI catalog with `execute` or append",
|
|
102
103
|
"`.md` to website docs URLs. For contracts, read `publish-spec.json` or OpenAPI. Never guess",
|
|
103
104
|
"limits: publish, then read diagnostics.",
|
|
104
105
|
].join("\n");
|
|
105
|
-
/** Curl/API skill lane: docs URLs only
|
|
106
|
+
/** Curl/API skill lane: docs URLs only, no MCP or CLI tools required. */
|
|
106
107
|
export const HOW_TO_RESEARCH_API_PROSE = [
|
|
107
108
|
"For an error, fetch the problem document's `type` URL. For capability questions, append `.md` to website docs",
|
|
108
109
|
"URLs (for example `https://spacefast.com/docs/agents.md`) or read",
|
|
@@ -111,8 +112,9 @@ export const HOW_TO_RESEARCH_API_PROSE = [
|
|
|
111
112
|
].join("\n");
|
|
112
113
|
/** MCP plugin skill lane: prefer skills for workflows and execute for the OpenAPI catalog. */
|
|
113
114
|
export const HOW_TO_RESEARCH_MCP_PROSE = [
|
|
114
|
-
"For an error, fetch the problem document's `type` URL when present. For workflows
|
|
115
|
-
"
|
|
115
|
+
"For an error, fetch the problem document's `type` URL when present. For workflows and how-to",
|
|
116
|
+
"recipes, call `search_docs`; the `skills` tool serves the `execute` guide, not workflow recipes.",
|
|
117
|
+
"For capability questions, use `execute` with `tools.search`.",
|
|
116
118
|
"For contracts, read `publish-spec.json` or OpenAPI via docs. Never guess limits: publish,",
|
|
117
119
|
"then inspect status or diagnostics through `execute`.",
|
|
118
120
|
].join("\n");
|
|
@@ -127,9 +129,8 @@ export const ENV_NOTES_API_PROSE = [
|
|
|
127
129
|
"https://spacefast.com/docs/agents.",
|
|
128
130
|
].join("\n");
|
|
129
131
|
/**
|
|
130
|
-
* The CHECK verb:
|
|
131
|
-
*
|
|
132
|
-
* anonymous space instead of a new version.
|
|
132
|
+
* The CHECK verb: look for an existing space before creating one, so a returning
|
|
133
|
+
* agent publishes a new version instead of a duplicate anonymous space.
|
|
133
134
|
*/
|
|
134
135
|
export const CHECK_BEFORE_PUBLISH_PROSE = [
|
|
135
136
|
"Before creating a space, check whether this project already has one:",
|
|
@@ -141,7 +142,7 @@ export const CHECK_BEFORE_PUBLISH_PROSE = [
|
|
|
141
142
|
"",
|
|
142
143
|
"If any of these finds a space, publish a new version to that `spaceId` (see the update",
|
|
143
144
|
"instructions) instead of creating another space. Only create a new space when none exists",
|
|
144
|
-
"and the user wants a new
|
|
145
|
+
"and the user wants a new one.",
|
|
145
146
|
].join("\n");
|
|
146
147
|
export const CHECK_BEFORE_PUBLISH_API_PROSE = [
|
|
147
148
|
"Before creating a space, check whether this project already has one:",
|
|
@@ -157,7 +158,7 @@ export const CHECK_BEFORE_PUBLISH_API_PROSE = [
|
|
|
157
158
|
"",
|
|
158
159
|
"If any of these finds a space, publish a new version to that `spaceId` (see the update",
|
|
159
160
|
"instructions) instead of creating another space. Only create a new space when none exists",
|
|
160
|
-
"and the user wants a new
|
|
161
|
+
"and the user wants a new one.",
|
|
161
162
|
].join("\n");
|
|
162
163
|
export const CHECK_BEFORE_PUBLISH_MCP_PROSE = [
|
|
163
164
|
"Before creating a space, check whether this project already has one:",
|
|
@@ -168,7 +169,7 @@ export const CHECK_BEFORE_PUBLISH_MCP_PROSE = [
|
|
|
168
169
|
"- When authenticated, use `execute` to call the generated list-spaces operation.",
|
|
169
170
|
"",
|
|
170
171
|
"If any of these finds a space, publish a new version to that space instead of creating",
|
|
171
|
-
"another. Only create a new space when none exists and the user wants a new
|
|
172
|
+
"another. Only create a new space when none exists and the user wants a new one.",
|
|
172
173
|
].join("\n");
|
|
173
174
|
/**
|
|
174
175
|
* The SAVE verb: persist the space id and credential after a first publish so
|
|
@@ -219,6 +220,34 @@ export const SAVE_STATE_MCP_PROSE = [
|
|
|
219
220
|
* The UPDATE verb: a new version to the SAME space. Never re-run the
|
|
220
221
|
* first-publish recipe when saved state exists.
|
|
221
222
|
*/
|
|
223
|
+
/*
|
|
224
|
+
* Push to deploy. Written for an agent that is already in a checkout, where a
|
|
225
|
+
* push is fewer moves than an upload — and where the receipt comes back in the
|
|
226
|
+
* push output, so there is no second call to learn the URL.
|
|
227
|
+
*
|
|
228
|
+
* The GitHub note is not an aside: a space bound to a GitHub repository refuses
|
|
229
|
+
* direct pushes, and an agent that does not know that reads the refusal as a
|
|
230
|
+
* broken remote instead of as the answer it is.
|
|
231
|
+
*/
|
|
232
|
+
export const PUSH_TO_DEPLOY_PROSE = [
|
|
233
|
+
"A space's git remote is a real remote, and pushing to it publishes. Every publish receipt",
|
|
234
|
+
"and the Space resource carry `git.remoteUrl`; authenticate as HTTP Basic user `t` with the",
|
|
235
|
+
"space key or an API key as the password. The same key you already have works — publishing",
|
|
236
|
+
"and pushing are one credential, not two.",
|
|
237
|
+
"",
|
|
238
|
+
"```bash",
|
|
239
|
+
"git remote add spacefast https://t:<key>@push.new/<space-slug>.git",
|
|
240
|
+
"git push spacefast main",
|
|
241
|
+
"```",
|
|
242
|
+
"",
|
|
243
|
+
"The push output is the receipt: it reports the live URL, the immutable version URL, and —",
|
|
244
|
+
"for an anonymous space — the claim link and its expiry, as `remote:` lines. Report those to",
|
|
245
|
+
"the user the same way you would report a publish receipt. A deploy that outruns the push",
|
|
246
|
+
"prints a status URL instead and finishes on its own; the push still exits 0.",
|
|
247
|
+
"",
|
|
248
|
+
"If `git.managedBy` is `github`, this space deploys from `git.repository` and a direct push is",
|
|
249
|
+
"refused. Push to that GitHub repository instead, or tell the user to disconnect GitHub first.",
|
|
250
|
+
].join("\n");
|
|
222
251
|
export const UPDATE_SPACE_PROSE = [
|
|
223
252
|
"To publish a new version of an existing space, send the same `spaceId` with bearer auth —",
|
|
224
253
|
"never re-run the first-publish recipe when saved state exists:",
|
|
@@ -255,7 +284,7 @@ export const CONTINUE_AFTER_CLAIM_PROSE = [
|
|
|
255
284
|
"Save `data.credential.accessToken` into `.spacefast/state.json` (replace the `claimToken`",
|
|
256
285
|
'field with `"accessToken"`), then retry the publish with the new bearer. The exchange works',
|
|
257
286
|
"exactly once; if it returns `continuation_used` or `continuation_unavailable`, ask the user",
|
|
258
|
-
"to create an API key in the dashboard (
|
|
287
|
+
"to create an API key in the dashboard (team → Agents → API keys).",
|
|
259
288
|
].join("\n");
|
|
260
289
|
const LEXICAL_PUBLISH_PATH_PREFLIGHT = [
|
|
261
290
|
'case "$publish_target" in /*) check_path="$publish_target" ;; *) check_path="$PWD/$publish_target" ;; esac',
|
|
@@ -321,7 +350,7 @@ const SECURE_ARCHIVE_RECIPE = [
|
|
|
321
350
|
];
|
|
322
351
|
/**
|
|
323
352
|
* The zero-install publish sequence: a replay-safe curl loop for a file, a
|
|
324
|
-
* secret-preflighted zip for a folder. This is the load-bearing rung 0
|
|
353
|
+
* secret-preflighted zip for a folder. This is the load-bearing rung 0. An
|
|
325
354
|
* agent that stops here must still succeed.
|
|
326
355
|
*/
|
|
327
356
|
export const PUBLISH_SEQUENCE_PROSE = [
|
|
@@ -340,12 +369,8 @@ export const PUBLISH_SEQUENCE_PROSE = [
|
|
|
340
369
|
'[ -f "$publish_target" ] || { printf "error: publish file is not a regular file\\n" >&2; exit 2; }',
|
|
341
370
|
'escaped_path="${publish_target//\\\\/\\\\\\\\}"',
|
|
342
371
|
'escaped_path="${escaped_path//\\"/\\\\\\"}"',
|
|
343
|
-
"idempotency_key=\"publish-$(od -An -N16 -tx1 /dev/urandom | tr -d ' \\n')\"",
|
|
344
|
-
"idempotency_principal=\"$(od -An -N32 -tx1 /dev/urandom | tr -d ' \\n')\"",
|
|
345
372
|
'api_origin="${SPACEFAST_API_ORIGIN:-https://api.spacefast.com}"',
|
|
346
373
|
'curl -q -sS -F "files=@\\"$escaped_path\\"" \\',
|
|
347
|
-
' -H "Idempotency-Key: $idempotency_key" \\',
|
|
348
|
-
' -H "x-spacefast-idempotency-principal: $idempotency_principal" \\',
|
|
349
374
|
' "$api_origin/v1/publish"',
|
|
350
375
|
"SPACEFAST_PUBLISH",
|
|
351
376
|
"```",
|
|
@@ -379,7 +404,7 @@ export const PUBLISH_SEQUENCE_PROSE = [
|
|
|
379
404
|
* What the JSON publish receipt contains and how to read it. The receipt is the
|
|
380
405
|
* source of truth; never invent a separate success signal.
|
|
381
406
|
*/
|
|
382
|
-
export const
|
|
407
|
+
export const RECEIPT_CONTRACT_PROSE = [
|
|
383
408
|
"The JSON receipt is the source of truth:",
|
|
384
409
|
"",
|
|
385
410
|
"- `data.space.liveUrl` — the live URL.",
|
|
@@ -425,18 +450,14 @@ export const SIGNED_UPLOAD_PROSE = [
|
|
|
425
450
|
"For each `data.upload.targets[]`, send `target.method` to `target.url` with `target.headers`.",
|
|
426
451
|
'If `target.body.kind` is `url`, send JSON `{ "url": target.body.url }`; otherwise send the',
|
|
427
452
|
"local file bytes for `target.path`. Then call `data.links.finalize` and share the final receipt.",
|
|
428
|
-
"If the session expires or only a page of files is returned, follow `data.links.
|
|
453
|
+
"If the session expires or only a page of files is returned, follow `data.links.refresh`.",
|
|
429
454
|
"",
|
|
430
455
|
"A manifest entry may name a `sourceUrl`, so bytes that already live on a public HTTPS URL never",
|
|
431
456
|
"have to round-trip through the agent. Do not base64 large archives into chat or MCP tool arguments.",
|
|
432
457
|
].join("\n");
|
|
433
458
|
/**
|
|
434
|
-
* Named fragments for the skill-template generator
|
|
435
|
-
* `{{prose
|
|
436
|
-
* `{{prose.receiptShape}}`, `{{prose.signedUpload}}`,
|
|
437
|
-
* `{{prose.checkBeforePublish}}`, `{{prose.saveState}}`,
|
|
438
|
-
* `{{prose.updateSpace}}`, and `{{prose.continueAfterClaim}}` placeholders;
|
|
439
|
-
* generate-skill.mjs substitutes these.
|
|
459
|
+
* Named fragments for the skill-template generator: the templates carry
|
|
460
|
+
* `{{prose.<key>}}` placeholders and generate-skill.mjs substitutes these.
|
|
440
461
|
*/
|
|
441
462
|
export const AGENT_PROSE = {
|
|
442
463
|
positioning: POSITIONING_PROSE,
|
|
@@ -444,7 +465,7 @@ export const AGENT_PROSE = {
|
|
|
444
465
|
explainToUser: EXPLAIN_TO_USER_PROSE,
|
|
445
466
|
failureConduct: FAILURE_CONDUCT_PROSE,
|
|
446
467
|
publishSequence: PUBLISH_SEQUENCE_PROSE,
|
|
447
|
-
|
|
468
|
+
receiptContract: RECEIPT_CONTRACT_PROSE,
|
|
448
469
|
signedUpload: SIGNED_UPLOAD_PROSE,
|
|
449
470
|
checkBeforePublish: CHECK_BEFORE_PUBLISH_PROSE,
|
|
450
471
|
checkBeforePublishApi: CHECK_BEFORE_PUBLISH_API_PROSE,
|
|
@@ -454,6 +475,7 @@ export const AGENT_PROSE = {
|
|
|
454
475
|
saveStateApi: SAVE_STATE_API_PROSE,
|
|
455
476
|
saveStateMcp: SAVE_STATE_MCP_PROSE,
|
|
456
477
|
updateSpace: UPDATE_SPACE_PROSE,
|
|
478
|
+
pushToDeploy: PUSH_TO_DEPLOY_PROSE,
|
|
457
479
|
continueAfterClaim: CONTINUE_AFTER_CLAIM_PROSE,
|
|
458
480
|
lanePicker: LANE_PICKER_PROSE,
|
|
459
481
|
howToResearch: HOW_TO_RESEARCH_PROSE,
|