@spacefast/common 0.0.13 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/dist/agents/client-registry.d.ts +22 -0
  2. package/dist/agents/client-registry.js +41 -0
  3. package/dist/agents/connect-targets.d.ts +51 -6
  4. package/dist/agents/connect-targets.js +93 -28
  5. package/dist/brand.d.ts +0 -11
  6. package/dist/brand.js +0 -20
  7. package/dist/config/domains.d.ts +15 -6
  8. package/dist/config/domains.js +24 -10
  9. package/dist/config/postgres-budget.d.ts +3 -0
  10. package/dist/config/postgres-budget.js +6 -1
  11. package/dist/config/postgres-budget.json +2 -1
  12. package/dist/contracts/abuse.js +1 -1
  13. package/dist/contracts/access.d.ts +149 -44
  14. package/dist/contracts/access.js +94 -8
  15. package/dist/contracts/activity.js +5 -1
  16. package/dist/contracts/api-keys.js +9 -9
  17. package/dist/contracts/archives.js +11 -11
  18. package/dist/contracts/beta.js +1 -1
  19. package/dist/contracts/builds.js +14 -14
  20. package/dist/contracts/channels.js +2 -2
  21. package/dist/contracts/comments.d.ts +86 -15
  22. package/dist/contracts/comments.js +116 -20
  23. package/dist/contracts/common.d.ts +12 -12
  24. package/dist/contracts/common.js +33 -30
  25. package/dist/contracts/continuation.js +5 -5
  26. package/dist/contracts/data-source-routes.js +1 -1
  27. package/dist/contracts/device-auth.d.ts +6 -6
  28. package/dist/contracts/device-auth.js +12 -12
  29. package/dist/contracts/docs.d.ts +10 -10
  30. package/dist/contracts/domains.js +39 -33
  31. package/dist/contracts/enums.d.ts +2 -10
  32. package/dist/contracts/enums.js +0 -2
  33. package/dist/contracts/error-code-meta.d.ts +116 -47
  34. package/dist/contracts/error-code-meta.js +37 -16
  35. package/dist/contracts/error-codes.d.ts +7 -4
  36. package/dist/contracts/error-codes.js +46 -21
  37. package/dist/contracts/events.d.ts +10 -3
  38. package/dist/contracts/events.js +33 -5
  39. package/dist/contracts/execution.d.ts +26 -0
  40. package/dist/contracts/execution.js +21 -0
  41. package/dist/contracts/feature-lifecycle.d.ts +21 -24
  42. package/dist/contracts/feature-lifecycle.js +28 -26
  43. package/dist/contracts/features.js +4 -4
  44. package/dist/contracts/functions.d.ts +8 -140
  45. package/dist/contracts/functions.js +9 -19
  46. package/dist/contracts/git.d.ts +1 -1
  47. package/dist/contracts/grants.d.ts +96 -79
  48. package/dist/contracts/grants.js +130 -43
  49. package/dist/contracts/ids.d.ts +2 -2
  50. package/dist/contracts/ids.js +2 -4
  51. package/dist/contracts/internal.js +2 -2
  52. package/dist/contracts/mcp.d.ts +17 -556
  53. package/dist/contracts/mcp.js +9 -103
  54. package/dist/contracts/me.js +3 -4
  55. package/dist/contracts/notifications.d.ts +108 -0
  56. package/dist/contracts/notifications.js +54 -0
  57. package/dist/contracts/oauth-resources.d.ts +18 -1
  58. package/dist/contracts/oauth-resources.js +22 -2
  59. package/dist/contracts/operations.d.ts +2 -50
  60. package/dist/contracts/operations.js +0 -19
  61. package/dist/contracts/pages.d.ts +8 -4
  62. package/dist/contracts/pages.js +16 -3
  63. package/dist/contracts/platform.js +10 -8
  64. package/dist/contracts/problem-document.d.ts +14 -0
  65. package/dist/contracts/problem-document.js +35 -0
  66. package/dist/contracts/push-new.d.ts +21 -6
  67. package/dist/contracts/push-new.js +3 -7
  68. package/dist/contracts/resources.d.ts +8 -7
  69. package/dist/contracts/resources.js +9 -6
  70. package/dist/contracts/runtime-api.d.ts +162 -92
  71. package/dist/contracts/runtime-api.js +99 -27
  72. package/dist/contracts/runtime-app.d.ts +41 -11
  73. package/dist/contracts/runtime-app.js +22 -1
  74. package/dist/contracts/runtime-db.d.ts +424 -0
  75. package/dist/contracts/runtime-db.js +108 -0
  76. package/dist/contracts/runtime-storage.d.ts +120 -0
  77. package/dist/contracts/runtime-storage.js +40 -0
  78. package/dist/contracts/sf-config-v1.d.ts +49 -1
  79. package/dist/contracts/sf-config-v1.js +2 -2
  80. package/dist/contracts/sites.d.ts +1 -0
  81. package/dist/contracts/sites.js +20 -12
  82. package/dist/contracts/space-config.d.ts +62 -22
  83. package/dist/contracts/space-config.js +173 -47
  84. package/dist/contracts/spaces.d.ts +79 -45
  85. package/dist/contracts/spaces.js +66 -50
  86. package/dist/contracts/superadmin-spaces.d.ts +13 -2
  87. package/dist/contracts/superadmin-spaces.js +15 -0
  88. package/dist/contracts/superadmin-tenants.js +4 -4
  89. package/dist/contracts/superadmin.d.ts +4 -0
  90. package/dist/contracts/superadmin.js +14 -1
  91. package/dist/contracts/tags.d.ts +17 -17
  92. package/dist/contracts/tags.js +1 -1
  93. package/dist/contracts/teams.js +25 -16
  94. package/dist/contracts/theme-json.js +1 -1
  95. package/dist/contracts/transfers.js +4 -4
  96. package/dist/contracts/variables.js +3 -3
  97. package/dist/contracts/zero.d.ts +204 -338
  98. package/dist/contracts/zero.js +94 -98
  99. package/dist/dashboard-paths/index.d.ts +16 -1
  100. package/dist/dashboard-paths/index.js +18 -0
  101. package/dist/docs/agent-handoff-document.d.ts +2 -2
  102. package/dist/docs/agent-handoff-document.js +8 -7
  103. package/dist/docs/agent-prose.d.ts +22 -3
  104. package/dist/docs/agent-prose.js +221 -51
  105. package/dist/docs/agent-setup.d.ts +13 -10
  106. package/dist/docs/agent-setup.js +279 -53
  107. package/dist/docs/agent-solutions.d.ts +6 -6
  108. package/dist/docs/agent-solutions.js +23 -21
  109. package/dist/docs/catalog.d.ts +29 -74
  110. package/dist/docs/catalog.js +65 -92
  111. package/dist/docs/error-docs.js +293 -221
  112. package/dist/docs/skill-distribution.d.ts +2 -0
  113. package/dist/docs/skill-distribution.js +7 -4
  114. package/dist/docs/skills.d.ts +546 -0
  115. package/dist/docs/skills.js +340 -0
  116. package/dist/utils/browser-credential.d.ts +19 -0
  117. package/dist/utils/browser-credential.js +62 -0
  118. package/dist/utils/comment-avatars.d.ts +7 -0
  119. package/dist/utils/comment-avatars.js +42 -0
  120. package/dist/utils/content-type.d.ts +3 -3
  121. package/dist/utils/content-type.js +38 -61
  122. package/dist/utils/domain-diagnostics.d.ts +5 -5
  123. package/dist/utils/domain-diagnostics.js +21 -10
  124. package/dist/utils/local-space-state.d.ts +78 -4
  125. package/dist/utils/local-space-state.js +383 -82
  126. package/dist/utils/one-shot-replay.d.ts +14 -0
  127. package/dist/utils/one-shot-replay.js +21 -0
  128. package/dist/utils/page-preview.d.ts +7 -0
  129. package/dist/utils/page-preview.js +9 -0
  130. package/dist/utils/pages.d.ts +2 -11
  131. package/dist/utils/pages.js +58 -14
  132. package/dist/utils/publish-form-data.d.ts +22 -0
  133. package/dist/utils/publish-form-data.js +26 -0
  134. package/dist/utils/publish-policy.js +20 -4
  135. package/dist/utils/query-keys.d.ts +7 -4
  136. package/dist/utils/query-keys.js +13 -4
  137. package/dist/utils/runtime-paths.d.ts +3 -0
  138. package/dist/utils/runtime-paths.js +7 -0
  139. package/dist/utils/secure-local-file.d.ts +73 -0
  140. package/dist/utils/secure-local-file.js +1373 -0
  141. package/dist/utils/space-config.d.ts +13 -4
  142. package/dist/utils/space-config.js +123 -15
  143. package/dist/utils/storage-policy.d.ts +20 -0
  144. package/dist/utils/storage-policy.fixtures.json +174 -0
  145. package/dist/utils/storage-policy.js +193 -0
  146. package/dist/utils/upload-session.d.ts +12 -1
  147. package/dist/utils/upload-session.js +37 -2
  148. package/dist/utils/version-file-browser.d.ts +13 -0
  149. package/dist/utils/version-file-browser.js +59 -0
  150. package/package.json +19 -1
  151. package/dist/contracts/mounts.d.ts +0 -142
  152. package/dist/contracts/mounts.js +0 -56
  153. package/dist/utils/asset-fingerprint.d.ts +0 -64
  154. package/dist/utils/asset-fingerprint.js +0 -620
@@ -1,16 +1,29 @@
1
- import { getConnectTarget, } from "../agents/connect-targets.js";
1
+ import { buildClaudeMcpAddCommand, buildHermesMcpAddCommand, cursorHostedMcpDeeplink, cursorHostedMcpServersJson, getConnectTarget, opencodeHostedMcpConfig, vscodeHostedMcpDeeplink, vscodeHostedMcpServersJson, } from "../agents/connect-targets.js";
2
2
  import { BRAND } from "../brand.js";
3
+ import { serviceOrigin } from "../config/domains.js";
4
+ import { createBrowserCredentialUrl, parseBrowserCredentialUrl, } from "../utils/browser-credential.js";
3
5
  import { CHECK_BEFORE_PUBLISH_PROSE, CONTINUE_AFTER_CLAIM_PROSE, ENV_NOTES_PROSE, FAILURE_CONDUCT_PROSE, HOW_TO_RESEARCH_PROSE, LANE_PICKER_PROSE, POSITIONING_PROSE, PUBLISH_SEQUENCE_PROSE, RECEIPT_SHAPE_PROSE, SAVE_STATE_PROSE, SECRET_RULES_PROSE, SIGNED_UPLOAD_PROSE, UPDATE_SPACE_PROSE, } from "./agent-prose.js";
4
- import { spacefastInstallCurlCommand, spacefastInstallPowerShellCommand, spacefastSkillFetchCommand, spacefastSkillInstallCommand, spacefastSkillUrl, } from "./skill-distribution.js";
5
- export const agentSetupUrl = `${BRAND.agentSetupOrigin}/ai`;
6
- export const agentSetupTextUrl = `${BRAND.agentSetupOrigin}/ai.txt`;
6
+ import { spacefastClaudePluginAddCommand, spacefastClaudeDesktopExtensionUrl, spacefastClaudePluginInstallCommand, spacefastCodexPluginAddCommand, spacefastCodexPluginInstallCommand, spacefastInstallCurlCommand, spacefastInstallPowerShellCommand, spacefastSkillFetchCommand, spacefastSkillInstallCommand, spacefastSkillUrl, } from "./skill-distribution.js";
7
+ // The one URL every agent prompt points at. /setup.md is the actionable front
8
+ // door (publish now, then set up your client); /ai.txt stays the deep
9
+ // reference contract it links out to.
10
+ export const agentSetupUrl = `${serviceOrigin("www", "prod")}/setup.md`;
11
+ export const agentSetupTextUrl = `${serviceOrigin("www", "prod")}/ai.txt`;
7
12
  const agentSetupParsedUrl = new URL(agentSetupUrl);
8
13
  export const agentSetupHostLabel = agentSetupParsedUrl.host + agentSetupParsedUrl.pathname;
9
- export const agentSetupPrompt = `Fetch ${agentSetupUrl} and follow it to publish this project — or update it if it's already on ${BRAND.productName}. Ask me what I want to publish if it isn't obvious.`;
14
+ // Deliberately bare: the document itself tells the agent to ask what to
15
+ // publish when it isn't obvious and to update an existing space, so the prompt
16
+ // stays a single gesture that works in any client.
17
+ export const agentSetupPrompt = `Fetch ${agentSetupUrl}`;
18
+ // The no-install publish: one POST, no account. Single-sourced so the
19
+ // marketing pages and the setup doc can never show different commands.
20
+ export const anonymousPublishCurlCommand = `curl -F "files=@index.html" ${serviceOrigin("api", "prod")}/v1/publish`;
10
21
  /*
11
- * Agent handoff contract: links use https://<app-origin>/agent/<documentId>#<secret>, with
12
- * opaque document IDs and secrets. The fragment is the secret, so ordinary document fetches
13
- * never send or reveal it. Fetching never consumes a handoff; exactly one successful redemption
22
+ * Agent handoff contract: private links carry a one-use path token on
23
+ * https://<access-origin>/agent/<documentId>/<token>; durable agent credentials stay encrypted at
24
+ * the broker. Agents fetch the reconstructed clean dashboard document URL, never the private
25
+ * handoff URL. Fetching never consumes a
26
+ * handoff; exactly one successful redemption
14
27
  * does. Redemption is app-private, reuses the existing device/OAuth approval service, and returns
15
28
  * the already-approved finite Space-scoped credential: there is no public AgentGrant resource,
16
29
  * durable key table, or team-wide publisher key. The CLI accepts links only through stdin or a
@@ -47,19 +60,18 @@ function isSafeHttpUrl(url) {
47
60
  }
48
61
  /**
49
62
  * The single shared grammar for handoff links:
50
- * `<app-origin>/agent/<documentId>#<secret>`. The document ID must match
51
- * `[A-Za-z0-9_-]{1,512}` and the secret `[A-Za-z0-9_-]{16,512}` (the minimum keeps secrets
52
- * high-entropy and makes the public-projection leak check meaningful); the two must differ;
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.
63
+ * `<access-origin>/agent/<documentId>/<oneUseToken>`. The document ID must match
64
+ * `[A-Za-z0-9_-]{1,512}` and the token `[A-Za-z0-9_-]{16,512}`; the two must differ;
65
+ * queries, fragments, and URL userinfo are forbidden. The URL must sit exactly on the caller's
66
+ * configured HTTP(S) access origin; the clean static document may live on a separate explicitly
67
+ * supplied dashboard origin.
56
68
  * Normalization is part of the contract: the input is WHATWG-URL-normalized first (backslashes,
57
69
  * dot segments, stray tab/CR/LF, empty userinfo, case, default ports), the grammar is enforced on
58
70
  * the normalized URL, and only the reconstructed canonical URL ever leaves this function.
59
71
  * Dashboard minting, CLI ingestion, and control-plane validation must all use this parser so the
60
72
  * grammar can never fork.
61
73
  */
62
- export function parseAgentHandoffUrl(value, expectedOrigin) {
74
+ export function parseAgentHandoffUrl(value, expectedOrigin, documentOrigin = expectedOrigin) {
63
75
  let expectedUrl;
64
76
  try {
65
77
  expectedUrl = new URL(expectedOrigin);
@@ -71,26 +83,54 @@ export function parseAgentHandoffUrl(value, expectedOrigin) {
71
83
  throw new TypeError("Agent handoff expectedOrigin must be an HTTP(S) origin.");
72
84
  }
73
85
  const canonicalExpectedOrigin = expectedUrl.origin;
86
+ let canonicalDocumentOrigin;
87
+ try {
88
+ const documentUrl = new URL(documentOrigin);
89
+ if (!isSafeHttpUrl(documentUrl) ||
90
+ documentUrl.username ||
91
+ documentUrl.password ||
92
+ documentUrl.pathname !== "/" ||
93
+ documentUrl.search ||
94
+ documentUrl.hash) {
95
+ throw new TypeError("invalid_document_origin");
96
+ }
97
+ canonicalDocumentOrigin = documentUrl.origin;
98
+ }
99
+ catch {
100
+ throw new TypeError("Agent handoff documentOrigin must be a clean HTTP(S) origin.");
101
+ }
74
102
  let url;
103
+ let parsedCredential;
75
104
  try {
76
105
  url = new URL(value);
106
+ parsedCredential = parseBrowserCredentialUrl(value);
77
107
  }
78
108
  catch {
79
- throw new TypeError("Agent handoff URL must be a valid HTTP(S) URL.");
109
+ throw new TypeError("Agent handoff URL must be a valid private HTTP(S) URL.");
80
110
  }
81
- const documentId = /^\/agent\/([^/]+)$/.exec(url.pathname)?.[1];
82
- const secret = url.hash.slice(1);
111
+ const documentId = /^\/agent\/([^/]+)\/[^/]+$/.exec(url.pathname)?.[1];
112
+ const secret = parsedCredential.credential;
83
113
  if (url.origin !== canonicalExpectedOrigin ||
84
- url.username !== "" ||
85
- url.password !== "" ||
86
114
  value.includes("?") ||
115
+ url.hash !== "" ||
87
116
  !documentId ||
88
117
  !opaqueHandoffDocumentId.test(documentId) ||
89
118
  !opaqueHandoffSecret.test(secret) ||
90
119
  documentId === secret) {
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.");
120
+ throw new TypeError("Agent handoff URL must be a private /agent/<id>/<token> URL on the access origin with one opaque token (16-512 chars, distinct from the id) and no userinfo, query, or fragment.");
92
121
  }
93
- return { documentId, documentUrl: `${url.origin}${url.pathname}`, secret };
122
+ const handoffUrl = createBrowserCredentialUrl({
123
+ origin: canonicalExpectedOrigin,
124
+ path: "/agent",
125
+ publicId: documentId,
126
+ credential: secret,
127
+ });
128
+ return {
129
+ documentId,
130
+ documentUrl: new URL(`/agent/${encodeURIComponent(documentId)}`, canonicalDocumentOrigin).toString(),
131
+ handoffUrl,
132
+ secret,
133
+ };
94
134
  }
95
135
  function validateMcpEndpoint(value) {
96
136
  let url;
@@ -114,11 +154,11 @@ function validateMcpEndpoint(value) {
114
154
  export const AGENT_HANDOFF_CLI_TARGET_IDS = ["claude-code", "codex"];
115
155
  export const AGENT_HANDOFF_PREAMBLE = "Talk to the user before installing tools or publishing. Confirm the file or folder they want to publish.";
116
156
  /** The fetch-safety contract: reading the document never consumes or leaks a handoff. */
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.";
118
- export const AGENT_HANDOFF_FRAGMENT_HYGIENE = "Keep its fragment out of commands, request URLs, logs, and replies.";
157
+ export const AGENT_HANDOFF_FETCH_SAFETY = "Keep the original handoff link private. Fetching this clean document URL did not use or send its credential; the credential 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.";
158
+ export const AGENT_HANDOFF_CREDENTIAL_HYGIENE = "Keep the private handoff URL out of commands, request URLs, logs, and replies.";
119
159
  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.`;
120
160
  export const AGENT_HANDOFF_BROWSER_APPROVAL_GUIDANCE = "When the client opens browser approval, ask the user to approve there before publishing.";
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.";
161
+ 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 Access URL; create a scoped Link when a recipient needs access.";
122
162
  /** One target's Connect prose: blurb, copyable setup blocks, deeplink/download lines. */
123
163
  export function agentHandoffConnectSections(target) {
124
164
  const setupSections = [target.primary, target.config].flatMap((setup) => setup ? [[setup.label, `\`\`\`${setup.language}`, setup.value, "```"].join("\n")] : []);
@@ -142,7 +182,7 @@ function renderAgentHandoffInstructions(documentUrl, target) {
142
182
  "## Continue the approved handoff",
143
183
  AGENT_HANDOFF_FETCH_SAFETY,
144
184
  approvalGuidance,
145
- AGENT_HANDOFF_FRAGMENT_HYGIENE,
185
+ AGENT_HANDOFF_CREDENTIAL_HYGIENE,
146
186
  ].join("\n\n"),
147
187
  ["## Publish", AGENT_HANDOFF_PUBLISH_PROSE].join("\n\n"),
148
188
  `General agent instructions: ${agentSetupTextUrl}`,
@@ -151,12 +191,12 @@ function renderAgentHandoffInstructions(documentUrl, target) {
151
191
  }
152
192
  /** Builds every copy-ready handoff artifact from one validated URL and target catalog row. */
153
193
  export function buildAgentHandoff(input) {
154
- const { documentUrl, secret } = parseAgentHandoffUrl(input.url, input.expectedOrigin);
194
+ const { documentUrl, handoffUrl, secret } = parseAgentHandoffUrl(input.url, input.expectedOrigin, input.documentOrigin);
155
195
  const mcpEndpoint = validateMcpEndpoint(input.mcpEndpoint);
156
196
  const target = getConnectTarget(input.targetId, {
157
197
  mcpEndpoint,
158
198
  });
159
- const prompt = `Fetch ${documentUrl}#${secret} and follow the ${target.name} setup instructions to publish with ${BRAND.productName}. Ask me what I want to publish if it isn't obvious.`;
199
+ const prompt = `Fetch ${documentUrl} and follow the ${target.name} setup instructions to publish with ${BRAND.productName}. Use this private one-use handoff only for the exchange step: ${handoffUrl}. Do not fetch the private handoff URL. Ask me what I want to publish if it isn't obvious.`;
160
200
  const instructions = renderAgentHandoffInstructions(documentUrl, target);
161
201
  const actions = [
162
202
  { kind: "copy", label: "Copy for my agent", sensitive: true, value: prompt },
@@ -188,7 +228,7 @@ export function buildAgentHandoff(input) {
188
228
  }),
189
229
  ];
190
230
  if (publicProjections.some((projection) => projection.includes(secret))) {
191
- throw new TypeError("Agent handoff inputs would leak the fragment secret into a public projection.");
231
+ throw new TypeError("Agent handoff inputs would leak the private credential into a public projection.");
192
232
  }
193
233
  return { actions, documentUrl, instructions, prompt };
194
234
  }
@@ -244,8 +284,8 @@ export function aiInstructionsText() {
244
284
  HOW_TO_RESEARCH_PROSE,
245
285
  "",
246
286
  'Add a bearer access token with `-H "Authorization: Bearer ..."` to publish into the user\'s',
247
- "team instead of anonymously. Agent clients should prefer OAuth client credentials. Errors come back as JSON with stable `code` values and a",
248
- "`docsUrl`; fetch the `docsUrl` when you need recovery steps.",
287
+ "team instead of anonymously. Agent clients should prefer OAuth client credentials. Errors come back as RFC 9457 problem documents with stable `code` values and a",
288
+ "`type` URL; fetch the `type` URL when you need recovery steps.",
249
289
  "",
250
290
  "## Install the CLI (On-Device files, updates, versions, rollback, and the rest)",
251
291
  "",
@@ -282,7 +322,7 @@ export function aiInstructionsText() {
282
322
  LANE_PICKER_PROSE,
283
323
  "",
284
324
  "Hosted MCP is the no-download cloud lane for ChatGPT, Codex, Claude, Cursor, and browser",
285
- "clients that can authenticate with OAuth/API bearer credentials:",
325
+ "clients that can authenticate with OAuth:",
286
326
  "",
287
327
  "```text",
288
328
  "https://mcp.spacefast.com",
@@ -293,25 +333,17 @@ export function aiInstructionsText() {
293
333
  "`spaces:read` for status, versions, diffs, and logs, `spaces:write` for claim/create/update,",
294
334
  "`spaces:rename` for slug and managed-address renames, `publish:write` for publish and rollback,",
295
335
  "and `domains:read`/`domains:write` for domain",
296
- "inventory, DNS diagnostics, assignment, verification, and mutation. Hosted MCP does not read",
297
- "local paths; its workspace tools operate on a durable virtual workspace attached to the MCP",
298
- "session.",
336
+ "inventory, DNS diagnostics, assignment, verification, and mutation. Hosted MCP has no",
337
+ "filesystem access.",
299
338
  "",
300
- "For hosted MCP, prefer `prepare_publish`, `resume_publish`, and `finalize_publish` for large",
301
- "artifacts so the agent uploads to signed URLs instead of stuffing bytes into tool calls. Use",
302
- "`import_from_url` when the archive already has an HTTPS URL. Hosted MCP is the right path for",
339
+ "For hosted MCP, call `publish` with inline files. For larger artifacts, use `execute` to",
340
+ "discover and invoke the catalog publish operations. Hosted MCP is the right path for",
303
341
  "ChatGPT/browser agents because it requires no download and never claims local filesystem access.",
304
- "For generated files, use `workspace_write` or `workspace_import`, review with",
305
- "`workspace_manifest` / `workspace_diff`, hand off with `workspace_export`, and publish the",
306
- 'virtual workspace with `publish({ path: "." })`.',
307
- "Before acting on authenticated spaces, load MCP resources such as",
308
- "`spacefast://account/current`, `spacefast://teams/current`, `spacefast://spaces/recent`,",
309
- "`spacefast://spaces/{spaceId}`, and `spacefast://policy/current` for bounded read-only",
310
- "context.",
311
- "`execute` is dry-run-first; mutating multi-step workflows can return",
312
- "`approval_required` and continue through `resume` after the user approves.",
313
- "",
314
- "Use On-Device MCP when the agent needs the user's checkout, path-based publish, or file edits:",
342
+ "`execute` runs code in Spacefast's secure QuickJS sandbox. Mutating OpenAPI calls pause before HTTP",
343
+ "and return an execution ID; continue the same live execution with `resume` and an",
344
+ "`accept`, `decline`, or `cancel` action.",
345
+ "",
346
+ "Use On-Device MCP when the agent needs path-based publish from the user's checkout:",
315
347
  "",
316
348
  "```bash",
317
349
  "sf setup agent --agent auto --connect",
@@ -330,7 +362,7 @@ export function aiInstructionsText() {
330
362
  "",
331
363
  "## After publishing",
332
364
  "",
333
- "- Report the bare Live URL, the immutable Version URL when ready, the private Open URL, and",
365
+ "- Report the bare Live URL, the immutable Version URL when ready, the private Access URL, and",
334
366
  " — for anonymous spaces — the claim link with its expiry. Bare Live and Version URLs carry",
335
367
  " no authority for a private Space; create a scoped Link when a recipient needs access.",
336
368
  " If the receipt's immutable URL is null, poll the",
@@ -347,8 +379,9 @@ export function aiInstructionsText() {
347
379
  " `200` proxy rules.",
348
380
  "- `_headers` — supported response headers.",
349
381
  "- `404.html` — custom not-found page.",
350
- "- `sf.jsonc` — serving config (index, fallback, listing, meta) and the optional",
351
- " `space` link. JSON Schema: https://spacefast.com/schemas/sf.json",
382
+ "- `sf.jsonc` — serving config (index, fallback, listing, meta), the optional `space`",
383
+ " link, and `redirects` / `rewrites` / `headers` arrays in the same matcher grammar",
384
+ " the files use. JSON Schema: https://spacefast.com/schemas/sf.json",
352
385
  "- `/llms.txt` and `/ai.txt` — publish these in the user's site when it should be",
353
386
  " agent-readable too.",
354
387
  "- Plan limits surface as diagnostics, not failures you should guess at: publish the",
@@ -368,7 +401,7 @@ export function aiInstructionsText() {
368
401
  "",
369
402
  "- Docs index for agents: https://spacefast.com/llms.txt",
370
403
  "- One-click connect (Claude Code, Claude, Codex, Cursor, VS Code): https://spacefast.com/agents",
371
- "- Agent guide: https://spacefast.com/docs/agents",
404
+ "- Agent guide: https://spacefast.com/docs/getting-started/agents",
372
405
  "- CLI reference: https://spacefast.com/docs/cli",
373
406
  "- REST API + OpenAPI: https://spacefast.com/docs/api",
374
407
  "- Publish contract (machine-readable): https://spacefast.com/publish-spec.json",
@@ -384,3 +417,196 @@ export function aiInstructionsText() {
384
417
  "",
385
418
  ].join("\n");
386
419
  }
420
+ /*
421
+ * The /setup.md document — the one URL every agent prompt points at. Modeled
422
+ * on the Cloudflare/here.now agent-setup prompts: publish first with zero
423
+ * install, then per-client setup for repeat use. Every command, endpoint,
424
+ * deeplink, and config here is derived from the same canonical constants as
425
+ * the plugin tree and the agent card, so this document can never drift from
426
+ * what we ship. Keep it short enough that an agent reads it whole; the deep
427
+ * contract stays in aiInstructionsText() (/ai.txt).
428
+ */
429
+ export function agentSetupMarkdown() {
430
+ const mcpEndpoint = serviceOrigin("mcp", "prod");
431
+ const dropUrl = `${serviceOrigin("dashboard", "prod")}/drop`;
432
+ return [
433
+ `# ${BRAND.productName} agent setup`,
434
+ "",
435
+ `These are official instructions from ${BRAND.productName} for AI agents. Follow them to publish the user's file, folder, site, or app to a live URL — then, optionally, set up your own client so the next publish is one word. Every command on this page is validated by ${BRAND.productName} and ready to run.`,
436
+ "",
437
+ "Talk to the user before publishing. Confirm the file or folder they want to publish; if it isn't obvious, ask.",
438
+ "",
439
+ "## Before you publish — reuse an existing space",
440
+ "",
441
+ CHECK_BEFORE_PUBLISH_PROSE,
442
+ "",
443
+ "## Publish now — no install, no account",
444
+ "",
445
+ "Publish a single file with one POST:",
446
+ "",
447
+ "```bash",
448
+ anonymousPublishCurlCommand,
449
+ "```",
450
+ "",
451
+ "For a folder, zip the public output only — never a repository root, never `.env*`,",
452
+ "credentials, `.git`, or `.spacefast` — and send it as `-F archive=@site.zip` to the same",
453
+ "endpoint. Fetch the full contract when a publish goes beyond the happy path:",
454
+ `${agentSetupTextUrl}.`,
455
+ "",
456
+ "The JSON receipt is the result:",
457
+ "",
458
+ "- `data.space.liveUrl` — the live URL. Lead the reply with it.",
459
+ "- `data.version.immutableUrl` — the permanent address of this version.",
460
+ "- `data.claim.url` and `data.claim.expiresAt` — an anonymous space expires unless claimed.",
461
+ " Always show the claim link and its deadline.",
462
+ "- `data.claim.token` — a secret. Use it as the bearer to update the same `spaceId`;",
463
+ " never print it.",
464
+ "",
465
+ "## If you can't act on this page",
466
+ "",
467
+ "Reading this but unable to run commands or make HTTP requests — no shell, no",
468
+ "fetch-and-POST tool? Then this surface can't publish. Say that plainly and tell the",
469
+ "user what unlocks it:",
470
+ "",
471
+ "- In ChatGPT: switch to Work mode or the desktop app (paid plans). The Free plan",
472
+ " can't publish.",
473
+ "- In any other client: enable its tool/agent mode, or pick a client from",
474
+ ` ${serviceOrigin("www", "prod")}/setup.`,
475
+ "",
476
+ "Don't fall back to generating files for the user to download and re-upload by hand",
477
+ "unless they ask for exactly that.",
478
+ "",
479
+ "## Set up your agent (optional, for repeat use)",
480
+ "",
481
+ 'Use the section for your client. If yours isn\'t listed, use "Every other agent".',
482
+ "",
483
+ "### Claude Code",
484
+ "",
485
+ "The plugin ships the publish skill plus MCP in one install:",
486
+ "",
487
+ "```bash",
488
+ spacefastClaudePluginAddCommand,
489
+ spacefastClaudePluginInstallCommand,
490
+ "```",
491
+ "",
492
+ "Restart Claude Code (or run `/reload-plugins`) to activate it. MCP-only alternative:",
493
+ "",
494
+ "```bash",
495
+ buildClaudeMcpAddCommand(mcpEndpoint),
496
+ "```",
497
+ "",
498
+ "### Codex",
499
+ "",
500
+ "```bash",
501
+ spacefastCodexPluginAddCommand,
502
+ spacefastCodexPluginInstallCommand,
503
+ "```",
504
+ "",
505
+ "Or teach every task in a repository with one AGENTS.md line:",
506
+ "",
507
+ "```text",
508
+ `To publish, fetch ${agentSetupUrl} and follow the instructions.`,
509
+ "```",
510
+ "",
511
+ "### Cursor",
512
+ "",
513
+ `One click: ${cursorHostedMcpDeeplink(mcpEndpoint)}`,
514
+ "",
515
+ "Or add to `~/.cursor/mcp.json`:",
516
+ "",
517
+ "```json",
518
+ JSON.stringify(cursorHostedMcpServersJson(mcpEndpoint), null, 2),
519
+ "```",
520
+ "",
521
+ "### VS Code",
522
+ "",
523
+ `One click: ${vscodeHostedMcpDeeplink(mcpEndpoint)}`,
524
+ "",
525
+ "Or add to `.vscode/mcp.json`:",
526
+ "",
527
+ "```json",
528
+ JSON.stringify(vscodeHostedMcpServersJson(mcpEndpoint), null, 2),
529
+ "```",
530
+ "",
531
+ "### Claude (web, desktop, and Cowork)",
532
+ "",
533
+ "Settings → Connectors → Add custom connector, paste the endpoint. Approve once in the",
534
+ "browser, nothing to install:",
535
+ "",
536
+ "```text",
537
+ mcpEndpoint,
538
+ "```",
539
+ "",
540
+ "On desktop, the extension is a double-click install:",
541
+ spacefastClaudeDesktopExtensionUrl,
542
+ "",
543
+ "### ChatGPT and ChatGPT Work",
544
+ "",
545
+ // TODO(plugin-directory): once the Spacefast listing is live, lead this
546
+ // section with the one-click Plugin Directory install instead.
547
+ "Publishing works from Work mode or the desktop app (paid plans) — follow the",
548
+ "publish-now section above; nothing to install. On ChatGPT Work, an admin can add",
549
+ "Spacefast once as a workspace app (Workspace settings → Apps) with the endpoint",
550
+ "below and deploy it to the whole team:",
551
+ "",
552
+ "```text",
553
+ mcpEndpoint,
554
+ "```",
555
+ "",
556
+ "If this ChatGPT can't run tools (Free plan, plain Chat), say so and point the user",
557
+ "at Work mode or the desktop app — see “If you can't act on this page”.",
558
+ "",
559
+ "### Every other agent (OpenClaw, Hermes, Raycast, Pi, OpenCode, Devin, Poke, Indent, …)",
560
+ "",
561
+ "Install the publish skill:",
562
+ "",
563
+ "```bash",
564
+ spacefastSkillInstallCommand,
565
+ "```",
566
+ "",
567
+ "Clients that speak MCP add the hosted server; sign-in happens once on first tool use.",
568
+ "Hermes — one command:",
569
+ "",
570
+ "```bash",
571
+ buildHermesMcpAddCommand(mcpEndpoint),
572
+ "```",
573
+ "",
574
+ "OpenCode V2 — merge this schema-pinned config into `~/.config/opencode/opencode.jsonc`:",
575
+ "",
576
+ "```json",
577
+ JSON.stringify(opencodeHostedMcpConfig(mcpEndpoint), null, 2),
578
+ "```",
579
+ "",
580
+ 'Any other MCP client, under `"mcpServers"`:',
581
+ "",
582
+ "```json",
583
+ `"spacefast": { "url": "${mcpEndpoint}" }`,
584
+ "```",
585
+ "",
586
+ `Chat agents without tools (Pi, Poke): create a self-contained index.html and upload it at ${dropUrl}.`,
587
+ "",
588
+ "## After publishing",
589
+ "",
590
+ "- Report the live URL first, then the immutable version URL, then the claim link with",
591
+ " its expiry.",
592
+ "- Persist the space id and credential so the next publish updates the same space instead",
593
+ " of creating a new one. The CLI and On-Device MCP do this automatically in `.spacefast/`.",
594
+ " Hosted MCP is stateless between publishes: retain the receipt's `spaceId` in conversation",
595
+ " or project context and pass it explicitly on the next publish.",
596
+ "- To update, post to the same endpoint with `--form-string 'spaceId=<spc_id>'` and the",
597
+ " saved bearer token.",
598
+ "- Recommend the matching setup above so the next publish is one word.",
599
+ "",
600
+ "## Resources",
601
+ "",
602
+ `- Full publishing contract for agents: ${agentSetupTextUrl}`,
603
+ `- One-POST publish spec (machine-readable): ${serviceOrigin("www", "prod")}/publish-spec.json`,
604
+ `- Docs index for agents: ${serviceOrigin("www", "prod")}/llms.txt`,
605
+ `- Human setup guides, per agent: ${serviceOrigin("www", "prod")}/setup`,
606
+ `- CLI (versions, rollback, domains, logs): \`${spacefastInstallCurlCommand}\` — Windows PowerShell: \`${spacefastInstallPowerShellCommand}\``,
607
+ "- Every API error code: https://spacefast.com/docs/errors",
608
+ "",
609
+ `These instructions are published at ${agentSetupUrl} so you can re-verify their authenticity at any time.`,
610
+ "",
611
+ ].join("\n");
612
+ }
@@ -52,9 +52,9 @@ export declare const agentSolutions: readonly [{
52
52
  readonly aliases: readonly ["chatgpt", "custom gpt", "openai apps"];
53
53
  readonly lane: "hosted-mcp";
54
54
  readonly summary: "Recommended: direct hosted MCP.";
55
- readonly description: "Connect ChatGPT-style cloud agents to Spacefast through hosted MCP; use the virtual workspace or signed uploads instead of local paths.";
55
+ readonly description: "Connect ChatGPT-style cloud agents to Spacefast through hosted MCP; use inline files or signed uploads instead of local paths.";
56
56
  readonly commands: readonly ["https://mcp.spacefast.com"];
57
- readonly guideUrl: `${string}/docs/agents`;
57
+ readonly guideUrl: `${string}/getting-started/agents`;
58
58
  readonly setupText: string;
59
59
  }, {
60
60
  readonly id: "claude-ai";
@@ -64,7 +64,7 @@ export declare const agentSolutions: readonly [{
64
64
  readonly summary: "Recommended: direct hosted MCP.";
65
65
  readonly description: "Connect the web Claude app to Spacefast through hosted MCP when it cannot read your local checkout.";
66
66
  readonly commands: readonly ["https://mcp.spacefast.com"];
67
- readonly guideUrl: `${string}/docs/agents`;
67
+ readonly guideUrl: `${string}/getting-started/agents`;
68
68
  readonly setupText: string;
69
69
  }, {
70
70
  readonly id: "raycast";
@@ -74,7 +74,7 @@ export declare const agentSolutions: readonly [{
74
74
  readonly summary: "Recommended: direct hosted MCP.";
75
75
  readonly description: "Use hosted MCP for Raycast and similar clients that can connect to a remote MCP server but should not need a local Spacefast install.";
76
76
  readonly commands: readonly ["https://mcp.spacefast.com"];
77
- readonly guideUrl: `${string}/docs/agents`;
77
+ readonly guideUrl: `${string}/getting-started/agents`;
78
78
  readonly setupText: string;
79
79
  }, {
80
80
  readonly id: "cursor";
@@ -84,7 +84,7 @@ export declare const agentSolutions: readonly [{
84
84
  readonly summary: "Recommended: local MCP when it needs the checkout.";
85
85
  readonly description: "Use On-Device MCP for editor agents that need local files; use hosted MCP instead when they only need cloud-safe tools.";
86
86
  readonly commands: readonly ["sf setup agent --agent auto --connect", "sf mcp install --agent auto --mode local --transport stdio --connect"];
87
- readonly guideUrl: `${string}/docs/agents`;
87
+ readonly guideUrl: `${string}/getting-started/agents`;
88
88
  readonly setupText: string;
89
89
  }, {
90
90
  readonly id: "terminal";
@@ -94,7 +94,7 @@ export declare const agentSolutions: readonly [{
94
94
  readonly summary: "Recommended: Spacefast CLI.";
95
95
  readonly description: "Use the CLI for repeat publishes, CI jobs, JSON output, rollback, logs, and domains.";
96
96
  readonly commands: readonly [string, "sf login", "sf publish {file-or-dir} --json"];
97
- readonly guideUrl: `${string}/docs/cli`;
97
+ readonly guideUrl: `${string}/cli`;
98
98
  readonly setupText: string;
99
99
  }, {
100
100
  readonly id: "generic";
@@ -1,7 +1,9 @@
1
1
  import { BRAND } from "../brand.js";
2
+ import { DOCS_BASE_URL, serviceOrigin } from "../config/domains.js";
2
3
  import { agentSetupPrompt, agentSetupTextUrl, agentSetupUrl } from "./agent-setup.js";
3
4
  import { spacefastClaudePluginAddCommand, spacefastClaudePluginInstallCommand, spacefastCodexSkillInstallCommand, spacefastInstallCurlCommand, } from "./skill-distribution.js";
4
5
  export const hostedMcpEndpointUrl = "https://mcp.spacefast.com";
6
+ const WEBSITE_ORIGIN = serviceOrigin("www", "prod");
5
7
  function pluginText(label, commands, guideUrl) {
6
8
  return [
7
9
  `Set up ${BRAND.productName} for ${label}.`,
@@ -24,7 +26,7 @@ function hostedMcpText(label, guideUrl) {
24
26
  "Minimum scope: mcp:tools",
25
27
  "Common scopes: teams:read teams:create spaces:read spaces:write spaces:rename publish:write domains:read domains:write",
26
28
  "",
27
- "Hosted MCP is for cloud/browser agents. It does not read local paths; use the hosted workspace tools or signed uploads for artifacts.",
29
+ "Hosted MCP is for cloud/browser agents. Publish small generated files inline or use signed uploads for larger artifacts; it has no filesystem.",
28
30
  `Guide: ${guideUrl}`,
29
31
  `Plain-text setup contract: ${agentSetupTextUrl}`,
30
32
  ].join("\n");
@@ -51,9 +53,9 @@ export const agentSolutions = [
51
53
  summary: "Recommended: Codex plugin.",
52
54
  description: `Install the ${BRAND.productName} skill/plugin so Codex can publish, update, and inspect spaces from the same setup path.`,
53
55
  commands: [spacefastCodexSkillInstallCommand],
54
- guideUrl: `${BRAND.websiteOrigin}/for-codex`,
55
- detailGuideUrl: `${BRAND.websiteOrigin}/guides/how-to-use-with-codex`,
56
- setupText: pluginText("Codex", [spacefastCodexSkillInstallCommand], `${BRAND.websiteOrigin}/for-codex`),
56
+ guideUrl: `${WEBSITE_ORIGIN}/for-codex`,
57
+ detailGuideUrl: `${WEBSITE_ORIGIN}/guides/how-to-use-with-codex`,
58
+ setupText: pluginText("Codex", [spacefastCodexSkillInstallCommand], `${WEBSITE_ORIGIN}/for-codex`),
57
59
  },
58
60
  {
59
61
  id: "claude-code",
@@ -63,9 +65,9 @@ export const agentSolutions = [
63
65
  summary: "Recommended: Claude plugin.",
64
66
  description: `Install the ${BRAND.productName} Claude plugin once, then Claude Code can publish and operate spaces from the terminal.`,
65
67
  commands: [spacefastClaudePluginAddCommand, spacefastClaudePluginInstallCommand],
66
- guideUrl: `${BRAND.websiteOrigin}/for-claude`,
67
- detailGuideUrl: `${BRAND.websiteOrigin}/guides/how-to-use-with-claude-code`,
68
- setupText: pluginText("Claude Code", [spacefastClaudePluginAddCommand, spacefastClaudePluginInstallCommand], `${BRAND.websiteOrigin}/guides/how-to-use-with-claude-code`),
68
+ guideUrl: `${WEBSITE_ORIGIN}/for-claude`,
69
+ detailGuideUrl: `${WEBSITE_ORIGIN}/guides/how-to-use-with-claude-code`,
70
+ setupText: pluginText("Claude Code", [spacefastClaudePluginAddCommand, spacefastClaudePluginInstallCommand], `${WEBSITE_ORIGIN}/guides/how-to-use-with-claude-code`),
69
71
  },
70
72
  {
71
73
  id: "claude-cowork",
@@ -75,9 +77,9 @@ export const agentSolutions = [
75
77
  summary: "Recommended: Claude plugin.",
76
78
  description: `Use the ${BRAND.productName} Claude plugin path so Cowork gets the same publish instructions and MCP-aware setup as Claude Code.`,
77
79
  commands: [spacefastClaudePluginAddCommand, spacefastClaudePluginInstallCommand],
78
- guideUrl: `${BRAND.websiteOrigin}/for-claude`,
79
- detailGuideUrl: `${BRAND.websiteOrigin}/guides/how-to-use-with-claude-cowork`,
80
- setupText: pluginText("Claude Cowork", [spacefastClaudePluginAddCommand, spacefastClaudePluginInstallCommand, agentSetupPrompt], `${BRAND.websiteOrigin}/guides/how-to-use-with-claude-cowork`),
80
+ guideUrl: `${WEBSITE_ORIGIN}/for-claude`,
81
+ detailGuideUrl: `${WEBSITE_ORIGIN}/guides/how-to-use-with-claude-cowork`,
82
+ setupText: pluginText("Claude Cowork", [spacefastClaudePluginAddCommand, spacefastClaudePluginInstallCommand, agentSetupPrompt], `${WEBSITE_ORIGIN}/guides/how-to-use-with-claude-cowork`),
81
83
  },
82
84
  {
83
85
  id: "chatgpt",
@@ -85,10 +87,10 @@ export const agentSolutions = [
85
87
  aliases: ["chatgpt", "custom gpt", "openai apps"],
86
88
  lane: "hosted-mcp",
87
89
  summary: "Recommended: direct hosted MCP.",
88
- description: `Connect ChatGPT-style cloud agents to ${BRAND.productName} through hosted MCP; use the virtual workspace or signed uploads instead of local paths.`,
90
+ description: `Connect ChatGPT-style cloud agents to ${BRAND.productName} through hosted MCP; use inline files or signed uploads instead of local paths.`,
89
91
  commands: [hostedMcpEndpointUrl],
90
- guideUrl: `${BRAND.websiteOrigin}/docs/agents`,
91
- setupText: hostedMcpText("ChatGPT", `${BRAND.websiteOrigin}/docs/agents`),
92
+ guideUrl: `${DOCS_BASE_URL}/getting-started/agents`,
93
+ setupText: hostedMcpText("ChatGPT", `${DOCS_BASE_URL}/getting-started/agents`),
92
94
  },
93
95
  {
94
96
  id: "claude-ai",
@@ -98,8 +100,8 @@ export const agentSolutions = [
98
100
  summary: "Recommended: direct hosted MCP.",
99
101
  description: `Connect the web Claude app to ${BRAND.productName} through hosted MCP when it cannot read your local checkout.`,
100
102
  commands: [hostedMcpEndpointUrl],
101
- guideUrl: `${BRAND.websiteOrigin}/docs/agents`,
102
- setupText: hostedMcpText("Claude.ai", `${BRAND.websiteOrigin}/docs/agents`),
103
+ guideUrl: `${DOCS_BASE_URL}/getting-started/agents`,
104
+ setupText: hostedMcpText("Claude.ai", `${DOCS_BASE_URL}/getting-started/agents`),
103
105
  },
104
106
  {
105
107
  id: "raycast",
@@ -109,8 +111,8 @@ export const agentSolutions = [
109
111
  summary: "Recommended: direct hosted MCP.",
110
112
  description: `Use hosted MCP for Raycast and similar clients that can connect to a remote MCP server but should not need a local ${BRAND.productName} install.`,
111
113
  commands: [hostedMcpEndpointUrl],
112
- guideUrl: `${BRAND.websiteOrigin}/docs/agents`,
113
- setupText: hostedMcpText("Raycast", `${BRAND.websiteOrigin}/docs/agents`),
114
+ guideUrl: `${DOCS_BASE_URL}/getting-started/agents`,
115
+ setupText: hostedMcpText("Raycast", `${DOCS_BASE_URL}/getting-started/agents`),
114
116
  },
115
117
  {
116
118
  id: "cursor",
@@ -123,8 +125,8 @@ export const agentSolutions = [
123
125
  "sf setup agent --agent auto --connect",
124
126
  "sf mcp install --agent auto --mode local --transport stdio --connect",
125
127
  ],
126
- guideUrl: `${BRAND.websiteOrigin}/docs/agents`,
127
- setupText: localMcpText("Cursor or another MCP client", `${BRAND.websiteOrigin}/docs/agents`),
128
+ guideUrl: `${DOCS_BASE_URL}/getting-started/agents`,
129
+ setupText: localMcpText("Cursor or another MCP client", `${DOCS_BASE_URL}/getting-started/agents`),
128
130
  },
129
131
  {
130
132
  id: "terminal",
@@ -134,12 +136,12 @@ export const agentSolutions = [
134
136
  summary: `Recommended: ${BRAND.productName} CLI.`,
135
137
  description: "Use the CLI for repeat publishes, CI jobs, JSON output, rollback, logs, and domains.",
136
138
  commands: [spacefastInstallCurlCommand, "sf login", "sf publish {file-or-dir} --json"],
137
- guideUrl: `${BRAND.websiteOrigin}/docs/cli`,
139
+ guideUrl: `${DOCS_BASE_URL}/cli`,
138
140
  setupText: [
139
141
  `Install CLI: ${spacefastInstallCurlCommand}`,
140
142
  "Login: sf login",
141
143
  "Publish: sf publish {file-or-dir} --json",
142
- `Guide: ${BRAND.websiteOrigin}/docs/cli`,
144
+ `Guide: ${DOCS_BASE_URL}/cli`,
143
145
  ].join("\n"),
144
146
  },
145
147
  {