@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 +1 -1
- package/public/index.html +3 -1
- package/public/robots.txt +2 -0
- package/src/index.ts +188 -10
- package/src/lib/a2a.ts +676 -5
- package/src/lib/ard.ts +77 -0
- package/src/lib/auth.ts +105 -0
- package/src/lib/did.ts +40 -0
- package/src/lib/identity.ts +4 -0
- package/src/lib/mcp.ts +2 -1
- package/src/lib/webbotauth.ts +19 -120
- package/wrangler.jsonc +11 -1
package/package.json
CHANGED
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 {
|
|
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
|
-
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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,
|