@dexwox-labs/a2a-server
Version:
TypeScript server implementation for Google's Agent-to-Agent (A2A) protocol - includes Express/WebSocket handlers, request validation and queue management
197 lines • 7.92 kB
JavaScript
;
/**
* @module A2AServer
* @description Server implementation for hosting A2A protocol agents
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.A2AServer = void 0;
const express_1 = __importDefault(require("express"));
const body_parser_1 = require("body-parser");
const cors_1 = __importDefault(require("cors"));
const ws_1 = require("ws");
/**
* Server implementation for hosting A2A protocol agents
*
* The A2AServer class provides a complete HTTP and WebSocket server implementation
* for hosting agents that implement the A2A protocol. It handles request routing,
* error handling, and WebSocket connections for streaming.
*
* @example
* ```typescript
* import { A2AServer, DefaultRequestHandler } from '@dexwox-labs/a2a-node';
*
* // Define an agent
* const agent = {
* id: 'weather-agent',
* name: 'Weather Agent',
* description: 'Provides weather information',
* capabilities: ['weather-forecasting'],
* endpoint: 'http://localhost:3000'
* };
*
* // Create a request handler
* const requestHandler = new DefaultRequestHandler([agent]);
*
* // Create and start the server
* const server = new A2AServer(agent, requestHandler);
* server.start(3000);
* ```
*/
class A2AServer {
/**
* Creates a new A2AServer instance
*
* @param agentCard - The agent card describing this server's capabilities
* @param requestHandler - Handler for processing incoming requests
* @param contextMiddleware - Optional custom middleware for request context
*/
constructor(agentCard, requestHandler, contextMiddleware) {
this.contextMiddleware = contextMiddleware;
/** WebSocket server instance */
this.wss = null;
this.app = (0, express_1.default)();
this.agentCard = agentCard;
this.requestHandler = requestHandler;
this.configureMiddleware();
this.configureRoutes();
this.configureErrorHandling();
}
/**
* Configures Express middleware for the server
*
* Sets up JSON parsing, CORS, and request context middleware.
* @private
*/
configureMiddleware() {
this.app.use((0, body_parser_1.json)());
this.app.use((0, cors_1.default)({
origin: process.env.CORS_ORIGIN || '*',
methods: ['GET', 'POST', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// Add context middleware if provided, otherwise use default
if (this.contextMiddleware) {
this.app.use(this.contextMiddleware);
}
else {
this.app.use(require('./agent-execution/context-middleware').contextMiddleware(this.agentCard.id));
}
}
/**
* Configures HTTP routes for the server
*
* Sets up the agent card endpoint and API routes.
* @private
*/
configureRoutes() {
// Agent card endpoint
this.app.get('/.well-known/agent.json', (_, res) => {
res.json(this.agentCard);
});
// API routes
this.app.use('/api/v1', this.requestHandler.router);
}
/**
* Configures global error handling for the server
*
* Sets up middleware to catch and format errors according to the A2A protocol.
* @private
*/
configureErrorHandling() {
const errorHandler = (err, req, res, next) => {
if (err instanceof Error && 'code' in err && 'message' in err) {
const a2aError = err;
res.status(400).json({
jsonrpc: '2.0',
error: Object.assign({ code: a2aError.code, message: a2aError.message }, (a2aError.data && { data: a2aError.data }))
});
}
console.error(err);
res.status(500).json({
jsonrpc: '2.0',
error: {
code: -32603,
message: 'Internal server error'
}
});
};
this.app.use(errorHandler);
}
/**
* Starts the A2A server on the specified port
*
* This method starts both the HTTP server and WebSocket server for handling
* A2A protocol requests. The WebSocket server is used for streaming messages.
*
* @param port - The port to listen on (default: 3000)
*
* @example
* ```typescript
* // Start on the default port (3000)
* server.start();
*
* // Start on a specific port
* server.start(8080);
* ```
*/
start(port = 3000) {
const server = this.app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
// Create WebSocket server
this.wss = new ws_1.WebSocketServer({ server });
this.wss.on('connection', (ws) => {
console.log('New WebSocket connection');
ws.on('message', (data) => __awaiter(this, void 0, void 0, function* () {
var _a, e_1, _b, _c;
try {
const message = JSON.parse(data.toString());
if (message.method === 'streamMessage') {
try {
for (var _d = true, _e = __asyncValues(this.requestHandler.handleStreamMessage(message.params.parts, message.params.agentId)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
_c = _f.value;
_d = false;
const part = _c;
ws.send(JSON.stringify(part));
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
}
finally { if (e_1) throw e_1.error; }
}
}
}
catch (err) {
ws.send(JSON.stringify(this.requestHandler.normalizeError(err)));
}
}));
ws.on('close', () => {
console.log('WebSocket connection closed');
});
});
}
}
exports.A2AServer = A2AServer;
//# sourceMappingURL=app.js.map