@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.
Files changed (320) hide show
  1. package/brand-assets/LICENSE-Haskoy.txt +93 -0
  2. package/brand-assets/haskoy-latin-variable.woff2 +0 -0
  3. package/brand-assets/spacefast-favicon.svg +4 -4
  4. package/brand-assets/spacefast-sf-full-bleed.svg +3 -3
  5. package/brand-assets/spacefast-wordmark.svg +2 -4
  6. package/dist/agents/connect-targets.d.ts +6 -146
  7. package/dist/agents/connect-targets.js +63 -295
  8. package/dist/agents/private-key-oauth.d.ts +31 -2
  9. package/dist/agents/private-key-oauth.js +166 -92
  10. package/dist/agents/registry/core.d.ts +292 -0
  11. package/dist/agents/registry/core.js +1388 -0
  12. package/dist/agents/registry/deeplink-probes.d.ts +23 -0
  13. package/dist/agents/registry/deeplink-probes.js +18 -0
  14. package/dist/agents/registry/node.d.ts +56 -0
  15. package/dist/agents/registry/node.js +339 -0
  16. package/dist/brand-assets-build.js +3 -6
  17. package/dist/brand-assets.d.ts +5 -15
  18. package/dist/brand-assets.js +12 -14
  19. package/dist/brand.d.ts +17 -0
  20. package/dist/brand.js +24 -4
  21. package/dist/config/domains.d.ts +79 -12
  22. package/dist/config/domains.js +128 -29
  23. package/dist/config/index.js +1 -2
  24. package/dist/contracts/abuse.js +9 -24
  25. package/dist/contracts/access-profiles.d.ts +86 -0
  26. package/dist/contracts/access-profiles.js +89 -0
  27. package/dist/contracts/access.d.ts +24 -30
  28. package/dist/contracts/access.js +33 -64
  29. package/dist/contracts/activity.d.ts +18 -1
  30. package/dist/contracts/activity.js +72 -26
  31. package/dist/contracts/analytics.js +8 -10
  32. package/dist/contracts/api-keys.d.ts +77 -52
  33. package/dist/contracts/api-keys.js +44 -24
  34. package/dist/contracts/application-journal.d.ts +216 -0
  35. package/dist/contracts/application-journal.js +167 -0
  36. package/dist/contracts/auth.d.ts +90 -17
  37. package/dist/contracts/auth.js +86 -17
  38. package/dist/contracts/beta.d.ts +9 -4
  39. package/dist/contracts/beta.js +20 -16
  40. package/dist/contracts/billing.d.ts +2 -1
  41. package/dist/contracts/billing.js +37 -11
  42. package/dist/contracts/bootstrap.d.ts +791 -0
  43. package/dist/contracts/bootstrap.js +51 -0
  44. package/dist/contracts/builds.d.ts +209 -18
  45. package/dist/contracts/builds.js +93 -27
  46. package/dist/contracts/channels.d.ts +43 -0
  47. package/dist/contracts/channels.js +25 -9
  48. package/dist/contracts/cli.d.ts +7 -0
  49. package/dist/contracts/cli.js +6 -0
  50. package/dist/contracts/collab-public.d.ts +1 -1
  51. package/dist/contracts/collab-public.js +5 -8
  52. package/dist/contracts/comments.d.ts +44 -34
  53. package/dist/contracts/comments.js +107 -77
  54. package/dist/contracts/commerce.d.ts +165 -0
  55. package/dist/contracts/commerce.js +146 -0
  56. package/dist/contracts/common.d.ts +3 -3
  57. package/dist/contracts/common.js +20 -31
  58. package/dist/contracts/content-contract-verification.d.ts +521 -0
  59. package/dist/contracts/content-contract-verification.js +320 -0
  60. package/dist/contracts/content-platform-fixture.d.ts +1278 -0
  61. package/dist/contracts/content-platform-fixture.js +44 -0
  62. package/dist/contracts/content-program.d.ts +994 -0
  63. package/dist/contracts/content-program.js +658 -0
  64. package/dist/contracts/content-sync.d.ts +290 -0
  65. package/dist/contracts/content-sync.js +167 -0
  66. package/dist/contracts/content.d.ts +432 -0
  67. package/dist/contracts/content.js +303 -0
  68. package/dist/contracts/continuation.d.ts +17 -1
  69. package/dist/contracts/continuation.js +23 -23
  70. package/dist/contracts/countries.js +3 -4
  71. package/dist/contracts/crons.d.ts +52 -0
  72. package/dist/contracts/crons.js +60 -0
  73. package/dist/contracts/dashboard-prefs.d.ts +56 -0
  74. package/dist/contracts/dashboard-prefs.js +51 -0
  75. package/dist/contracts/device-auth.d.ts +234 -37
  76. package/dist/contracts/device-auth.js +267 -53
  77. package/dist/contracts/docs.d.ts +1 -5
  78. package/dist/contracts/docs.js +7 -15
  79. package/dist/contracts/domains.d.ts +501 -93
  80. package/dist/contracts/domains.js +193 -55
  81. package/dist/contracts/email-preferences.d.ts +3 -3
  82. package/dist/contracts/email-preferences.js +6 -7
  83. package/dist/contracts/enums.d.ts +27 -26
  84. package/dist/contracts/enums.js +68 -61
  85. package/dist/contracts/error-code-meta.d.ts +283 -7
  86. package/dist/contracts/error-code-meta.js +97 -5
  87. package/dist/contracts/error-codes.d.ts +5 -5
  88. package/dist/contracts/error-codes.js +82 -17
  89. package/dist/contracts/events.d.ts +100 -20
  90. package/dist/contracts/events.js +107 -20
  91. package/dist/contracts/execution.d.ts +48 -64
  92. package/dist/contracts/execution.js +52 -62
  93. package/dist/contracts/feature-lifecycle.d.ts +73 -27
  94. package/dist/contracts/feature-lifecycle.js +86 -26
  95. package/dist/contracts/features.d.ts +68 -19
  96. package/dist/contracts/features.js +42 -41
  97. package/dist/contracts/fixtures/content-platform-v1.json +1108 -0
  98. package/dist/contracts/frame-session.d.ts +192 -0
  99. package/dist/contracts/frame-session.js +164 -0
  100. package/dist/contracts/functions.d.ts +338 -164
  101. package/dist/contracts/functions.js +367 -198
  102. package/dist/contracts/git.d.ts +27 -33
  103. package/dist/contracts/git.js +10 -11
  104. package/dist/contracts/grant-copy.d.ts +43 -0
  105. package/dist/contracts/grant-copy.js +44 -0
  106. package/dist/contracts/grants.d.ts +62 -54
  107. package/dist/contracts/grants.js +124 -23
  108. package/dist/contracts/ids.d.ts +4 -1
  109. package/dist/contracts/ids.js +6 -3
  110. package/dist/contracts/internal.d.ts +8 -18
  111. package/dist/contracts/internal.js +13 -14
  112. package/dist/contracts/mcp.d.ts +28 -0
  113. package/dist/contracts/mcp.js +28 -0
  114. package/dist/contracts/me.d.ts +6 -1
  115. package/dist/contracts/me.js +12 -6
  116. package/dist/contracts/notifications.d.ts +27 -29
  117. package/dist/contracts/notifications.js +9 -4
  118. package/dist/contracts/oauth-resources.d.ts +112 -10
  119. package/dist/contracts/oauth-resources.js +116 -11
  120. package/dist/contracts/oauth-scope-actions.d.ts +90 -0
  121. package/dist/contracts/oauth-scope-actions.js +126 -0
  122. package/dist/contracts/operations.d.ts +27 -4
  123. package/dist/contracts/operations.js +12 -6
  124. package/dist/contracts/pages.d.ts +31 -7
  125. package/dist/contracts/pages.js +22 -5
  126. package/dist/contracts/partner-notifications.d.ts +3 -0
  127. package/dist/contracts/partner-notifications.js +65 -0
  128. package/dist/contracts/{platform.d.ts → partner.d.ts} +29 -42
  129. package/dist/contracts/{platform.js → partner.js} +27 -40
  130. package/dist/contracts/plan-policy.js +1 -1
  131. package/dist/contracts/principal-assertion.d.ts +43 -0
  132. package/dist/contracts/principal-assertion.js +67 -0
  133. package/dist/contracts/principals.d.ts +73 -0
  134. package/dist/contracts/principals.js +59 -0
  135. package/dist/contracts/privacy.d.ts +11 -0
  136. package/dist/contracts/privacy.js +17 -0
  137. package/dist/contracts/problem-document.js +3 -4
  138. package/dist/contracts/publish-archive.d.ts +269 -161
  139. package/dist/contracts/publish-archive.js +11 -1
  140. package/dist/contracts/push-new.d.ts +8 -8
  141. package/dist/contracts/quotas.js +5 -5
  142. package/dist/contracts/realtime.d.ts +52 -0
  143. package/dist/contracts/realtime.js +54 -0
  144. package/dist/contracts/repository-connections.d.ts +44 -31
  145. package/dist/contracts/repository-connections.js +17 -5
  146. package/dist/contracts/resources.d.ts +105 -14
  147. package/dist/contracts/resources.js +150 -26
  148. package/dist/contracts/route-inventory.d.ts +358 -0
  149. package/dist/contracts/route-inventory.js +141 -0
  150. package/dist/contracts/runtime-api.d.ts +206 -82
  151. package/dist/contracts/runtime-api.js +131 -70
  152. package/dist/contracts/runtime-app.d.ts +13 -23
  153. package/dist/contracts/runtime-app.js +39 -24
  154. package/dist/contracts/runtime-components.d.ts +143 -0
  155. package/dist/contracts/runtime-components.js +199 -0
  156. package/dist/contracts/runtime-db.d.ts +49 -23
  157. package/dist/contracts/runtime-db.js +87 -22
  158. package/dist/contracts/runtime-purge.d.ts +8 -0
  159. package/dist/contracts/runtime-purge.js +5 -0
  160. package/dist/contracts/runtime-services.d.ts +43 -75
  161. package/dist/contracts/runtime-services.js +29 -49
  162. package/dist/contracts/runtime-storage.d.ts +15 -14
  163. package/dist/contracts/runtime-storage.js +30 -14
  164. package/dist/contracts/sf-config-v1.d.ts +48 -14
  165. package/dist/contracts/slugs.js +13 -4
  166. package/dist/contracts/space-config.d.ts +283 -19
  167. package/dist/contracts/space-config.js +430 -80
  168. package/dist/contracts/spaces.d.ts +376 -290
  169. package/dist/contracts/spaces.js +200 -135
  170. package/dist/contracts/superadmin-activity.d.ts +3 -43
  171. package/dist/contracts/superadmin-activity.js +6 -30
  172. package/dist/contracts/superadmin-emails.d.ts +1 -0
  173. package/dist/contracts/superadmin-emails.js +17 -1
  174. package/dist/contracts/superadmin-runtime.d.ts +199 -0
  175. package/dist/contracts/superadmin-runtime.js +141 -0
  176. package/dist/contracts/superadmin-search.d.ts +22 -2
  177. package/dist/contracts/superadmin-search.js +14 -6
  178. package/dist/contracts/superadmin-spaces.d.ts +438 -189
  179. package/dist/contracts/superadmin-spaces.js +131 -25
  180. package/dist/contracts/superadmin-teams.d.ts +5 -2
  181. package/dist/contracts/superadmin-teams.js +1 -2
  182. package/dist/contracts/superadmin-tenants.d.ts +627 -12
  183. package/dist/contracts/superadmin-tenants.js +32 -13
  184. package/dist/contracts/superadmin.d.ts +232 -37
  185. package/dist/contracts/superadmin.js +106 -22
  186. package/dist/contracts/tags.d.ts +85 -58
  187. package/dist/contracts/tags.js +47 -37
  188. package/dist/contracts/teams.d.ts +86 -26
  189. package/dist/contracts/teams.js +90 -31
  190. package/dist/contracts/tenants.d.ts +135 -0
  191. package/dist/contracts/tenants.js +120 -0
  192. package/dist/contracts/test-triggers.d.ts +70 -0
  193. package/dist/contracts/test-triggers.js +116 -0
  194. package/dist/contracts/theme-json.js +3 -8
  195. package/dist/contracts/transfers.d.ts +1 -0
  196. package/dist/contracts/transfers.js +3 -2
  197. package/dist/contracts/usage.d.ts +209 -0
  198. package/dist/contracts/usage.js +124 -12
  199. package/dist/contracts/variables.d.ts +2 -2
  200. package/dist/contracts/variables.js +9 -13
  201. package/dist/contracts/webhooks.d.ts +13 -5
  202. package/dist/contracts/webhooks.js +3 -1
  203. package/dist/contracts/zero.d.ts +72 -81
  204. package/dist/contracts/zero.js +80 -78
  205. package/dist/dashboard-paths/index.d.ts +24 -21
  206. package/dist/dashboard-paths/index.js +35 -47
  207. package/dist/dashboard-paths/route-reservations.generated.d.ts +2 -0
  208. package/dist/dashboard-paths/route-reservations.generated.js +36 -0
  209. package/dist/docs/agent-arrival.d.ts +92 -0
  210. package/dist/docs/agent-arrival.js +227 -0
  211. package/dist/docs/agent-output-policy.d.ts +6 -0
  212. package/dist/docs/agent-output-policy.js +22 -0
  213. package/dist/docs/agent-prose.d.ts +12 -18
  214. package/dist/docs/agent-prose.js +73 -51
  215. package/dist/docs/agent-setup.d.ts +72 -41
  216. package/dist/docs/agent-setup.js +339 -436
  217. package/dist/docs/catalog.d.ts +153 -95
  218. package/dist/docs/catalog.js +163 -48
  219. package/dist/docs/error-docs.d.ts +2098 -1
  220. package/dist/docs/error-docs.js +421 -144
  221. package/dist/docs/index-build.d.ts +0 -1
  222. package/dist/docs/index-build.js +2 -3
  223. package/dist/docs/page-context-prompt.d.ts +77 -0
  224. package/dist/docs/page-context-prompt.js +131 -0
  225. package/dist/docs/reference-urls.d.ts +46 -0
  226. package/dist/docs/reference-urls.js +79 -0
  227. package/dist/docs/search.d.ts +6 -8
  228. package/dist/docs/search.js +10 -13
  229. package/dist/docs/skill-distribution.d.ts +4 -1
  230. package/dist/docs/skill-distribution.js +10 -5
  231. package/dist/docs/skills.d.ts +120 -120
  232. package/dist/docs/skills.js +121 -73
  233. package/dist/docs/start-prompts.d.ts +45 -0
  234. package/dist/docs/start-prompts.js +62 -0
  235. package/dist/domain-exploration.js +9 -13
  236. package/dist/og-template.d.ts +56 -0
  237. package/dist/og-template.js +56 -0
  238. package/dist/slug-policy/index.js +5 -11
  239. package/dist/test-helpers/fetch-stub.js +1 -0
  240. package/dist/utils/browser-credential.d.ts +13 -6
  241. package/dist/utils/browser-credential.js +14 -7
  242. package/dist/utils/build-settings.d.ts +21 -1
  243. package/dist/utils/build-settings.js +133 -15
  244. package/dist/utils/canonical-json.d.ts +3 -5
  245. package/dist/utils/canonical-json.js +4 -5
  246. package/dist/utils/cast-transport.d.ts +5 -17
  247. package/dist/utils/cast-transport.js +6 -19
  248. package/dist/utils/claim-token.js +1 -2
  249. package/dist/utils/comment-avatars.js +4 -6
  250. package/dist/utils/concurrency.js +3 -3
  251. package/dist/utils/content-type.d.ts +7 -6
  252. package/dist/utils/content-type.js +73 -51
  253. package/dist/utils/credential-policy.d.ts +50 -6
  254. package/dist/utils/credential-policy.js +126 -59
  255. package/dist/utils/dns-instructions.js +9 -9
  256. package/dist/utils/egress-policy.fixtures.json +1 -1
  257. package/dist/utils/error-code.js +1 -0
  258. package/dist/utils/generate-space-name.d.ts +3 -8
  259. package/dist/utils/generate-space-name.js +3 -8
  260. package/dist/utils/grant-decision.fixtures.json +2 -2
  261. package/dist/utils/grants.d.ts +0 -1
  262. package/dist/utils/grants.js +7 -8
  263. package/dist/utils/gravatar.d.ts +2 -4
  264. package/dist/utils/gravatar.js +6 -12
  265. package/dist/utils/id-hints.js +14 -23
  266. package/dist/utils/idempotency.d.ts +3 -3
  267. package/dist/utils/idempotency.js +6 -7
  268. package/dist/utils/local-space-state.d.ts +23 -26
  269. package/dist/utils/local-space-state.js +46 -46
  270. package/dist/utils/oauth-signed-query.d.ts +3 -5
  271. package/dist/utils/oauth-signed-query.js +6 -9
  272. package/dist/utils/one-shot-replay.d.ts +7 -6
  273. package/dist/utils/one-shot-replay.js +3 -5
  274. package/dist/utils/page-colors.d.ts +15 -19
  275. package/dist/utils/page-colors.js +23 -45
  276. package/dist/utils/page-fonts.d.ts +1 -1
  277. package/dist/utils/page-fonts.js +8 -20
  278. package/dist/utils/page-preview.d.ts +0 -1
  279. package/dist/utils/page-preview.js +0 -1
  280. package/dist/utils/pages.d.ts +33 -3
  281. package/dist/utils/pages.js +83 -30
  282. package/dist/utils/privacy-regions.d.ts +19 -0
  283. package/dist/utils/privacy-regions.js +91 -0
  284. package/dist/utils/publish-form-data.d.ts +4 -5
  285. package/dist/utils/publish-form-data.js +2 -3
  286. package/dist/utils/publish-policy.d.ts +11 -17
  287. package/dist/utils/publish-policy.fixtures.json +11 -5
  288. package/dist/utils/publish-policy.js +65 -110
  289. package/dist/utils/query-client.d.ts +2 -0
  290. package/dist/utils/query-client.js +10 -1
  291. package/dist/utils/runtime-paths.d.ts +4 -4
  292. package/dist/utils/runtime-paths.js +6 -8
  293. package/dist/utils/runtime-upload.d.ts +9 -0
  294. package/dist/utils/runtime-upload.js +12 -0
  295. package/dist/utils/secure-local-file.d.ts +3 -3
  296. package/dist/utils/secure-local-file.js +23 -40
  297. package/dist/utils/space-config.d.ts +12 -15
  298. package/dist/utils/space-config.js +13 -16
  299. package/dist/utils/static-runtime-policy.d.ts +10 -7
  300. package/dist/utils/static-runtime-policy.fixtures.json +38 -41
  301. package/dist/utils/static-runtime-policy.generated.d.ts +16 -0
  302. package/dist/utils/static-runtime-policy.generated.js +70 -0
  303. package/dist/utils/static-runtime-policy.js +31 -132
  304. package/dist/utils/storage-policy.js +1 -2
  305. package/dist/utils/upload-session.d.ts +4 -1
  306. package/dist/utils/upload-session.js +41 -19
  307. package/dist/utils/version-path.js +11 -19
  308. package/dist/utils/wpcom-auth-redirects.d.ts +5 -14
  309. package/dist/utils/wpcom-auth-redirects.js +1 -4
  310. package/dist/vocabulary.d.ts +13 -13
  311. package/dist/vocabulary.js +52 -27
  312. package/package.json +15 -18
  313. package/dist/agents/client-registry.d.ts +0 -22
  314. package/dist/agents/client-registry.js +0 -41
  315. package/dist/docs/agent-handoff-document.d.ts +0 -15
  316. package/dist/docs/agent-handoff-document.js +0 -180
  317. package/dist/docs/agent-solutions.d.ts +0 -110
  318. package/dist/docs/agent-solutions.js +0 -183
  319. package/dist/utils/query-keys.d.ts +0 -84
  320. package/dist/utils/query-keys.js +0 -108
@@ -1,57 +1,92 @@
1
- import { randomUUID, webcrypto } from "node:crypto";
1
+ import { webcrypto } from "node:crypto";
2
+ import * as oauth from "oauth4webapi";
3
+ import { z } from "zod";
2
4
  import { OAUTH_API_RESOURCE_SCOPES, oauthApiResourceUrl } from "../contracts/oauth-resources.js";
3
- const PRIVATE_KEY_JWT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
4
5
  const API_RESOURCE_SCOPES = new Set(OAUTH_API_RESOURCE_SCOPES);
5
6
  const INVALID_PRIVATE_KEY_MESSAGE = "SPACEFAST_AGENT_CONFIG private_jwk must be an ES256 P-256 JWK.";
6
- function isRecord(value) {
7
- return value !== null && typeof value === "object" && !Array.isArray(value);
8
- }
9
- function isJwkObject(value) {
10
- // Keep parsing permissive here. Web Crypto remains the authority on the JWK's
11
- // individual fields, matching the callers' previous behavior.
12
- return value !== null && typeof value === "object";
13
- }
14
- function readJsonObject(value) {
7
+ const parsedAgentPrivateJwkSchema = z.object({
8
+ alg: z.string().optional(),
9
+ crv: z.string().optional(),
10
+ d: z.string().optional(),
11
+ ext: z.boolean().optional(),
12
+ key_ops: z.array(z.string()).optional(),
13
+ kid: z.string().optional(),
14
+ kty: z.string().optional(),
15
+ use: z.string().optional(),
16
+ x: z.string().optional(),
17
+ y: z.string().optional(),
18
+ });
19
+ const agentPrivateJwkSchema = parsedAgentPrivateJwkSchema.transform((parsed) => {
20
+ const jwk = {};
21
+ if (parsed.alg !== undefined)
22
+ jwk.alg = parsed.alg;
23
+ if (parsed.crv !== undefined)
24
+ jwk.crv = parsed.crv;
25
+ if (parsed.d !== undefined)
26
+ jwk.d = parsed.d;
27
+ if (parsed.ext !== undefined)
28
+ jwk.ext = parsed.ext;
29
+ if (parsed.key_ops !== undefined)
30
+ jwk.key_ops = parsed.key_ops;
31
+ if (parsed.kid !== undefined)
32
+ jwk.kid = parsed.kid;
33
+ if (parsed.kty !== undefined)
34
+ jwk.kty = parsed.kty;
35
+ if (parsed.use !== undefined)
36
+ jwk.use = parsed.use;
37
+ if (parsed.x !== undefined)
38
+ jwk.x = parsed.x;
39
+ if (parsed.y !== undefined)
40
+ jwk.y = parsed.y;
41
+ return jwk;
42
+ });
43
+ const bundledAgentAuthConfigSchema = z.object({
44
+ client_id: z.string().optional(),
45
+ token_endpoint: z.string().optional(),
46
+ private_jwk: agentPrivateJwkSchema.optional(),
47
+ scope: z.string().optional(),
48
+ resources: z.array(z.string()).optional(),
49
+ });
50
+ function readBundledAgentAuthConfig(value) {
15
51
  if (!value?.trim()) {
16
52
  return null;
17
53
  }
18
- const parsed = JSON.parse(value);
19
- return isRecord(parsed) ? parsed : null;
54
+ return bundledAgentAuthConfigSchema.parse(JSON.parse(value));
20
55
  }
21
- function stringArray(value) {
22
- return Array.isArray(value)
23
- ? value.filter((entry) => typeof entry === "string")
24
- : undefined;
56
+ function readPrivateJwk(value) {
57
+ if (!value?.trim()) {
58
+ return null;
59
+ }
60
+ return agentPrivateJwkSchema.parse(JSON.parse(value));
25
61
  }
26
- /** Parse private-key agent credentials using the established bundled-over-split env precedence. */
62
+ /** Parse private-key agent credentials using the bundled-over-split env precedence. */
27
63
  export function readAgentAuthConfig(env, apiUrl) {
28
- const bundled = readJsonObject(env.SPACEFAST_AGENT_CONFIG);
29
- const bundledPrivateJwk = bundled?.private_jwk;
30
- const privateJwk = isJwkObject(bundledPrivateJwk)
31
- ? bundledPrivateJwk
32
- : readJsonObject(env.SPACEFAST_AGENT_PRIVATE_JWK);
33
- const clientId = (typeof bundled?.client_id === "string" ? bundled.client_id : undefined) ??
34
- env.SPACEFAST_AGENT_CLIENT_ID;
64
+ const bundled = readBundledAgentAuthConfig(env.SPACEFAST_AGENT_CONFIG);
65
+ const privateJwk = bundled?.private_jwk ?? readPrivateJwk(env.SPACEFAST_AGENT_PRIVATE_JWK);
66
+ const clientId = bundled?.client_id ?? env.SPACEFAST_AGENT_CLIENT_ID;
35
67
  if (!clientId || !privateJwk) {
36
68
  return null;
37
69
  }
38
- const tokenEndpoint = (typeof bundled?.token_endpoint === "string" ? bundled.token_endpoint : undefined) ??
70
+ const tokenEndpoint = bundled?.token_endpoint ??
39
71
  env.SPACEFAST_AGENT_TOKEN_ENDPOINT ??
40
72
  `${apiUrl}/v1/auth/oauth2/token`;
41
- const scope = (typeof bundled?.scope === "string" ? bundled.scope : undefined) ?? env.SPACEFAST_AGENT_SCOPE;
42
- const resources = stringArray(bundled?.resources) ??
73
+ const scope = bundled?.scope ?? env.SPACEFAST_AGENT_SCOPE;
74
+ const resources = bundled?.resources ??
43
75
  (env.SPACEFAST_AGENT_RESOURCES
44
76
  ? env.SPACEFAST_AGENT_RESOURCES.split(",")
45
77
  .map((entry) => entry.trim())
46
78
  .filter(Boolean)
47
79
  : undefined);
48
- return {
80
+ const config = {
49
81
  client_id: clientId,
50
82
  token_endpoint: tokenEndpoint,
51
83
  private_jwk: privateJwk,
52
- ...(scope ? { scope } : {}),
53
- ...(resources?.length ? { resources } : {}),
54
84
  };
85
+ if (scope)
86
+ config.scope = scope;
87
+ if (resources?.length)
88
+ config.resources = resources;
89
+ return config;
55
90
  }
56
91
  export function resolveAgentApiAuthorization(config, apiUrl) {
57
92
  const configuredResources = config.resources;
@@ -75,84 +110,123 @@ export function resolveAgentApiAuthorization(config, apiUrl) {
75
110
  scope: apiScopes.length > 0 ? apiScopes.join(" ") : undefined,
76
111
  };
77
112
  }
78
- function base64urlJson(value) {
79
- return Buffer.from(JSON.stringify(value)).toString("base64url");
80
- }
81
- export async function createPrivateKeyJwtAssertion(config, createInvalidConfigError = (message) => new Error(message)) {
113
+ async function privateKeyJwtClientAuth(config, createInvalidConfigError = (message) => new Error(message)) {
82
114
  if (config.private_jwk.kty !== "EC") {
83
115
  throw createInvalidConfigError(INVALID_PRIVATE_KEY_MESSAGE);
84
116
  }
85
- const now = Math.floor(Date.now() / 1000);
86
- const key = await webcrypto.subtle.importKey("jwk", config.private_jwk, { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
87
- const header = base64urlJson({
88
- alg: "ES256",
89
- typ: "JWT",
90
- ...(config.private_jwk.kid ? { kid: config.private_jwk.kid } : {}),
91
- });
92
- const payload = base64urlJson({
93
- iss: config.client_id,
94
- sub: config.client_id,
95
- aud: config.token_endpoint,
96
- jti: randomUUID(),
97
- iat: now,
98
- exp: now + 300,
99
- });
100
- const signingInput = `${header}.${payload}`;
101
- const signature = await webcrypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, key, new TextEncoder().encode(signingInput));
102
- return `${signingInput}.${Buffer.from(signature).toString("base64url")}`;
103
- }
104
- function oauthErrorDescription(payload) {
105
- return isRecord(payload) && "error_description" in payload
106
- ? String(payload.error_description)
107
- : undefined;
108
- }
109
- function oauthAccessToken(payload) {
110
- return isRecord(payload) && typeof payload.access_token === "string"
111
- ? payload.access_token
112
- : undefined;
113
- }
114
- function oauthExpiresIn(payload) {
115
- return isRecord(payload) && typeof payload.expires_in === "number" ? payload.expires_in : 300;
117
+ try {
118
+ const key = await webcrypto.subtle.importKey("jwk", config.private_jwk, { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
119
+ if (config.private_jwk.kid) {
120
+ return oauth.PrivateKeyJwt({ key, kid: config.private_jwk.kid });
121
+ }
122
+ return oauth.PrivateKeyJwt({ key });
123
+ }
124
+ catch {
125
+ throw createInvalidConfigError(INVALID_PRIVATE_KEY_MESSAGE);
126
+ }
116
127
  }
117
128
  export async function exchangeAgentAccessToken(input) {
118
129
  const { resources, scope } = resolveAgentApiAuthorization(input.config, input.apiUrl);
119
- const body = new URLSearchParams({
120
- grant_type: "client_credentials",
121
- client_id: input.config.client_id,
122
- client_assertion_type: PRIVATE_KEY_JWT_ASSERTION_TYPE,
123
- client_assertion: await createPrivateKeyJwtAssertion(input.config, input.createInvalidConfigError),
124
- });
130
+ const authorizationServer = {
131
+ // Agent credentials store the exact token endpoint rather than separate
132
+ // issuer metadata. RFC 7523 permits the endpoint as the assertion audience.
133
+ issuer: input.config.token_endpoint,
134
+ token_endpoint: input.config.token_endpoint,
135
+ };
136
+ const client = { client_id: input.config.client_id };
137
+ const body = new URLSearchParams();
125
138
  if (scope) {
126
139
  body.set("scope", scope);
127
140
  }
128
141
  for (const resource of resources) {
129
142
  body.append("resource", resource);
130
143
  }
131
- const response = await (input.fetch ?? globalThis.fetch)(input.config.token_endpoint, {
132
- method: "POST",
133
- headers: {
134
- accept: "application/json",
135
- "content-type": "application/x-www-form-urlencoded",
136
- "x-spacefast-client": input.client,
137
- },
138
- body,
144
+ const fetchFn = input.fetch ?? globalThis.fetch;
145
+ const response = await oauth.clientCredentialsGrantRequest(authorizationServer, client, await privateKeyJwtClientAuth(input.config, input.createInvalidConfigError), body, {
146
+ headers: { "x-spacefast-client": input.client },
139
147
  signal: input.signal,
148
+ [oauth.customFetch]: (url, init) => {
149
+ const requestInit = {
150
+ method: init.method,
151
+ headers: init.headers,
152
+ body: init.body,
153
+ redirect: init.redirect,
154
+ };
155
+ if (init.signal)
156
+ requestInit.signal = init.signal;
157
+ return fetchFn(url, requestInit);
158
+ },
140
159
  });
141
- const payload = await response.json().catch(() => null);
142
- if (!response.ok) {
143
- throw input.createError({
144
- status: response.status,
145
- code: "agent_token_exchange_failed",
146
- message: oauthErrorDescription(payload) ?? response.statusText,
147
- });
160
+ let tokens;
161
+ try {
162
+ tokens = await oauth.processClientCredentialsResponse(authorizationServer, client, response);
148
163
  }
149
- const accessToken = oauthAccessToken(payload);
150
- if (accessToken === undefined) {
164
+ catch (error) {
165
+ const description = error instanceof oauth.ResponseBodyError ? error.error_description : undefined;
151
166
  throw input.createError({
152
- status: response.status,
167
+ status: error instanceof oauth.ResponseBodyError ? error.status : response.status,
153
168
  code: "agent_token_exchange_failed",
154
- message: "OAuth token endpoint did not return an access_token.",
169
+ message: description ??
170
+ (response.ok
171
+ ? "OAuth token endpoint did not return a valid access-token response."
172
+ : response.statusText),
155
173
  });
156
174
  }
157
- return { accessToken, expiresIn: oauthExpiresIn(payload) };
175
+ return { accessToken: tokens.access_token, expiresIn: tokens.expires_in ?? 300 };
176
+ }
177
+ /**
178
+ * Refresh margin: a token this close to expiry is treated as already gone, so a
179
+ * request never leaves with a credential that dies in flight.
180
+ */
181
+ const TOKEN_EXPIRY_SKEW_MS = 30_000;
182
+ /**
183
+ * One agent access token held across calls instead of re-exchanged per request.
184
+ *
185
+ * The client-credentials grant is a signed round trip to the token endpoint, and
186
+ * an agent identity issues the same token for the same authorization until it
187
+ * expires: paying for it on every command (the CLI) or every tool call (the MCP
188
+ * server) buys nothing. The cache key is the authorization itself — client,
189
+ * endpoint, and the scope/resources {@link resolveAgentApiAuthorization} settled
190
+ * on — so pointing a process at a second API never replays the first one's
191
+ * bearer.
192
+ */
193
+ export function createCachedAgentTokenProvider(options) {
194
+ const now = options.now ?? (() => Date.now());
195
+ let cached = null;
196
+ return {
197
+ async accessToken(request) {
198
+ const { resources, scope } = resolveAgentApiAuthorization(request.config, request.apiUrl);
199
+ const key = JSON.stringify({
200
+ client_id: request.config.client_id,
201
+ token_endpoint: request.config.token_endpoint,
202
+ scope,
203
+ resources,
204
+ });
205
+ if (cached?.key === key && cached.expiresAtMs > now() + TOKEN_EXPIRY_SKEW_MS) {
206
+ return cached.accessToken;
207
+ }
208
+ const exchangeInput = {
209
+ apiUrl: request.apiUrl,
210
+ client: request.client,
211
+ config: request.config,
212
+ signal: request.signal,
213
+ createError: options.createError,
214
+ };
215
+ if (options.createInvalidConfigError) {
216
+ exchangeInput.createInvalidConfigError = options.createInvalidConfigError;
217
+ }
218
+ if (options.fetch)
219
+ exchangeInput.fetch = options.fetch;
220
+ const result = await exchangeAgentAccessToken(exchangeInput);
221
+ cached = {
222
+ key,
223
+ accessToken: result.accessToken,
224
+ expiresAtMs: now() + result.expiresIn * 1_000,
225
+ };
226
+ return result.accessToken;
227
+ },
228
+ invalidate() {
229
+ cached = null;
230
+ },
231
+ };
158
232
  }
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Canonical, browser-safe agent integration registry.
3
+ *
4
+ * Keep this module free of Node built-ins, filesystem access, and environment
5
+ * reads. Runtime-specific path and config mechanics live in ./node.ts.
6
+ */
7
+ export declare const AGENT_IDS: readonly ["claude-code", "claude-app", "claude-desktop", "cursor", "codex", "chatgpt", "vscode", "github-copilot", "devin-desktop", "devin-cloud", "zed", "gemini-cli", "opencode", "amp", "warp", "factory-droid", "cline", "continue", "raycast", "poke", "pi", "indent", "hermes", "openclaw"];
8
+ export declare const SKILLS_CLI_PACKAGE_SPEC = "skills";
9
+ export declare const SPACEFAST_CLI_PACKAGE_SPEC = "spacefast";
10
+ export declare const PLUGINS_CLI_PACKAGE_SPEC = "plugins";
11
+ /**
12
+ * The complete `-t` target set the universal plugin installer implements.
13
+ * Anything outside this list has no install
14
+ * mechanics upstream: `npx plugins add … -t <id>` either errors or synthesizes a
15
+ * manifest, so `sf plugins` must refuse it rather than run a command that cannot
16
+ * work. Capability flags are NOT a substitute — several entries carry a `plugin`
17
+ * capability because a vendor-native plugin lane exists, not because this
18
+ * installer can drive it.
19
+ */
20
+ export declare const PLUGINS_CLI_INSTALLER_TARGETS: readonly ["claude-code", "cursor", "codex", "github-copilot", "vscode", "grok", "kimi"];
21
+ export type PluginsCliInstallerTarget = (typeof PLUGINS_CLI_INSTALLER_TARGETS)[number];
22
+ export declare const AGENT_DISTRIBUTION_PROFILE: {
23
+ readonly author: {
24
+ readonly name: "Automattic";
25
+ readonly url: "https://spacefast.com";
26
+ };
27
+ readonly description: "Spacefast publishes files and folders to permanent web URLs with immutable versions, scoped access, and resumable MCP workflows.";
28
+ readonly displayName: "Spacefast";
29
+ readonly homepage: "https://spacefast.com";
30
+ readonly keywords: readonly ["spacefast", "publish-to-spacefast", "artifact-sharing", "hosting"];
31
+ readonly license: "MIT";
32
+ readonly name: "spacefast";
33
+ readonly privacyPolicyUrl: "https://automattic.com/privacy/";
34
+ readonly repository: "https://github.com/spacefast/plugins";
35
+ readonly reviewerAccount: {
36
+ readonly environment: "marketplace-reviewers";
37
+ readonly secret: "SPACEFAST_REVIEWER_ACCOUNT";
38
+ };
39
+ readonly shortDescription: "Publish files and folders to the web instantly.";
40
+ readonly termsOfServiceUrl: "https://wordpress.com/tos/";
41
+ };
42
+ export type AgentId = (typeof AGENT_IDS)[number];
43
+ export type AgentAlias = string;
44
+ export type Platform = "darwin" | "linux" | "win32" | "web" | "ios" | "android";
45
+ export type PresentationContext = "interactive-primary" | "terminal-alternates" | "static-docs" | "machine-installer";
46
+ export type AgentCapability = "skills" | "mcp-local" | "mcp-oauth" | "plugin" | "deeplink-only" | "none";
47
+ export type ListingStatus = "live" | "submitted" | "planned";
48
+ export type SkillLane = "curl" | "mcp-only" | "plugin" | "cli";
49
+ export type TokenClass = "api-key" | "handoff" | "oauth-access-token" | "plugin-secret";
50
+ export type RegistryContext = Readonly<{
51
+ apiOrigin: string;
52
+ dashboardOrigin: string;
53
+ mcpEndpoint: string;
54
+ /** Override setup executables when a published artifact requires exact package versions. */
55
+ packageSpecs?: Readonly<{
56
+ skillsCli?: string;
57
+ spacefastCli?: string;
58
+ }>;
59
+ /**
60
+ * Origin that serves space git remotes. Optional so existing callers keep
61
+ * working; it falls back to this environment's push origin, which is what
62
+ * every caller would have passed anyway.
63
+ */
64
+ pushOrigin?: string;
65
+ token?: string;
66
+ wwwOrigin: string;
67
+ }>;
68
+ /**
69
+ * `declared` is what a hand-entered registry row can honestly claim: a human
70
+ * read the vendor source on `checkedAt`. Only the scheduled deeplink probe
71
+ * (scripts/marketplaces/probe-deeplinks.mjs) may write `probed` or `failed`, and
72
+ * a `failed` deeplink is demoted out of `interactive-primary` automatically.
73
+ * There is deliberately no "verified" value: nothing in this file can earn it.
74
+ */
75
+ export type RegistryVerificationStatus = "declared" | "probed" | "failed";
76
+ export type RegistryVerification = Readonly<{
77
+ checkedAt: string;
78
+ evidence?: string;
79
+ sourceUrl: string;
80
+ status: RegistryVerificationStatus;
81
+ }>;
82
+ export type TokenSlot = Readonly<{
83
+ accepts: readonly TokenClass[];
84
+ location: "command" | "config-value" | "header";
85
+ placeholder: "{{token}}";
86
+ }>;
87
+ /**
88
+ * `checkedAt` is what stops a "soon" chip from aging silently (spec §8.2): the
89
+ * chip copy is date-free, so the age lives on the data and the release parity
90
+ * gate reports listing ages from here.
91
+ */
92
+ export type MethodListing = Readonly<{
93
+ checkedAt: string;
94
+ status: ListingStatus;
95
+ url?: string;
96
+ }>;
97
+ type MethodBase = Readonly<{
98
+ authLane: "oauth" | "token-slot" | "anonymous";
99
+ availability: Readonly<{
100
+ clients: readonly AgentId[];
101
+ minClientVersion?: string;
102
+ platforms?: readonly Platform[];
103
+ }>;
104
+ docsUrl: string;
105
+ id: string;
106
+ label: string;
107
+ listing: MethodListing;
108
+ rank: Partial<Readonly<Record<PresentationContext, number>>>;
109
+ summary: string;
110
+ tokenSlot?: TokenSlot;
111
+ /** The vendor's own doc for this exact method, rendered beside the summary. */
112
+ vendorDocs?: Readonly<{
113
+ href: string;
114
+ label: string;
115
+ }>;
116
+ verification: RegistryVerification;
117
+ }>;
118
+ export type IntegrationMethod = MethodBase & (Readonly<{
119
+ authPolicy?: "on-install";
120
+ commands: Readonly<{
121
+ inSession: readonly string[];
122
+ shell: readonly string[];
123
+ }>;
124
+ /**
125
+ * Present only when the universal installer can actually drive
126
+ * this client. `sf plugins` and every rendered `npx plugins … -t` command
127
+ * gate on this, never on the `plugin` capability flag.
128
+ */
129
+ installerTargets?: readonly PluginsCliInstallerTarget[];
130
+ kind: "plugin";
131
+ marketplace?: Readonly<{
132
+ id: string;
133
+ url: string;
134
+ }>;
135
+ }> | Readonly<{
136
+ badge?: Readonly<{
137
+ alt: string;
138
+ url: string;
139
+ }>;
140
+ build: (context: RegistryContext) => string;
141
+ /** Resolved against sibling methods at registry construction; never dangling. */
142
+ fallback: Readonly<{
143
+ id: string;
144
+ kind: IntegrationMethod["kind"];
145
+ }>;
146
+ kind: "deeplink";
147
+ }> | Readonly<{
148
+ build: (context: RegistryContext) => readonly string[];
149
+ kind: "command";
150
+ }> | Readonly<{
151
+ build: (context: RegistryContext) => string;
152
+ kind: "config";
153
+ /** `text` is a value pasted into a client's own settings form, not a file. */
154
+ language: "json" | "jsonc" | "text" | "toml" | "yaml";
155
+ path: string;
156
+ }> | Readonly<{
157
+ build: (context: RegistryContext) => string;
158
+ kind: "prompt";
159
+ }> | Readonly<{
160
+ kind: "download";
161
+ url: (context: RegistryContext) => string;
162
+ }> | Readonly<{
163
+ kind: "skill";
164
+ lane: SkillLane;
165
+ source: Readonly<{
166
+ id: string;
167
+ url: string;
168
+ }>;
169
+ }>);
170
+ export type AgentEntry = Readonly<{
171
+ agentsMd: "native" | "opt-in" | "none";
172
+ aliases: readonly AgentAlias[];
173
+ capabilities: readonly AgentCapability[];
174
+ description: string;
175
+ detectionHints: readonly string[];
176
+ docs: Readonly<{
177
+ spacefast: string;
178
+ vendor: string;
179
+ }>;
180
+ icon: string;
181
+ id: AgentId;
182
+ kind: "coding-agent" | "chat-app" | "mcp-app" | "editor";
183
+ legacyResultAliases: readonly string[];
184
+ machineClient?: boolean;
185
+ methods: readonly IntegrationMethod[];
186
+ monogram: string;
187
+ name: string;
188
+ notes?: readonly string[];
189
+ redeemableClientTargetId?: AgentId;
190
+ skills: Readonly<{
191
+ agentsDirs: boolean;
192
+ claudeDirs: boolean;
193
+ }>;
194
+ tier: "featured" | "listed";
195
+ verification: RegistryVerification;
196
+ vendor: string;
197
+ wizardGroup: "claude" | "openai" | "coding-agents" | "personal-agents" | "mcp-apps" | "prompt-first" | null;
198
+ }>;
199
+ export type PresentedMethod = Readonly<{
200
+ /**
201
+ * `disabled` is the registry's own answer for a method that has nothing to
202
+ * copy yet (a planned marketplace listing whose install command does not exist).
203
+ * Consumers render the "soon" chip from this; they never gate on an empty string.
204
+ */
205
+ action: Readonly<{
206
+ kind: "copy" | "disabled" | "download" | "link";
207
+ label: string;
208
+ value: string;
209
+ }>;
210
+ docsUrl: string;
211
+ fallbackMethodId?: string;
212
+ id: string;
213
+ kind: IntegrationMethod["kind"];
214
+ label: string;
215
+ /** The fence language for `action.value`, so renderers never re-derive it. */
216
+ language: "bash" | "json" | "jsonc" | "text" | "toml" | "yaml";
217
+ listing: MethodListing;
218
+ /** Where the value goes: a config file path, or the client's own settings screen. */
219
+ path?: string;
220
+ rank: number;
221
+ summary: string;
222
+ vendorDocs?: Readonly<{
223
+ href: string;
224
+ label: string;
225
+ }>;
226
+ }>;
227
+ export type PresentedAgentEntry = Omit<AgentEntry, "methods"> & Readonly<{
228
+ methods: readonly PresentedMethod[];
229
+ /** The no-session, no-mint lane: docs pages and the machine artifacts. */
230
+ staticDocs: readonly PresentedMethod[];
231
+ terminalAlternates: readonly PresentedMethod[];
232
+ }>;
233
+ export declare const AGENTS_MD_SPACEFAST_START = "<!-- SPACEFAST START -->";
234
+ export declare const AGENTS_MD_SPACEFAST_END = "<!-- SPACEFAST END -->";
235
+ export declare const AGENTS_MD_SPACEFAST_SNIPPET: string;
236
+ export declare const SOURCE_LANES: readonly [{
237
+ readonly id: "drop";
238
+ readonly label: "Drop files";
239
+ readonly description: "Drop a folder or archive and put it live now.";
240
+ readonly href: "/drop";
241
+ readonly icon: "upload";
242
+ }, {
243
+ readonly id: "push";
244
+ readonly label: "Push directly";
245
+ readonly description: "Push a Git repository to Spacefast and publish every update.";
246
+ readonly href: "/push";
247
+ readonly icon: "git";
248
+ }, {
249
+ readonly id: "github";
250
+ readonly label: "Connect a GitHub repository";
251
+ readonly description: "Connect GitHub and build and publish on every push.";
252
+ readonly href: "https://spacefast.com/docs/builds";
253
+ readonly icon: "github";
254
+ }, {
255
+ readonly id: "terminal";
256
+ readonly label: "Command line";
257
+ readonly description: "Publish from the terminal with the Spacefast CLI.";
258
+ readonly href: "/start?step=terminal";
259
+ readonly icon: "terminal";
260
+ }, {
261
+ readonly id: "api";
262
+ readonly label: "API";
263
+ readonly description: "Publish or automate with the REST API and OpenAPI.";
264
+ readonly href: "https://spacefast.com/docs/api";
265
+ readonly icon: "api";
266
+ }];
267
+ /**
268
+ * The generic hosted-MCP entry, for any client whose config is an `mcpServers`
269
+ * map. `type: "http"` is load-bearing and not decoration: Claude Code rejects a
270
+ * bare `{ url }` outright and VS Code reads one as a stdio command, so a bare
271
+ * url is not a smaller version of this — it is a broken version of it.
272
+ */
273
+ export declare function hostedMcpServersConfig(context: RegistryContext): string;
274
+ export declare const AGENT_REGISTRY: readonly AgentEntry[];
275
+ export declare function getAgentEntry(id: AgentId): AgentEntry;
276
+ export declare function resolveAgentId(value: string): AgentId | null;
277
+ /**
278
+ * The ranking rule, as a pure function so the demote path is testable without a
279
+ * registry entry: a deeplink whose last probe failed is dropped from the
280
+ * interactive lane, and everything else sorts by its per-context rank.
281
+ */
282
+ export declare function methodsForContext(methods: readonly IntegrationMethod[], context: PresentationContext): readonly IntegrationMethod[];
283
+ export declare function agentMethodsForContext(agentId: AgentId, context: PresentationContext): readonly IntegrationMethod[];
284
+ /**
285
+ * The `sf plugins` gate. Returns the universal-installer targets this agent can
286
+ * actually be installed into — empty for every agent whose plugin lane is
287
+ * vendor-native or not live, including entries that carry a `plugin` capability.
288
+ */
289
+ export declare function pluginInstallerTargetsFor(agentId: AgentId): readonly PluginsCliInstallerTarget[];
290
+ export declare function presentAgentEntry(agentId: AgentId, context: RegistryContext): PresentedAgentEntry;
291
+ export declare function base64Utf8(value: string): string;
292
+ export {};