a2a-mesh 1.0.0

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.
@@ -0,0 +1,119 @@
1
+ import { createRemoteJWKSet, jwtVerify } from 'jose';
2
+
3
+ class JwtAuthMiddleware {
4
+ constructor(options) {
5
+ this.options = options;
6
+ }
7
+ remoteSets = /* @__PURE__ */ new Map();
8
+ async authenticateRequest(req) {
9
+ const securityRequirements = this.options.security && this.options.security.length > 0 ? this.options.security : [Object.fromEntries(this.options.securitySchemes.map((scheme) => [scheme.id, []]))];
10
+ let lastError;
11
+ for (const requirement of securityRequirements) {
12
+ try {
13
+ for (const schemeId of Object.keys(requirement)) {
14
+ const scheme = this.options.securitySchemes.find((item) => item.id === schemeId);
15
+ if (!scheme) {
16
+ throw new Error(`Unknown security scheme: ${schemeId}`);
17
+ }
18
+ if (scheme.type === "apiKey") {
19
+ return this.validateApiKey(req, scheme);
20
+ }
21
+ if (scheme.type === "http") {
22
+ return this.validateBearerToken(req);
23
+ }
24
+ if (scheme.type === "openIdConnect") {
25
+ return this.validateOidcToken(req, scheme);
26
+ }
27
+ }
28
+ } catch (error) {
29
+ lastError = error instanceof Error ? error : new Error(String(error));
30
+ }
31
+ }
32
+ throw lastError ?? new Error("Authentication failed");
33
+ }
34
+ middleware() {
35
+ return async (req, res, next) => {
36
+ try {
37
+ const authResult = await this.authenticateRequest(req);
38
+ Object.assign(req, { auth: authResult });
39
+ next();
40
+ } catch (error) {
41
+ res.status(401).json({
42
+ jsonrpc: "2.0",
43
+ error: {
44
+ code: -32040,
45
+ message: "Unauthorized",
46
+ data: { reason: String(error) }
47
+ },
48
+ id: req.body && typeof req.body === "object" && "id" in req.body ? req.body.id : null
49
+ });
50
+ }
51
+ };
52
+ }
53
+ validateApiKey(req, scheme) {
54
+ const expected = this.options.apiKeys?.[scheme.id];
55
+ const validValues = Array.isArray(expected) ? expected : expected ? [expected] : [];
56
+ if (validValues.length === 0) {
57
+ throw new Error(`No API key configured for scheme ${scheme.id}`);
58
+ }
59
+ const incoming = scheme.in === "header" ? req.header(scheme.name) : typeof req.query[scheme.name] === "string" ? req.query[scheme.name] : void 0;
60
+ if (typeof incoming !== "string" || !validValues.includes(incoming)) {
61
+ throw new Error("Invalid API key");
62
+ }
63
+ return { schemeId: scheme.id };
64
+ }
65
+ async validateOidcToken(req, scheme) {
66
+ const token = this.readBearerToken(req);
67
+ const discoveryResponse = await fetch(scheme.openIdConnectUrl);
68
+ if (!discoveryResponse.ok) {
69
+ throw new Error(`Failed to fetch OIDC configuration: ${discoveryResponse.status}`);
70
+ }
71
+ const discovery = await discoveryResponse.json();
72
+ const jwksUri = scheme.jwksUri ?? discovery.jwks_uri;
73
+ if (!jwksUri) {
74
+ throw new Error("OIDC configuration is missing jwks_uri");
75
+ }
76
+ let remoteSet = this.remoteSets.get(jwksUri);
77
+ if (!remoteSet) {
78
+ remoteSet = createRemoteJWKSet(new URL(jwksUri));
79
+ this.remoteSets.set(jwksUri, remoteSet);
80
+ }
81
+ const verifyOptions = {
82
+ ...scheme.audience ? { audience: scheme.audience } : {},
83
+ ...scheme.issuer ?? discovery.issuer ? { issuer: scheme.issuer ?? discovery.issuer } : {},
84
+ algorithms: scheme.algorithms ?? ["RS256", "ES256"]
85
+ };
86
+ const { payload } = await jwtVerify(token, remoteSet, verifyOptions);
87
+ return {
88
+ schemeId: scheme.id,
89
+ ...payload.sub ? { subject: payload.sub } : {},
90
+ claims: payload
91
+ };
92
+ }
93
+ async validateBearerToken(req) {
94
+ const token = this.readBearerToken(req);
95
+ const payload = this.decodeJwtWithoutValidation(token);
96
+ return {
97
+ schemeId: "bearer",
98
+ ...payload.sub ? { subject: payload.sub } : {},
99
+ claims: payload
100
+ };
101
+ }
102
+ readBearerToken(req) {
103
+ const header = req.header("authorization");
104
+ if (!header || !header.toLowerCase().startsWith("bearer ")) {
105
+ throw new Error("Missing bearer token");
106
+ }
107
+ return header.slice("bearer ".length).trim();
108
+ }
109
+ decodeJwtWithoutValidation(token) {
110
+ const parts = token.split(".");
111
+ if (parts.length < 2) {
112
+ throw new Error("Invalid JWT");
113
+ }
114
+ const payloadJson = Buffer.from(parts[1] ?? "", "base64url").toString("utf8");
115
+ return JSON.parse(payloadJson);
116
+ }
117
+ }
118
+
119
+ export { JwtAuthMiddleware as J };
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+
3
+ const api = require('@opentelemetry/api');
4
+
5
+ const VERSION = "1.0.0";
6
+ const a2aMeshTracer = api.trace.getTracer("a2a-mesh", VERSION);
7
+ function withA2ABaggage(taskId, contextId) {
8
+ let currentBaggage = api.propagation.getBaggage(api.context.active()) ?? api.propagation.createBaggage({});
9
+ if (taskId) {
10
+ currentBaggage = currentBaggage.setEntry("a2a.task_id", {
11
+ value: taskId,
12
+ metadata: api.baggageEntryMetadataFromString("a2a")
13
+ });
14
+ }
15
+ if (contextId) {
16
+ currentBaggage = currentBaggage.setEntry("a2a.context_id", {
17
+ value: contextId,
18
+ metadata: api.baggageEntryMetadataFromString("a2a")
19
+ });
20
+ }
21
+ const nextContext = api.propagation.setBaggage(api.context.active(), currentBaggage);
22
+ api.context.with(nextContext, () => void 0);
23
+ }
24
+
25
+ exports.SpanStatusCode = api.SpanStatusCode;
26
+ exports.a2aMeshTracer = a2aMeshTracer;
27
+ exports.withA2ABaggage = withA2ABaggage;
@@ -0,0 +1,7 @@
1
+ import * as _opentelemetry_api from '@opentelemetry/api';
2
+ export { SpanStatusCode } from '@opentelemetry/api';
3
+
4
+ declare const a2aMeshTracer: _opentelemetry_api.Tracer;
5
+ declare function withA2ABaggage(taskId?: string, contextId?: string): void;
6
+
7
+ export { a2aMeshTracer, withA2ABaggage };
@@ -0,0 +1,7 @@
1
+ import * as _opentelemetry_api from '@opentelemetry/api';
2
+ export { SpanStatusCode } from '@opentelemetry/api';
3
+
4
+ declare const a2aMeshTracer: _opentelemetry_api.Tracer;
5
+ declare function withA2ABaggage(taskId?: string, contextId?: string): void;
6
+
7
+ export { a2aMeshTracer, withA2ABaggage };
@@ -0,0 +1,7 @@
1
+ import * as _opentelemetry_api from '@opentelemetry/api';
2
+ export { SpanStatusCode } from '@opentelemetry/api';
3
+
4
+ declare const a2aMeshTracer: _opentelemetry_api.Tracer;
5
+ declare function withA2ABaggage(taskId?: string, contextId?: string): void;
6
+
7
+ export { a2aMeshTracer, withA2ABaggage };
@@ -0,0 +1,24 @@
1
+ import { trace, propagation, context, baggageEntryMetadataFromString } from '@opentelemetry/api';
2
+ export { SpanStatusCode } from '@opentelemetry/api';
3
+
4
+ const VERSION = "1.0.0";
5
+ const a2aMeshTracer = trace.getTracer("a2a-mesh", VERSION);
6
+ function withA2ABaggage(taskId, contextId) {
7
+ let currentBaggage = propagation.getBaggage(context.active()) ?? propagation.createBaggage({});
8
+ if (taskId) {
9
+ currentBaggage = currentBaggage.setEntry("a2a.task_id", {
10
+ value: taskId,
11
+ metadata: baggageEntryMetadataFromString("a2a")
12
+ });
13
+ }
14
+ if (contextId) {
15
+ currentBaggage = currentBaggage.setEntry("a2a.context_id", {
16
+ value: contextId,
17
+ metadata: baggageEntryMetadataFromString("a2a")
18
+ });
19
+ }
20
+ const nextContext = propagation.setBaggage(context.active(), currentBaggage);
21
+ context.with(nextContext, () => void 0);
22
+ }
23
+
24
+ export { a2aMeshTracer, withA2ABaggage };
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "a2a-mesh",
3
+ "version": "1.0.0",
4
+ "description": "A2A Protocol v1.0 server runtime, task model, auth, telemetry and middleware",
5
+ "keywords": [
6
+ "a2a",
7
+ "agent2agent",
8
+ "ai-agents",
9
+ "multi-agent",
10
+ "typescript"
11
+ ],
12
+ "author": "oaslananka",
13
+ "license": "Apache-2.0",
14
+ "homepage": "https://github.com/oaslananka/a2a-mesh#readme",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/oaslananka/a2a-mesh.git",
18
+ "directory": "packages/core"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/oaslananka/a2a-mesh/issues"
22
+ },
23
+ "main": "./dist/index.cjs",
24
+ "module": "./dist/index.mjs",
25
+ "types": "dist/index.d.ts",
26
+ "sideEffects": false,
27
+ "scripts": {
28
+ "build": "unbuild",
29
+ "typecheck": "tsc -b",
30
+ "test": "vitest run"
31
+ },
32
+ "dependencies": {
33
+ "@opentelemetry/api": "^1.9.0",
34
+ "eventsource": "^2.0.2",
35
+ "express": "^4.18.2",
36
+ "jose": "^5.2.0",
37
+ "uuid": "^9.0.1",
38
+ "zod": "^3.22.4"
39
+ },
40
+ "devDependencies": {
41
+ "@types/eventsource": "^1.1.15",
42
+ "@types/express": "^4.17.21",
43
+ "@types/uuid": "^9.0.8",
44
+ "typescript": "^5.2.2"
45
+ },
46
+ "type": "module",
47
+ "engines": {
48
+ "node": ">=20.0.0"
49
+ },
50
+ "peerDependencies": {
51
+ "@opentelemetry/api": "^1.9.0",
52
+ "better-sqlite3": "^11.10.0"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "@opentelemetry/api": {
56
+ "optional": true
57
+ },
58
+ "better-sqlite3": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "exports": {
63
+ ".": {
64
+ "import": "./dist/index.mjs",
65
+ "require": "./dist/index.cjs",
66
+ "types": "./dist/index.d.ts"
67
+ },
68
+ "./middleware": {
69
+ "import": "./dist/middleware/index.mjs",
70
+ "require": "./dist/middleware/index.cjs",
71
+ "types": "./dist/middleware/index.d.ts"
72
+ },
73
+ "./telemetry": {
74
+ "import": "./dist/telemetry/index.mjs",
75
+ "require": "./dist/telemetry/index.cjs",
76
+ "types": "./dist/telemetry/index.d.ts"
77
+ },
78
+ "./auth": {
79
+ "import": "./dist/auth/index.mjs",
80
+ "require": "./dist/auth/index.cjs",
81
+ "types": "./dist/auth/index.d.ts"
82
+ }
83
+ },
84
+ "module": "./dist/index.mjs",
85
+ "files": [
86
+ "dist",
87
+ "README.md",
88
+ "LICENSE"
89
+ ]
90
+ }