@cogitator-ai/express 0.2.15 → 0.3.1
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/README.md +68 -22
- package/dist/middleware/cors.d.ts.map +1 -1
- package/dist/middleware/cors.js +17 -18
- package/dist/middleware/cors.js.map +1 -1
- package/dist/middleware/error-handler.d.ts.map +1 -1
- package/dist/middleware/error-handler.js +34 -0
- package/dist/middleware/error-handler.js.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +14 -4
- package/dist/middleware/rate-limit.js.map +1 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +59 -43
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/swarms.d.ts.map +1 -1
- package/dist/routes/swarms.js +53 -48
- package/dist/routes/swarms.js.map +1 -1
- package/dist/routes/threads.d.ts.map +1 -1
- package/dist/routes/threads.js +27 -45
- package/dist/routes/threads.js.map +1 -1
- package/dist/routes/utils.d.ts +36 -0
- package/dist/routes/utils.d.ts.map +1 -0
- package/dist/routes/utils.js +95 -0
- package/dist/routes/utils.js.map +1 -0
- package/dist/routes/workflows.d.ts.map +1 -1
- package/dist/routes/workflows.js +49 -38
- package/dist/routes/workflows.js.map +1 -1
- package/dist/server.d.ts +8 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +27 -14
- package/dist/server.js.map +1 -1
- package/dist/streaming/express-stream-writer.d.ts +1 -0
- package/dist/streaming/express-stream-writer.d.ts.map +1 -1
- package/dist/streaming/express-stream-writer.js +14 -1
- package/dist/streaming/express-stream-writer.js.map +1 -1
- package/dist/websocket/ws-handler.d.ts +7 -0
- package/dist/websocket/ws-handler.d.ts.map +1 -1
- package/dist/websocket/ws-handler.js +238 -124
- package/dist/websocket/ws-handler.js.map +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -20,14 +20,17 @@ import { CogitatorServer } from '@cogitator-ai/express';
|
|
|
20
20
|
const app = express();
|
|
21
21
|
|
|
22
22
|
const cogitator = new Cogitator({
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
llm: {
|
|
24
|
+
defaultModel: 'openai/gpt-4o-mini',
|
|
25
|
+
providers: { openai: { apiKey: process.env.OPENAI_API_KEY } },
|
|
26
|
+
},
|
|
25
27
|
});
|
|
26
28
|
|
|
27
29
|
const chatAgent = new Agent({
|
|
28
30
|
name: 'chat',
|
|
31
|
+
description: 'General-purpose chat assistant',
|
|
29
32
|
instructions: 'You are a helpful assistant.',
|
|
30
|
-
model: 'gpt-4o-mini',
|
|
33
|
+
model: 'openai/gpt-4o-mini',
|
|
31
34
|
});
|
|
32
35
|
|
|
33
36
|
const server = new CogitatorServer({
|
|
@@ -54,6 +57,8 @@ POST /api/agents/:name/run - Run agent (JSON response)
|
|
|
54
57
|
POST /api/agents/:name/stream - Run agent (SSE stream)
|
|
55
58
|
```
|
|
56
59
|
|
|
60
|
+
Run/stream body: `{ input: string; context?: object; threadId?: string }` — `input` must be a non-empty string (400 otherwise). The agent list exposes `config.description`, never the agent instructions. The authenticated `userId` (from `auth`) is passed to the run, and the run is aborted when the client disconnects.
|
|
61
|
+
|
|
57
62
|
### Threads (Memory)
|
|
58
63
|
|
|
59
64
|
```
|
|
@@ -62,6 +67,8 @@ POST /api/threads/:id/messages - Add message to thread
|
|
|
62
67
|
DELETE /api/threads/:id - Delete thread
|
|
63
68
|
```
|
|
64
69
|
|
|
70
|
+
Requires `memory` on the `Cogitator` instance (503 otherwise). New messages need `role` (`user` | `assistant` | `system`) and a non-empty string `content`; optional `metadata` is stored on the memory entry.
|
|
71
|
+
|
|
65
72
|
### Workflows
|
|
66
73
|
|
|
67
74
|
```
|
|
@@ -70,15 +77,19 @@ POST /api/workflows/:name/run - Run workflow
|
|
|
70
77
|
POST /api/workflows/:name/stream - Stream workflow events
|
|
71
78
|
```
|
|
72
79
|
|
|
80
|
+
Body: `{ input?: object; options?: { maxConcurrency?: number; maxIterations?: number; checkpoint?: boolean } }`. `maxConcurrency`/`maxIterations` must be positive integers and `checkpoint` a boolean (400 otherwise); other options are dropped. A failed workflow returns `500` with code `WORKFLOW_FAILED` (the stream ends with an `error` event instead of `workflow_completed`). Disconnecting aborts the workflow.
|
|
81
|
+
|
|
73
82
|
### Swarms
|
|
74
83
|
|
|
75
84
|
```
|
|
76
85
|
GET /api/swarms - List all swarms
|
|
77
86
|
POST /api/swarms/:name/run - Run swarm
|
|
78
87
|
POST /api/swarms/:name/stream - Stream swarm events
|
|
79
|
-
GET /api/swarms/:name/blackboard - Get
|
|
88
|
+
GET /api/swarms/:name/blackboard - Get configured blackboard sections
|
|
80
89
|
```
|
|
81
90
|
|
|
91
|
+
Run/stream body: `{ input: string; context?: object; threadId?: string; timeout?: number }` (`timeout` must be positive). Disconnecting aborts the swarm. Each run creates a fresh swarm, so the blackboard endpoint returns the configured initial sections.
|
|
92
|
+
|
|
82
93
|
### Tools & Docs
|
|
83
94
|
|
|
84
95
|
```
|
|
@@ -102,20 +113,27 @@ const server = new CogitatorServer({
|
|
|
102
113
|
enableWebSocket: true,
|
|
103
114
|
enableSwagger: true,
|
|
104
115
|
|
|
105
|
-
// Authentication
|
|
116
|
+
// Authentication — throw to reject (401); the returned context is available
|
|
117
|
+
// as req.cogitator.auth and its userId is passed to agent runs.
|
|
118
|
+
// Also applied to WebSocket upgrade requests.
|
|
106
119
|
auth: async (req) => {
|
|
107
120
|
const token = req.headers.authorization?.replace('Bearer ', '');
|
|
108
121
|
const user = await validateToken(token);
|
|
109
122
|
return { userId: user.id, roles: user.roles };
|
|
110
123
|
},
|
|
111
124
|
|
|
112
|
-
// Rate limiting
|
|
125
|
+
// Rate limiting (in-memory, per process). windowMs must be positive and max
|
|
126
|
+
// non-negative, otherwise the constructor throws. Requests without a client
|
|
127
|
+
// address share a single bucket.
|
|
113
128
|
rateLimit: {
|
|
114
129
|
windowMs: 60000, // 1 minute
|
|
115
130
|
max: 100, // 100 requests per window
|
|
131
|
+
trustProxy: false, // true: key by the nearest X-Forwarded-For hop
|
|
116
132
|
},
|
|
117
133
|
|
|
118
|
-
// CORS
|
|
134
|
+
// CORS — credentials default to true for explicit origins and to false for '*'.
|
|
135
|
+
// `origin: '*'` with `credentials: true` reflects any origin with credentials;
|
|
136
|
+
// only do that for trusted, non-cookie setups.
|
|
119
137
|
cors: {
|
|
120
138
|
origin: ['https://myapp.com'],
|
|
121
139
|
credentials: true,
|
|
@@ -163,23 +181,40 @@ while (true) {
|
|
|
163
181
|
|
|
164
182
|
## WebSocket Support
|
|
165
183
|
|
|
166
|
-
Enable real-time bidirectional communication:
|
|
184
|
+
Enable real-time bidirectional communication (requires the optional `ws` package):
|
|
167
185
|
|
|
168
186
|
```typescript
|
|
187
|
+
import { createServer } from 'http';
|
|
188
|
+
|
|
169
189
|
const server = new CogitatorServer({
|
|
170
190
|
// ...
|
|
171
|
-
config: {
|
|
191
|
+
config: {
|
|
192
|
+
enableWebSocket: true,
|
|
193
|
+
websocket: { path: '/api/ws', pingInterval: 30000, maxPayloadSize: 1024 * 1024 },
|
|
194
|
+
},
|
|
172
195
|
});
|
|
173
196
|
|
|
174
|
-
|
|
175
|
-
import { setupWebSocket } from '@cogitator-ai/express';
|
|
176
|
-
import { createServer } from 'http';
|
|
197
|
+
await server.init();
|
|
177
198
|
|
|
178
199
|
const httpServer = createServer(app);
|
|
179
|
-
await
|
|
200
|
+
await server.attachWebSocket(httpServer); // defaults to `${basePath}/ws`
|
|
180
201
|
httpServer.listen(3000);
|
|
181
202
|
```
|
|
182
203
|
|
|
204
|
+
`attachWebSocket` throws if `enableWebSocket` is off, `init()` has not run, or `ws` is not installed. The configured `auth` function runs on the upgrade request (rejected with 401 when it throws). For custom setups, `setupWebSocket(httpServer, routeContext, config)` is also exported.
|
|
205
|
+
|
|
206
|
+
Protocol:
|
|
207
|
+
|
|
208
|
+
| Message | Direction | Description |
|
|
209
|
+
| ----------------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
210
|
+
| `{ type: 'run', id, payload: { type: 'agent', name, input, context?, threadId? } }` | Client→Server | Run an agent; events arrive as `{ type: 'event', id, payload }` (`token`, `tool-call`, `tool-result`, `complete`, `cancelled`) |
|
|
211
|
+
| `{ type: 'stop' }` | Client→Server | Abort the client's running agent (emits `cancelled`) |
|
|
212
|
+
| `{ type: 'subscribe', channel: 'agent:<name>' }` | Client→Server | Receive events of runs of that agent started by other clients |
|
|
213
|
+
| `{ type: 'unsubscribe', channel }` | Client→Server | Stop receiving channel events |
|
|
214
|
+
| `{ type: 'ping' }` | Client→Server | Answered with `{ type: 'pong' }` |
|
|
215
|
+
|
|
216
|
+
Only agent runs are supported over WebSocket; use the HTTP endpoints for workflows and swarms. A new `run` from the same connection aborts the previous one.
|
|
217
|
+
|
|
183
218
|
Client usage:
|
|
184
219
|
|
|
185
220
|
```typescript
|
|
@@ -249,15 +284,19 @@ router.post('/custom/stream', async (req, res) => {
|
|
|
249
284
|
setupSSEHeaders(res);
|
|
250
285
|
const writer = new ExpressStreamWriter(res);
|
|
251
286
|
const messageId = generateId('msg');
|
|
287
|
+
const textId = generateId('txt');
|
|
288
|
+
|
|
289
|
+
// Use res.on('close') (not req.on('close'), which fires once the body is read)
|
|
290
|
+
res.on('close', () => writer.close());
|
|
252
291
|
|
|
253
292
|
writer.start(messageId);
|
|
254
|
-
writer.textStart(
|
|
293
|
+
writer.textStart(textId);
|
|
255
294
|
|
|
256
295
|
// Your streaming logic
|
|
257
|
-
writer.textDelta(
|
|
258
|
-
writer.textDelta(
|
|
296
|
+
writer.textDelta(textId, 'Hello ');
|
|
297
|
+
writer.textDelta(textId, 'World!');
|
|
259
298
|
|
|
260
|
-
writer.textEnd(
|
|
299
|
+
writer.textEnd(textId);
|
|
261
300
|
writer.finish(messageId);
|
|
262
301
|
writer.close();
|
|
263
302
|
});
|
|
@@ -271,6 +310,7 @@ router.post('/custom/stream', async (req, res) => {
|
|
|
271
310
|
class CogitatorServer {
|
|
272
311
|
constructor(options: CogitatorServerConfig);
|
|
273
312
|
init(): Promise<void>;
|
|
313
|
+
attachWebSocket(server: http.Server): Promise<WebSocketServer>;
|
|
274
314
|
readonly isInitialized: boolean;
|
|
275
315
|
}
|
|
276
316
|
```
|
|
@@ -293,7 +333,6 @@ interface CogitatorServerConfig {
|
|
|
293
333
|
cors?: CorsConfig;
|
|
294
334
|
swagger?: SwaggerConfig;
|
|
295
335
|
websocket?: WebSocketConfig;
|
|
296
|
-
requestTimeout?: number; // Default: 30000
|
|
297
336
|
};
|
|
298
337
|
}
|
|
299
338
|
```
|
|
@@ -316,9 +355,12 @@ class ExpressStreamWriter {
|
|
|
316
355
|
error(message: string, code?: string): void;
|
|
317
356
|
finish(messageId: string, usage?: Usage): void;
|
|
318
357
|
close(): void;
|
|
358
|
+
readonly isClosed: boolean;
|
|
319
359
|
}
|
|
320
360
|
```
|
|
321
361
|
|
|
362
|
+
Writes after the response has ended (or the client disconnected) are ignored.
|
|
363
|
+
|
|
322
364
|
## Error Handling
|
|
323
365
|
|
|
324
366
|
All endpoints return consistent error responses:
|
|
@@ -334,13 +376,17 @@ All endpoints return consistent error responses:
|
|
|
334
376
|
|
|
335
377
|
Error codes map to HTTP status codes:
|
|
336
378
|
|
|
337
|
-
- `INVALID_INPUT` → 400
|
|
379
|
+
- `INVALID_INPUT` → 400 (validation errors and malformed JSON bodies)
|
|
338
380
|
- `UNAUTHORIZED` → 401
|
|
339
|
-
- `PERMISSION_DENIED` → 403
|
|
340
381
|
- `NOT_FOUND` → 404
|
|
382
|
+
- `PAYLOAD_TOO_LARGE` → 413
|
|
341
383
|
- `RATE_LIMIT_EXCEEDED` → 429
|
|
342
|
-
- `
|
|
343
|
-
- `
|
|
384
|
+
- `WORKFLOW_FAILED` → 500
|
|
385
|
+
- `INTERNAL` → 500 (details are logged, not returned)
|
|
386
|
+
- `UNIMPLEMENTED` → 501 (optional workflows/swarms package missing)
|
|
387
|
+
- `UNAVAILABLE` → 503 (memory not configured)
|
|
388
|
+
|
|
389
|
+
`CogitatorError`s thrown by runs keep their own code and use its mapped status (for example `LLM_RATE_LIMITED` → 429).
|
|
344
390
|
|
|
345
391
|
## License
|
|
346
392
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"cors.d.ts","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAkB9C,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,IAU7C,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAmCxD"}
|
package/dist/middleware/cors.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
function isOriginAllowed(origin, allowed) {
|
|
2
|
-
if (!origin)
|
|
3
|
-
return false;
|
|
4
2
|
if (typeof allowed === 'string') {
|
|
5
3
|
return allowed === '*' || allowed === origin;
|
|
6
4
|
}
|
|
@@ -13,32 +11,33 @@ function isOriginAllowed(origin, allowed) {
|
|
|
13
11
|
return false;
|
|
14
12
|
}
|
|
15
13
|
export function createCorsMiddleware(config) {
|
|
16
|
-
const { origin, credentials =
|
|
14
|
+
const { origin, credentials = origin !== '*', methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], allowedHeaders = ['Content-Type', 'Authorization', 'X-Request-ID'], exposedHeaders = ['X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-RateLimit-Reset'], maxAge = 86400, } = config;
|
|
17
15
|
return (req, res, next) => {
|
|
18
16
|
const requestOrigin = req.headers.origin;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
26
|
-
}
|
|
17
|
+
let allowed = false;
|
|
18
|
+
if (origin === '*' && !credentials) {
|
|
19
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
20
|
+
allowed = true;
|
|
27
21
|
}
|
|
28
|
-
else
|
|
29
|
-
res.setHeader('Access-Control-Allow-Origin', requestOrigin);
|
|
22
|
+
else {
|
|
30
23
|
res.setHeader('Vary', 'Origin');
|
|
24
|
+
if (requestOrigin && (origin === '*' || isOriginAllowed(requestOrigin, origin))) {
|
|
25
|
+
res.setHeader('Access-Control-Allow-Origin', requestOrigin);
|
|
26
|
+
allowed = true;
|
|
27
|
+
}
|
|
31
28
|
}
|
|
32
|
-
if (credentials) {
|
|
29
|
+
if (allowed && credentials) {
|
|
33
30
|
res.setHeader('Access-Control-Allow-Credentials', 'true');
|
|
34
31
|
}
|
|
35
|
-
if (exposedHeaders.length > 0) {
|
|
32
|
+
if (allowed && exposedHeaders.length > 0) {
|
|
36
33
|
res.setHeader('Access-Control-Expose-Headers', exposedHeaders.join(', '));
|
|
37
34
|
}
|
|
38
35
|
if (req.method === 'OPTIONS') {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
if (allowed) {
|
|
37
|
+
res.setHeader('Access-Control-Allow-Methods', methods.join(', '));
|
|
38
|
+
res.setHeader('Access-Control-Allow-Headers', allowedHeaders.join(', '));
|
|
39
|
+
res.setHeader('Access-Control-Max-Age', maxAge.toString());
|
|
40
|
+
}
|
|
42
41
|
res.status(204).end();
|
|
43
42
|
return;
|
|
44
43
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cors.js","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAGA,SAAS,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"cors.js","sourceRoot":"","sources":["../../src/middleware/cors.ts"],"names":[],"mappings":"AAGA,SAAS,eAAe,CAAC,MAAc,EAAE,OAA6B;IACpE,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,MAAM,CAAC;IAC/C,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAkB;IACrD,MAAM,EACJ,MAAM,EACN,WAAW,GAAG,MAAM,KAAK,GAAG,EAC5B,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,EAC9D,cAAc,GAAG,CAAC,cAAc,EAAE,eAAe,EAAE,cAAc,CAAC,EAClE,cAAc,GAAG,CAAC,mBAAmB,EAAE,uBAAuB,EAAE,mBAAmB,CAAC,EACpF,MAAM,GAAG,KAAK,GACf,GAAG,MAAM,CAAC;IAEX,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,MAAM,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;QACzC,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,IAAI,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;YACnC,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;YAClD,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAChC,IAAI,aAAa,IAAI,CAAC,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;gBAChF,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,aAAa,CAAC,CAAC;gBAC5D,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QAED,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;YAC3B,GAAG,CAAC,SAAS,CAAC,kCAAkC,EAAE,MAAM,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,OAAO,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,SAAS,CAAC,+BAA+B,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5E,CAAC;QAED,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,OAAO,EAAE,CAAC;gBACZ,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAClE,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBACzE,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC7D,CAAC;YACD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAgC/D,wBAAgB,YAAY,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAqChG;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,IAAI,CAOlE"}
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
import { CogitatorError, ERROR_STATUS_CODES, ErrorCode } from '@cogitator-ai/types';
|
|
2
|
+
const CLIENT_ERROR_CODES = {
|
|
3
|
+
400: 'INVALID_INPUT',
|
|
4
|
+
401: 'UNAUTHORIZED',
|
|
5
|
+
403: 'FORBIDDEN',
|
|
6
|
+
404: 'NOT_FOUND',
|
|
7
|
+
413: 'PAYLOAD_TOO_LARGE',
|
|
8
|
+
415: 'UNSUPPORTED_MEDIA_TYPE',
|
|
9
|
+
429: 'RATE_LIMIT_EXCEEDED',
|
|
10
|
+
};
|
|
11
|
+
function asClientError(err) {
|
|
12
|
+
if (typeof err !== 'object' || err === null)
|
|
13
|
+
return null;
|
|
14
|
+
const rawStatus = Reflect.get(err, 'status') ?? Reflect.get(err, 'statusCode');
|
|
15
|
+
if (typeof rawStatus !== 'number' || rawStatus < 400 || rawStatus >= 500)
|
|
16
|
+
return null;
|
|
17
|
+
const message = Reflect.get(err, 'message');
|
|
18
|
+
const type = Reflect.get(err, 'type');
|
|
19
|
+
return {
|
|
20
|
+
status: rawStatus,
|
|
21
|
+
message: typeof message === 'string' ? message : 'Bad request',
|
|
22
|
+
type: typeof type === 'string' ? type : undefined,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
2
25
|
export function errorHandler(err, _req, res, _next) {
|
|
3
26
|
if (res.headersSent) {
|
|
4
27
|
return;
|
|
@@ -13,6 +36,17 @@ export function errorHandler(err, _req, res, _next) {
|
|
|
13
36
|
});
|
|
14
37
|
return;
|
|
15
38
|
}
|
|
39
|
+
const clientError = asClientError(err);
|
|
40
|
+
if (clientError) {
|
|
41
|
+
const message = clientError.type === 'entity.parse.failed' ? 'Invalid JSON body' : clientError.message;
|
|
42
|
+
res.status(clientError.status).json({
|
|
43
|
+
error: {
|
|
44
|
+
message,
|
|
45
|
+
code: CLIENT_ERROR_CODES[clientError.status] ?? 'BAD_REQUEST',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
16
50
|
console.error('[CogitatorServer] Unhandled error:', err);
|
|
17
51
|
res.status(500).json({
|
|
18
52
|
error: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAW,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAW,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAsBrE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,eAAe,IA4BvD,KAAK,gBAAgB,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAoCjE"}
|
|
@@ -2,15 +2,25 @@ function buildKeyGenerator(trustProxy) {
|
|
|
2
2
|
return (req) => {
|
|
3
3
|
if (trustProxy) {
|
|
4
4
|
const forwarded = req.headers['x-forwarded-for'];
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
const hops = (Array.isArray(forwarded) ? forwarded.join(',') : (forwarded ?? ''))
|
|
6
|
+
.split(',')
|
|
7
|
+
.map((hop) => hop.trim())
|
|
8
|
+
.filter(Boolean);
|
|
9
|
+
const nearest = hops.at(-1);
|
|
10
|
+
if (nearest)
|
|
11
|
+
return nearest;
|
|
8
12
|
}
|
|
9
|
-
return req.ip || req.socket.remoteAddress ||
|
|
13
|
+
return req.ip || req.socket.remoteAddress || 'unknown';
|
|
10
14
|
};
|
|
11
15
|
}
|
|
12
16
|
export function createRateLimitMiddleware(config) {
|
|
13
17
|
const { windowMs, max, message = 'Too many requests, please try again later', keyGenerator = buildKeyGenerator(config.trustProxy ?? false), skip, } = config;
|
|
18
|
+
if (!Number.isFinite(windowMs) || windowMs <= 0) {
|
|
19
|
+
throw new Error('RateLimitConfig.windowMs must be a positive number');
|
|
20
|
+
}
|
|
21
|
+
if (!Number.isFinite(max) || max < 0) {
|
|
22
|
+
throw new Error('RateLimitConfig.max must be a non-negative number');
|
|
23
|
+
}
|
|
14
24
|
const store = new Map();
|
|
15
25
|
const cleanup = setInterval(() => {
|
|
16
26
|
const now = Date.now();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAQA,SAAS,iBAAiB,CAAC,UAAmB;IAC5C,OAAO,CAAC,GAAY,EAAU,EAAE;QAC9B,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;YACjD,IAAI,OAAO,SAAS,
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EACV,YAAY,
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,aAAa,CAAC;AAUrB,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAuJ3D"}
|
package/dist/routes/agents.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import { ExpressStreamWriter, setupSSEHeaders, generateId } from '../streaming/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { handleRouteError, onClientDisconnect, parseRunBody, resolveError, sendError, } from './utils.js';
|
|
4
4
|
export function createAgentRoutes(ctx) {
|
|
5
5
|
const router = Router();
|
|
6
|
+
const findAgent = (name) => Object.hasOwn(ctx.agents, name) ? ctx.agents[name] : undefined;
|
|
6
7
|
router.get('/agents', (_req, res) => {
|
|
7
8
|
const agentList = Object.entries(ctx.agents).map(([name, agent]) => ({
|
|
8
9
|
name,
|
|
9
|
-
description: agent.config.
|
|
10
|
+
description: agent.config.description,
|
|
10
11
|
tools: agent.config.tools?.map((t) => t.name) || [],
|
|
11
12
|
}));
|
|
12
13
|
const response = { agents: agentList };
|
|
@@ -14,25 +15,26 @@ export function createAgentRoutes(ctx) {
|
|
|
14
15
|
});
|
|
15
16
|
router.post('/agents/:name/run', async (req, res) => {
|
|
16
17
|
const { name } = req.params;
|
|
17
|
-
const agent =
|
|
18
|
+
const agent = findAgent(name);
|
|
18
19
|
if (!agent) {
|
|
19
|
-
res
|
|
20
|
-
error: { message: `Agent '${name}' not found`, code: 'NOT_FOUND' },
|
|
21
|
-
});
|
|
20
|
+
sendError(res, 404, `Agent '${name}' not found`, 'NOT_FOUND');
|
|
22
21
|
return;
|
|
23
22
|
}
|
|
24
|
-
const
|
|
25
|
-
if (!
|
|
26
|
-
res.
|
|
27
|
-
error: { message: 'Missing required field: input', code: 'INVALID_INPUT' },
|
|
28
|
-
});
|
|
23
|
+
const parsed = parseRunBody(req.body);
|
|
24
|
+
if (!parsed.ok) {
|
|
25
|
+
sendError(res, 400, parsed.message, 'INVALID_INPUT');
|
|
29
26
|
return;
|
|
30
27
|
}
|
|
28
|
+
const body = parsed.value;
|
|
29
|
+
const abortController = new AbortController();
|
|
30
|
+
onClientDisconnect(res, () => abortController.abort());
|
|
31
31
|
try {
|
|
32
32
|
const result = await ctx.cogitator.run(agent, {
|
|
33
33
|
input: body.input,
|
|
34
34
|
context: body.context,
|
|
35
35
|
threadId: body.threadId,
|
|
36
|
+
userId: req.cogitator?.auth?.userId,
|
|
37
|
+
signal: abortController.signal,
|
|
36
38
|
});
|
|
37
39
|
const response = {
|
|
38
40
|
output: result.output,
|
|
@@ -47,60 +49,76 @@ export function createAgentRoutes(ctx) {
|
|
|
47
49
|
res.json(response);
|
|
48
50
|
}
|
|
49
51
|
catch (error) {
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
else {
|
|
54
|
-
console.error('[CogitatorServer] Agent run error:', error);
|
|
55
|
-
res.status(500).json({ error: { message: 'Internal server error', code: 'INTERNAL' } });
|
|
56
|
-
}
|
|
52
|
+
if (abortController.signal.aborted)
|
|
53
|
+
return;
|
|
54
|
+
handleRouteError(res, error, 'Agent run error');
|
|
57
55
|
}
|
|
58
56
|
});
|
|
59
57
|
router.post('/agents/:name/stream', async (req, res) => {
|
|
60
58
|
const { name } = req.params;
|
|
61
|
-
const agent =
|
|
59
|
+
const agent = findAgent(name);
|
|
62
60
|
if (!agent) {
|
|
63
|
-
res
|
|
64
|
-
error: { message: `Agent '${name}' not found`, code: 'NOT_FOUND' },
|
|
65
|
-
});
|
|
61
|
+
sendError(res, 404, `Agent '${name}' not found`, 'NOT_FOUND');
|
|
66
62
|
return;
|
|
67
63
|
}
|
|
68
|
-
const
|
|
69
|
-
if (!
|
|
70
|
-
res.
|
|
71
|
-
error: { message: 'Missing required field: input', code: 'INVALID_INPUT' },
|
|
72
|
-
});
|
|
64
|
+
const parsed = parseRunBody(req.body);
|
|
65
|
+
if (!parsed.ok) {
|
|
66
|
+
sendError(res, 400, parsed.message, 'INVALID_INPUT');
|
|
73
67
|
return;
|
|
74
68
|
}
|
|
69
|
+
const body = parsed.value;
|
|
75
70
|
setupSSEHeaders(res);
|
|
76
71
|
const writer = new ExpressStreamWriter(res);
|
|
77
72
|
const messageId = generateId('msg');
|
|
78
|
-
|
|
73
|
+
const abortController = new AbortController();
|
|
74
|
+
onClientDisconnect(res, () => {
|
|
75
|
+
abortController.abort();
|
|
79
76
|
writer.close();
|
|
80
77
|
});
|
|
78
|
+
let textId = null;
|
|
79
|
+
let streamedText = false;
|
|
80
|
+
const writeText = (delta) => {
|
|
81
|
+
if (!delta)
|
|
82
|
+
return;
|
|
83
|
+
if (textId === null) {
|
|
84
|
+
textId = generateId('txt');
|
|
85
|
+
writer.textStart(textId);
|
|
86
|
+
}
|
|
87
|
+
writer.textDelta(textId, delta);
|
|
88
|
+
};
|
|
89
|
+
const endText = () => {
|
|
90
|
+
if (textId === null)
|
|
91
|
+
return;
|
|
92
|
+
writer.textEnd(textId);
|
|
93
|
+
textId = null;
|
|
94
|
+
};
|
|
81
95
|
try {
|
|
82
96
|
writer.start(messageId);
|
|
83
|
-
const textId = generateId('txt');
|
|
84
|
-
writer.textStart(textId);
|
|
85
97
|
const result = await ctx.cogitator.run(agent, {
|
|
86
98
|
input: body.input,
|
|
87
99
|
context: body.context,
|
|
88
100
|
threadId: body.threadId,
|
|
101
|
+
userId: req.cogitator?.auth?.userId,
|
|
102
|
+
signal: abortController.signal,
|
|
89
103
|
stream: true,
|
|
90
104
|
onToken: (token) => {
|
|
91
|
-
|
|
105
|
+
if (token)
|
|
106
|
+
streamedText = true;
|
|
107
|
+
writeText(token);
|
|
92
108
|
},
|
|
93
109
|
onToolCall: (toolCall) => {
|
|
94
|
-
|
|
95
|
-
writer.toolCallStart(
|
|
96
|
-
writer.
|
|
110
|
+
endText();
|
|
111
|
+
writer.toolCallStart(toolCall.id, toolCall.name);
|
|
112
|
+
writer.toolCallDelta(toolCall.id, JSON.stringify(toolCall.arguments));
|
|
113
|
+
writer.toolCallEnd(toolCall.id);
|
|
97
114
|
},
|
|
98
115
|
onToolResult: (toolResult) => {
|
|
99
|
-
|
|
100
|
-
writer.toolResult(resultId, toolResult.callId, toolResult.result);
|
|
116
|
+
writer.toolResult(generateId('res'), toolResult.callId, toolResult.result);
|
|
101
117
|
},
|
|
102
118
|
});
|
|
103
|
-
|
|
119
|
+
if (!streamedText)
|
|
120
|
+
writeText(result.output);
|
|
121
|
+
endText();
|
|
104
122
|
writer.finish(messageId, {
|
|
105
123
|
inputTokens: result.usage.inputTokens,
|
|
106
124
|
outputTokens: result.usage.outputTokens,
|
|
@@ -108,12 +126,10 @@ export function createAgentRoutes(ctx) {
|
|
|
108
126
|
});
|
|
109
127
|
}
|
|
110
128
|
catch (error) {
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
console.error('[CogitatorServer] Agent stream error:', error);
|
|
116
|
-
writer.error('Internal server error', 'INTERNAL');
|
|
129
|
+
if (!abortController.signal.aborted) {
|
|
130
|
+
const resolved = resolveError(error, 'Agent stream error');
|
|
131
|
+
endText();
|
|
132
|
+
writer.error(resolved.message, resolved.code);
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
135
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swarms.d.ts","sourceRoot":"","sources":["../../src/routes/swarms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EACV,YAAY,
|
|
1
|
+
{"version":3,"file":"swarms.d.ts","sourceRoot":"","sources":["../../src/routes/swarms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,OAAO,KAAK,EACV,YAAY,EAKb,MAAM,aAAa,CAAC;AAcrB,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CA4M3D"}
|