@nshipster/sosumi 1.0.8 → 1.0.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nshipster/sosumi",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Making Apple docs AI-readable",
5
5
  "license": "MIT",
6
6
  "author": "NSHipster",
package/public/index.html CHANGED
@@ -11,6 +11,8 @@
11
11
  <meta name="robots" content="index, follow">
12
12
 
13
13
  <link rel="canonical" href="https://sosumi.ai/">
14
+ <link rel="ard" href="/.well-known/ard.json" type="application/ai-catalog+json">
15
+ <link rel="ai-catalog" href="/.well-known/ai-catalog.json" type="application/ai-catalog+json">
14
16
 
15
17
  <meta property="og:title" content="sosumi.ai - Apple Docs for LLMs">
16
18
  <meta property="og:description"
@@ -1057,4 +1059,4 @@ curl -o ~/.agents/skills/sosumi/SKILL.md https://sosumi.ai/SKILL.md</code></pre>
1057
1059
  </footer>
1058
1060
  </body>
1059
1061
 
1060
- </html>
1062
+ </html>
package/public/robots.txt CHANGED
@@ -1,5 +1,6 @@
1
1
  User-agent: *
2
2
  Content-Signal: search=yes, ai-input=yes, ai-train=no
3
+ Content-Usage: train-ai=n, search=y
3
4
  Allow: /
4
5
  Disallow: /documentation/
5
6
  Disallow: /external/
@@ -7,3 +8,4 @@ Disallow: /design/human-interface-guidelines
7
8
  Disallow: /videos/play/
8
9
 
9
10
  Sitemap: https://sosumi.ai/sitemap.xml
11
+ Agentmap: https://sosumi.ai/.well-known/ard.json
package/src/index.ts CHANGED
@@ -5,7 +5,24 @@ import { cache } from "hono/cache"
5
5
  import { cors } from "hono/cors"
6
6
  import { HTTPException } from "hono/http-exception"
7
7
  import { trimTrailingSlash } from "hono/trailing-slash"
8
- import { buildAgentCard } from "./lib/a2a"
8
+ import {
9
+ A2A_MEDIA_TYPE,
10
+ A2A_MESSAGE_PATH,
11
+ A2AError,
12
+ buildAgentCard,
13
+ capabilityError,
14
+ createA2AErrorResponse,
15
+ handleA2AMessage,
16
+ parseA2AListTasksQuery,
17
+ pushNotificationsNotSupported,
18
+ readA2ACapabilityText,
19
+ readA2AJsonBody,
20
+ taskNotFound,
21
+ unsupportedOperation,
22
+ validateA2ARequestHeaders,
23
+ } from "./lib/a2a"
24
+ import { AI_CATALOG_MEDIA_TYPE, buildAiCatalog } from "./lib/ard"
25
+ import { buildDidDocument, DID_DOCUMENT_MEDIA_TYPE } from "./lib/did"
9
26
  import {
10
27
  decodeExternalTargetPath,
11
28
  ExternalAccessError,
@@ -51,6 +68,42 @@ interface Env {
51
68
 
52
69
  const app = new Hono<{ Bindings: Env }>()
53
70
 
71
+ function requestedA2AVersion(request: Request): string | null {
72
+ return request.headers.get("A2A-Version") ?? new URL(request.url).searchParams.get("A2A-Version")
73
+ }
74
+
75
+ function fixedA2AError(request: Request, error: A2AError): Response {
76
+ try {
77
+ validateA2ARequestHeaders(
78
+ request.method === "POST" ? request.headers.get("Content-Type") : null,
79
+ requestedA2AVersion(request),
80
+ )
81
+ return createA2AErrorResponse(error)
82
+ } catch (headerError) {
83
+ return createA2AErrorResponse(headerError)
84
+ }
85
+ }
86
+
87
+ function taskIdFromPath(path: string): string {
88
+ const taskPath = path.slice("/tasks/".length)
89
+ const encodedTaskId = taskPath.split(/[:/]/, 1)[0] ?? ""
90
+ try {
91
+ return decodeURIComponent(encodedTaskId) || "unknown"
92
+ } catch {
93
+ return encodedTaskId || "unknown"
94
+ }
95
+ }
96
+
97
+ function isMarkdownContentRoute(path: string): boolean {
98
+ return (
99
+ path.startsWith("/documentation/") ||
100
+ path.startsWith("/external/") ||
101
+ path === "/design/human-interface-guidelines" ||
102
+ path.startsWith("/design/human-interface-guidelines/") ||
103
+ path.startsWith("/videos/play/")
104
+ )
105
+ }
106
+
54
107
  app.use("*", async (c, next) => {
55
108
  // Prime Web Bot Auth signing before any route or outbound fetch runs.
56
109
  configureWebBotAuth(c.env)
@@ -63,6 +116,7 @@ app.use("*", async (c, next) => {
63
116
  c.header("X-XSS-Protection", "1; mode=block")
64
117
  c.header("Referrer-Policy", "strict-origin-when-cross-origin")
65
118
  c.header("Permissions-Policy", "camera=(), microphone=(), geolocation=()")
119
+ c.header("Content-Usage", "train-ai=n, search=y")
66
120
 
67
121
  // Performance headers
68
122
  c.header("Vary", "Accept")
@@ -72,18 +126,30 @@ app.use("*", async (c, next) => {
72
126
  c.header("Cache-Control", "no-store")
73
127
  }
74
128
 
129
+ const links = ['</llms.txt>; rel="describedby"']
130
+
75
131
  if (c.req.path === "/") {
76
- c.header(
77
- "Link",
78
- [
79
- '</.well-known/api-catalog>; rel="api-catalog"',
80
- '</.well-known/agent-card.json>; rel="service-desc"; type="application/json"',
81
- '</.well-known/mcp/server-card.json>; rel="service-desc"; type="application/json"',
82
- '</SKILL.md>; rel="service-doc"',
83
- '</llms.txt>; rel="alternate"; type="text/markdown"',
84
- ].join(", "),
132
+ links.unshift(
133
+ '</.well-known/api-catalog>; rel="api-catalog"',
134
+ `</.well-known/ard.json>; rel="ard"; type="${AI_CATALOG_MEDIA_TYPE}"`,
135
+ `</.well-known/ai-catalog.json>; rel="ai-catalog"; type="${AI_CATALOG_MEDIA_TYPE}"`,
136
+ '</.well-known/agent-card.json>; rel="service-desc"; type="application/json"',
137
+ '</.well-known/mcp/server-card.json>; rel="service-desc"; type="application/json"',
138
+ '</SKILL.md>; rel="service-doc"',
139
+ '</llms.txt>; rel="alternate"; type="text/markdown"',
85
140
  )
86
141
  }
142
+
143
+ if (isMarkdownContentRoute(c.req.path)) {
144
+ links.push(`<${c.req.path}>; rel="alternate"; type="text/markdown"`)
145
+ }
146
+
147
+ const existingLink = c.res.headers.get("Link")
148
+ if (existingLink) {
149
+ links.unshift(existingLink)
150
+ }
151
+
152
+ c.header("Link", links.join(", "))
87
153
  })
88
154
 
89
155
  app.use("*", cors())
@@ -188,6 +254,51 @@ const discoveryHeaders = {
188
254
  "Cache-Control": "public, max-age=300, s-maxage=600",
189
255
  } as const
190
256
 
257
+ const SECURITY_TXT_EXPIRY_MS = 364 * 24 * 60 * 60 * 1000 // 364 days
258
+
259
+ app.get("/.well-known/security.txt", (c) => {
260
+ const expires = new Date(Date.now() + SECURITY_TXT_EXPIRY_MS).toISOString()
261
+ const origin = new URL(c.req.url).origin
262
+
263
+ return c.text(
264
+ [
265
+ "Contact: mailto:info@sosumi.ai",
266
+ `Expires: ${expires}`,
267
+ `Canonical: ${origin}/.well-known/security.txt`,
268
+ "Preferred-Languages: en",
269
+ "",
270
+ ].join("\n"),
271
+ 200,
272
+ {
273
+ ...discoveryHeaders,
274
+ "Content-Type": "text/plain; charset=utf-8",
275
+ },
276
+ )
277
+ })
278
+
279
+ app.on("GET", ["/.well-known/ard.json", "/.well-known/ai-catalog.json"], (c) => {
280
+ const origin = new URL(c.req.url).origin
281
+
282
+ return c.json(buildAiCatalog(origin), 200, {
283
+ ...discoveryHeaders,
284
+ "Content-Type": `${AI_CATALOG_MEDIA_TYPE}; charset=utf-8`,
285
+ })
286
+ })
287
+
288
+ app.get("/.well-known/did.json", async (c) => {
289
+ const document = await buildDidDocument()
290
+ if (!document) {
291
+ return c.json({ error: "DID signing key is unavailable." }, 404, {
292
+ "Cache-Control": "no-store",
293
+ })
294
+ }
295
+
296
+ return c.json(document, 200, {
297
+ ...discoveryHeaders,
298
+ "Content-Type": `${DID_DOCUMENT_MEDIA_TYPE}; charset=utf-8`,
299
+ })
300
+ })
301
+
191
302
  app.get("/.well-known/api-catalog", (c) => {
192
303
  const origin = new URL(c.req.url).origin
193
304
 
@@ -250,6 +361,73 @@ app.get("/.well-known/agent-card.json", (c) => {
250
361
  })
251
362
  })
252
363
 
364
+ app.post(A2A_MESSAGE_PATH, async (c) => {
365
+ try {
366
+ validateA2ARequestHeaders(c.req.header("Content-Type") ?? null, requestedA2AVersion(c.req.raw))
367
+
368
+ const input = await readA2AJsonBody(c.req.raw)
369
+
370
+ const response = await handleA2AMessage(input, async (endpoint, outputMode) => {
371
+ const capabilityResponse = await app.request(
372
+ new URL(endpoint, c.req.url).toString(),
373
+ { headers: { Accept: outputMode } },
374
+ c.env,
375
+ )
376
+ const text = await readA2ACapabilityText(capabilityResponse)
377
+ if (!capabilityResponse.ok) {
378
+ throw capabilityError(
379
+ capabilityResponse.status,
380
+ text || `Sosumi capability request failed with status ${capabilityResponse.status}.`,
381
+ )
382
+ }
383
+ return text
384
+ })
385
+
386
+ return c.json(response, 200, {
387
+ "Content-Type": A2A_MEDIA_TYPE,
388
+ "Cache-Control": "no-store",
389
+ })
390
+ } catch (error) {
391
+ if (!(error instanceof A2AError)) {
392
+ console.error("A2A message execution failed", error)
393
+ }
394
+ return createA2AErrorResponse(error)
395
+ }
396
+ })
397
+
398
+ app.post("/message:stream", (c) =>
399
+ fixedA2AError(c.req.raw, unsupportedOperation("Streaming messages")),
400
+ )
401
+
402
+ app.get("/tasks", (c) => {
403
+ try {
404
+ validateA2ARequestHeaders(null, requestedA2AVersion(c.req.raw))
405
+ const pageSize = parseA2AListTasksQuery(new URL(c.req.url).searchParams)
406
+ return c.json({ tasks: [], nextPageToken: "", pageSize, totalSize: 0 }, 200, {
407
+ "Content-Type": A2A_MEDIA_TYPE,
408
+ "Cache-Control": "no-store",
409
+ })
410
+ } catch (error) {
411
+ return createA2AErrorResponse(error)
412
+ }
413
+ })
414
+
415
+ app.all("/tasks", (c) => fixedA2AError(c.req.raw, unsupportedOperation("This task operation")))
416
+
417
+ app.all("/tasks/*", (c) => {
418
+ if (c.req.path.includes("/pushNotificationConfigs")) {
419
+ return fixedA2AError(c.req.raw, pushNotificationsNotSupported())
420
+ }
421
+ if (c.req.path.endsWith(":subscribe")) {
422
+ return fixedA2AError(c.req.raw, unsupportedOperation("Task subscriptions"))
423
+ }
424
+ return fixedA2AError(c.req.raw, taskNotFound(taskIdFromPath(c.req.path)))
425
+ })
426
+
427
+ app.get("/extendedAgentCard", (c) =>
428
+ fixedA2AError(c.req.raw, unsupportedOperation("Extended Agent Cards")),
429
+ )
430
+
253
431
  app.get("/webmcp/manifest.json", (c) =>
254
432
  c.json(buildWebMcpManifest(), 200, {
255
433
  ...discoveryHeaders,