@radaros/transport 0.3.13 → 0.3.14
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/dist/index.d.ts +37 -1
- package/dist/index.js +167 -2
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Agent, A2AAgentCard, Team, Workflow, EventBus, VoiceAgent } from '@radaros/core';
|
|
1
|
+
import { Agent, A2AAgentCard, Registry, Servable, Team, Workflow, EventBus, VoiceAgent } from '@radaros/core';
|
|
2
2
|
|
|
3
3
|
interface A2AServerOptions {
|
|
4
4
|
agents: Record<string, Agent>;
|
|
@@ -71,6 +71,24 @@ interface SwaggerOptions {
|
|
|
71
71
|
specPath?: string;
|
|
72
72
|
}
|
|
73
73
|
interface RouterOptions {
|
|
74
|
+
/**
|
|
75
|
+
* Use a Registry for live auto-discovery. The router creates dynamic routes
|
|
76
|
+
* that resolve agents/teams/workflows at request time — any instance created
|
|
77
|
+
* after the router is mounted is automatically available.
|
|
78
|
+
*
|
|
79
|
+
* When omitted, falls back to the global registry from `@radaros/core`.
|
|
80
|
+
* Pass `false` to disable registry-based routing entirely (use explicit maps only).
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* createAgentRouter({ cors: true });
|
|
84
|
+
* new Agent({ name: "bot", model: openai("gpt-4o") }); // immediately routable
|
|
85
|
+
*/
|
|
86
|
+
registry?: Registry | false;
|
|
87
|
+
/**
|
|
88
|
+
* Auto-discover agents, teams, and workflows from a mixed array.
|
|
89
|
+
* Each item is classified by its `.kind` and keyed by `.name`.
|
|
90
|
+
*/
|
|
91
|
+
serve?: Servable[];
|
|
74
92
|
agents?: Record<string, Agent>;
|
|
75
93
|
teams?: Record<string, Team>;
|
|
76
94
|
workflows?: Record<string, Workflow<any>>;
|
|
@@ -175,6 +193,24 @@ interface BrowserGatewayOptions {
|
|
|
175
193
|
declare function createBrowserGateway(opts: BrowserGatewayOptions): void;
|
|
176
194
|
|
|
177
195
|
interface GatewayOptions {
|
|
196
|
+
/**
|
|
197
|
+
* Use a Registry for live auto-discovery. The gateway resolves agents/teams
|
|
198
|
+
* at event time — any instance created after the gateway starts is automatically
|
|
199
|
+
* reachable.
|
|
200
|
+
*
|
|
201
|
+
* When omitted, falls back to the global registry from `@radaros/core`.
|
|
202
|
+
* Pass `false` to disable registry-based lookup (use explicit maps only).
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* createAgentGateway({ io });
|
|
206
|
+
* new Agent({ name: "bot", model: openai("gpt-4o") }); // immediately reachable
|
|
207
|
+
*/
|
|
208
|
+
registry?: Registry | false;
|
|
209
|
+
/**
|
|
210
|
+
* Auto-discover agents and teams from a mixed array.
|
|
211
|
+
* Each item is classified by its `.kind` and keyed by `.name`.
|
|
212
|
+
*/
|
|
213
|
+
serve?: Servable[];
|
|
178
214
|
agents?: Record<string, Agent>;
|
|
179
215
|
teams?: Record<string, Team>;
|
|
180
216
|
io: any;
|
package/dist/index.js
CHANGED
|
@@ -431,6 +431,7 @@ function requestLogger(options) {
|
|
|
431
431
|
|
|
432
432
|
// src/express/router-factory.ts
|
|
433
433
|
import { createRequire as createRequire4 } from "module";
|
|
434
|
+
import { classifyServables, registry as globalRegistry } from "@radaros/core";
|
|
434
435
|
|
|
435
436
|
// src/express/swagger.ts
|
|
436
437
|
import { createRequire as createRequire3 } from "module";
|
|
@@ -955,6 +956,16 @@ function extractApiKey(req, agent) {
|
|
|
955
956
|
return req.body?.apiKey ?? void 0;
|
|
956
957
|
}
|
|
957
958
|
function createAgentRouter(opts) {
|
|
959
|
+
if (opts.serve?.length) {
|
|
960
|
+
const discovered = classifyServables(opts.serve);
|
|
961
|
+
opts = {
|
|
962
|
+
...opts,
|
|
963
|
+
agents: { ...discovered.agents, ...opts.agents },
|
|
964
|
+
teams: { ...discovered.teams, ...opts.teams },
|
|
965
|
+
workflows: { ...discovered.workflows, ...opts.workflows }
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
const reg = opts.registry === false ? null : opts.registry ?? globalRegistry;
|
|
958
969
|
let express;
|
|
959
970
|
try {
|
|
960
971
|
express = _require4("express");
|
|
@@ -1144,6 +1155,138 @@ function createAgentRouter(opts) {
|
|
|
1144
1155
|
});
|
|
1145
1156
|
}
|
|
1146
1157
|
}
|
|
1158
|
+
if (reg) {
|
|
1159
|
+
router.post(
|
|
1160
|
+
"/agents/:name/run",
|
|
1161
|
+
withUpload(async (req, res) => {
|
|
1162
|
+
const agent = reg.getAgent(req.params.name);
|
|
1163
|
+
if (!agent) return res.status(404).json({ error: `Agent "${req.params.name}" not found` });
|
|
1164
|
+
try {
|
|
1165
|
+
const validated = validateBody(req.body, { input: "string", sessionId: "string?", userId: "string?" });
|
|
1166
|
+
const input = buildMultiModalInput(req.body, req.files) ?? validated.input;
|
|
1167
|
+
if (!input) return res.status(400).json({ error: "input is required" });
|
|
1168
|
+
const apiKey = extractApiKey(req, agent);
|
|
1169
|
+
const result = await agent.run(input, {
|
|
1170
|
+
sessionId: validated.sessionId,
|
|
1171
|
+
userId: validated.userId,
|
|
1172
|
+
apiKey
|
|
1173
|
+
});
|
|
1174
|
+
res.json(result);
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
res.status(400).json({ error: error.message });
|
|
1177
|
+
}
|
|
1178
|
+
})
|
|
1179
|
+
);
|
|
1180
|
+
router.post("/agents/:name/stream", async (req, res) => {
|
|
1181
|
+
const agent = reg.getAgent(req.params.name);
|
|
1182
|
+
if (!agent) return res.status(404).json({ error: `Agent "${req.params.name}" not found` });
|
|
1183
|
+
try {
|
|
1184
|
+
const validated = validateBody(req.body, { input: "string", sessionId: "string?", userId: "string?" });
|
|
1185
|
+
const input = validated.input;
|
|
1186
|
+
if (!input) return res.status(400).json({ error: "input is required" });
|
|
1187
|
+
const apiKey = extractApiKey(req, agent);
|
|
1188
|
+
res.writeHead(200, {
|
|
1189
|
+
"Content-Type": "text/event-stream",
|
|
1190
|
+
"Cache-Control": "no-cache",
|
|
1191
|
+
Connection: "keep-alive"
|
|
1192
|
+
});
|
|
1193
|
+
for await (const chunk of agent.stream(input, {
|
|
1194
|
+
sessionId: validated.sessionId,
|
|
1195
|
+
userId: validated.userId,
|
|
1196
|
+
apiKey
|
|
1197
|
+
})) {
|
|
1198
|
+
res.write(`data: ${JSON.stringify(chunk)}
|
|
1199
|
+
|
|
1200
|
+
`);
|
|
1201
|
+
}
|
|
1202
|
+
res.write("data: [DONE]\n\n");
|
|
1203
|
+
res.end();
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
if (!res.headersSent) res.status(500).json({ error: error.message });
|
|
1206
|
+
else {
|
|
1207
|
+
res.write(`data: ${JSON.stringify({ type: "error", error: error.message })}
|
|
1208
|
+
|
|
1209
|
+
`);
|
|
1210
|
+
res.end();
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
});
|
|
1214
|
+
router.post("/teams/:name/run", async (req, res) => {
|
|
1215
|
+
const team = reg.getTeam(req.params.name);
|
|
1216
|
+
if (!team) return res.status(404).json({ error: `Team "${req.params.name}" not found` });
|
|
1217
|
+
try {
|
|
1218
|
+
const validated = validateBody(req.body, { input: "string", sessionId: "string?", userId: "string?" });
|
|
1219
|
+
const input = validated.input;
|
|
1220
|
+
if (!input) return res.status(400).json({ error: "input is required" });
|
|
1221
|
+
const apiKey = req.headers["x-api-key"] ?? req.body?.apiKey;
|
|
1222
|
+
const result = await team.run(input, {
|
|
1223
|
+
sessionId: validated.sessionId,
|
|
1224
|
+
userId: validated.userId,
|
|
1225
|
+
apiKey
|
|
1226
|
+
});
|
|
1227
|
+
res.json(result);
|
|
1228
|
+
} catch (error) {
|
|
1229
|
+
res.status(500).json({ error: error.message });
|
|
1230
|
+
}
|
|
1231
|
+
});
|
|
1232
|
+
router.post("/teams/:name/stream", async (req, res) => {
|
|
1233
|
+
const team = reg.getTeam(req.params.name);
|
|
1234
|
+
if (!team) return res.status(404).json({ error: `Team "${req.params.name}" not found` });
|
|
1235
|
+
try {
|
|
1236
|
+
const validated = validateBody(req.body, { input: "string", sessionId: "string?", userId: "string?" });
|
|
1237
|
+
const input = validated.input;
|
|
1238
|
+
if (!input) return res.status(400).json({ error: "input is required" });
|
|
1239
|
+
const apiKey = req.headers["x-api-key"] ?? req.body?.apiKey;
|
|
1240
|
+
res.writeHead(200, {
|
|
1241
|
+
"Content-Type": "text/event-stream",
|
|
1242
|
+
"Cache-Control": "no-cache",
|
|
1243
|
+
Connection: "keep-alive"
|
|
1244
|
+
});
|
|
1245
|
+
for await (const chunk of team.stream(input, {
|
|
1246
|
+
sessionId: validated.sessionId,
|
|
1247
|
+
userId: validated.userId,
|
|
1248
|
+
apiKey
|
|
1249
|
+
})) {
|
|
1250
|
+
res.write(`data: ${JSON.stringify(chunk)}
|
|
1251
|
+
|
|
1252
|
+
`);
|
|
1253
|
+
}
|
|
1254
|
+
res.write("data: [DONE]\n\n");
|
|
1255
|
+
res.end();
|
|
1256
|
+
} catch (error) {
|
|
1257
|
+
if (!res.headersSent) res.status(500).json({ error: error.message });
|
|
1258
|
+
else {
|
|
1259
|
+
res.write(`data: ${JSON.stringify({ type: "error", error: error.message })}
|
|
1260
|
+
|
|
1261
|
+
`);
|
|
1262
|
+
res.end();
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
});
|
|
1266
|
+
router.post("/workflows/:name/run", async (req, res) => {
|
|
1267
|
+
const workflow = reg.getWorkflow(req.params.name);
|
|
1268
|
+
if (!workflow) return res.status(404).json({ error: `Workflow "${req.params.name}" not found` });
|
|
1269
|
+
try {
|
|
1270
|
+
const { sessionId, userId } = req.body ?? {};
|
|
1271
|
+
const result = await workflow.run({ sessionId, userId });
|
|
1272
|
+
res.json(result);
|
|
1273
|
+
} catch (error) {
|
|
1274
|
+
res.status(500).json({ error: error.message });
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
router.get("/agents", (_req, res) => {
|
|
1278
|
+
res.json(reg.describeAgents());
|
|
1279
|
+
});
|
|
1280
|
+
router.get("/teams", (_req, res) => {
|
|
1281
|
+
res.json(reg.describeTeams());
|
|
1282
|
+
});
|
|
1283
|
+
router.get("/workflows", (_req, res) => {
|
|
1284
|
+
res.json(reg.describeWorkflows());
|
|
1285
|
+
});
|
|
1286
|
+
router.get("/registry", (_req, res) => {
|
|
1287
|
+
res.json(reg.list());
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1147
1290
|
return router;
|
|
1148
1291
|
}
|
|
1149
1292
|
|
|
@@ -1261,6 +1404,7 @@ function createBrowserGateway(opts) {
|
|
|
1261
1404
|
}
|
|
1262
1405
|
|
|
1263
1406
|
// src/socketio/gateway.ts
|
|
1407
|
+
import { classifyServables as classifyServables2, registry as globalRegistry2 } from "@radaros/core";
|
|
1264
1408
|
function createSocketRateLimiter(maxPerMinute = 60) {
|
|
1265
1409
|
return () => {
|
|
1266
1410
|
let count = 0;
|
|
@@ -1277,6 +1421,15 @@ function createSocketRateLimiter(maxPerMinute = 60) {
|
|
|
1277
1421
|
};
|
|
1278
1422
|
}
|
|
1279
1423
|
function createAgentGateway(opts) {
|
|
1424
|
+
if (opts.serve?.length) {
|
|
1425
|
+
const discovered = classifyServables2(opts.serve);
|
|
1426
|
+
opts = {
|
|
1427
|
+
...opts,
|
|
1428
|
+
agents: { ...discovered.agents, ...opts.agents },
|
|
1429
|
+
teams: { ...discovered.teams, ...opts.teams }
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
const reg = opts.registry === false ? null : opts.registry ?? globalRegistry2;
|
|
1280
1433
|
const ns = opts.io.of(opts.namespace ?? "/radaros");
|
|
1281
1434
|
if (opts.authMiddleware) {
|
|
1282
1435
|
ns.use(opts.authMiddleware);
|
|
@@ -1297,7 +1450,7 @@ function createAgentGateway(opts) {
|
|
|
1297
1450
|
socket.emit("agent.error", { error: "Invalid sessionId: must be a string" });
|
|
1298
1451
|
return;
|
|
1299
1452
|
}
|
|
1300
|
-
const agent = opts.agents?.[data.name];
|
|
1453
|
+
const agent = opts.agents?.[data.name] ?? reg?.getAgent(data.name);
|
|
1301
1454
|
if (!agent) {
|
|
1302
1455
|
socket.emit("agent.error", {
|
|
1303
1456
|
error: `Agent "${data.name}" not found`
|
|
@@ -1332,7 +1485,19 @@ function createAgentGateway(opts) {
|
|
|
1332
1485
|
socket.on("disconnect", () => {
|
|
1333
1486
|
});
|
|
1334
1487
|
socket.on("team.run", async (data) => {
|
|
1335
|
-
|
|
1488
|
+
if (!checkRate()) {
|
|
1489
|
+
socket.emit("agent.error", { error: "Rate limit exceeded" });
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
if (!data || typeof data.input !== "string" || !data.input.trim()) {
|
|
1493
|
+
socket.emit("agent.error", { error: "Invalid input: must be a non-empty string" });
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
if (data.sessionId !== void 0 && typeof data.sessionId !== "string") {
|
|
1497
|
+
socket.emit("agent.error", { error: "Invalid sessionId: must be a string" });
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
const team = opts.teams?.[data.name] ?? reg?.getTeam(data.name);
|
|
1336
1501
|
if (!team) {
|
|
1337
1502
|
socket.emit("agent.error", {
|
|
1338
1503
|
error: `Team "${data.name}" not found`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@radaros/transport",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "HTTP and WebSocket transport layer for RadarOS agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"typescript": "^5.6.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@radaros/core": "^0.3.
|
|
42
|
+
"@radaros/core": "^0.3.14",
|
|
43
43
|
"@types/express": "^4.0.0 || ^5.0.0",
|
|
44
44
|
"express": "^4.0.0 || ^5.0.0",
|
|
45
45
|
"multer": ">=2.0.0",
|