@microsoft/agents-hosting-express 1.6.0-beta.24.g285b02c21b → 1.6.0-beta.30.g545c41a189
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 +112 -11
- package/dist/package.json +5 -3
- package/dist/src/createAgentRequestHandler.d.ts +57 -0
- package/dist/src/createAgentRequestHandler.js +63 -0
- package/dist/src/createAgentRequestHandler.js.map +1 -0
- package/dist/src/createCloudAdapter.d.ts +36 -0
- package/dist/src/createCloudAdapter.js +45 -0
- package/dist/src/createCloudAdapter.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/startServer.d.ts +77 -11
- package/dist/src/startServer.js +43 -45
- package/dist/src/startServer.js.map +1 -1
- package/package.json +5 -3
- package/src/createAgentRequestHandler.ts +91 -0
- package/src/createCloudAdapter.ts +52 -0
- package/src/index.ts +2 -0
- package/src/startServer.ts +125 -24
package/README.md
CHANGED
|
@@ -2,17 +2,118 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
Provides integration to host
|
|
5
|
+
Provides integration to host agents with Express. The package offers three levels of abstraction:
|
|
6
|
+
|
|
7
|
+
- **`startServer`** — Quickest way to get running. Creates an Express app, wires up auth, and starts listening.
|
|
8
|
+
- **`createAgentRequestHandler`** — A handler signature without Express imports, for frameworks that can provide Express-compatible request/response objects.
|
|
9
|
+
- **`createCloudAdapter`** — Low-level factory to get a `CloudAdapter` for full control over request processing.
|
|
6
10
|
|
|
7
11
|
## Usage
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
### Basic — `startServer`
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
17
|
+
import { startServer } from '@microsoft/agents-hosting-express';
|
|
18
|
+
|
|
19
|
+
const app = new AgentApplication<TurnState>();
|
|
20
|
+
app.onActivity('message', async (context) => {
|
|
21
|
+
await context.sendActivity('Hello, world!');
|
|
22
|
+
});
|
|
23
|
+
startServer(app);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Custom routes alongside the agent endpoint
|
|
27
|
+
|
|
28
|
+
Use the `beforeListen` hook to add routes before the server starts listening:
|
|
29
|
+
|
|
30
|
+
_**Note:** The agent messages route is registered *after* this callback.
|
|
31
|
+
Avoid adding catch-all routes (e.g., `app.all('*', ...)` or `app.use('*', ...)`)
|
|
32
|
+
here, as they will shadow the messages endpoint._
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
36
|
+
import { startServer } from '@microsoft/agents-hosting-express';
|
|
37
|
+
|
|
38
|
+
const agent = new AgentApplication<TurnState>();
|
|
39
|
+
|
|
40
|
+
startServer(agent, {
|
|
41
|
+
port: 8080,
|
|
42
|
+
routePath: '/bot/messages',
|
|
43
|
+
beforeListen: (app) => {
|
|
44
|
+
app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Custom Express setup — `createAgentRequestHandler`
|
|
50
|
+
|
|
51
|
+
If you manage your own Express app (or another framework with an adapter that exposes Express-compatible request/response objects), use `createAgentRequestHandler` to get a handler that includes JWT authorization and activity processing:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import express from 'express';
|
|
55
|
+
import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
56
|
+
import { createAgentRequestHandler } from '@microsoft/agents-hosting-express';
|
|
57
|
+
|
|
58
|
+
const agent = new AgentApplication<TurnState>();
|
|
59
|
+
const handler = createAgentRequestHandler(agent);
|
|
60
|
+
|
|
61
|
+
const app = express();
|
|
62
|
+
app.use(express.json());
|
|
63
|
+
app.post('/api/messages', handler);
|
|
64
|
+
app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
65
|
+
app.listen(3978);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Advanced — `createCloudAdapter`
|
|
69
|
+
|
|
70
|
+
For full control, use `createCloudAdapter` to obtain the `CloudAdapter` directly. This is useful when you need to customize request processing and can provide the request/response shape expected by `CloudAdapter.process`.
|
|
71
|
+
|
|
72
|
+
`CloudAdapter.process` expects:
|
|
73
|
+
|
|
74
|
+
- a parsed activity body on `req.body` (for example, via `express.json()` in Express)
|
|
75
|
+
- a response object that supports `status()`, `setHeader()`, `send()`, and `end()`
|
|
76
|
+
|
|
77
|
+
If your framework does not expose those members directly, add an adapter layer before calling `adapter.process`.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import http from 'node:http';
|
|
81
|
+
import { AgentApplication, TurnState, getAuthConfigWithDefaults } from '@microsoft/agents-hosting';
|
|
82
|
+
import { createCloudAdapter } from '@microsoft/agents-hosting-express';
|
|
83
|
+
|
|
84
|
+
const agent = new AgentApplication<TurnState>();
|
|
85
|
+
const authConfig = getAuthConfigWithDefaults();
|
|
86
|
+
const { adapter, headerPropagation } = createCloudAdapter(agent, authConfig);
|
|
87
|
+
|
|
88
|
+
const server = http.createServer(async (req, res) => {
|
|
89
|
+
if (req.method === 'POST' && req.url === '/api/messages') {
|
|
90
|
+
// Adapt request/response as needed so they match CloudAdapter.process expectations.
|
|
91
|
+
await adapter.process(req as any, res as any, (context) => agent.run(context), headerPropagation);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
server.listen(3978);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Setting up auth middleware manually
|
|
98
|
+
|
|
99
|
+
If you need to apply JWT authorization on specific routes in your own Express app:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import express from 'express';
|
|
103
|
+
import { authorizeJWT, getAuthConfigWithDefaults, AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
104
|
+
import { createCloudAdapter } from '@microsoft/agents-hosting-express';
|
|
105
|
+
|
|
106
|
+
const agent = new AgentApplication<TurnState>();
|
|
107
|
+
const authConfig = getAuthConfigWithDefaults();
|
|
108
|
+
const { adapter, headerPropagation } = createCloudAdapter(agent, authConfig);
|
|
109
|
+
|
|
110
|
+
const app = express();
|
|
111
|
+
app.use(express.json());
|
|
112
|
+
|
|
113
|
+
// JWT is applied only on the agent route — custom routes remain unauthenticated
|
|
114
|
+
app.post('/api/messages', authorizeJWT(authConfig), (req, res) =>
|
|
115
|
+
adapter.process(req, res, (context) => agent.run(context), headerPropagation)
|
|
116
|
+
);
|
|
117
|
+
app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
118
|
+
app.listen(3978);
|
|
119
|
+
```
|
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@microsoft/agents-hosting-express",
|
|
4
|
-
"version": "1.6.0-beta.
|
|
4
|
+
"version": "1.6.0-beta.30.g545c41a189",
|
|
5
5
|
"homepage": "https://github.com/microsoft/Agents-for-js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
"main": "dist/src/index.js",
|
|
20
20
|
"types": "dist/src/index.d.ts",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@microsoft/agents-hosting": "1.6.0-beta.
|
|
23
|
-
"
|
|
22
|
+
"@microsoft/agents-hosting": "1.6.0-beta.30.g545c41a189",
|
|
23
|
+
"@microsoft/agents-telemetry": "1.6.0-beta.30.g545c41a189",
|
|
24
|
+
"express": "5.2.1",
|
|
25
|
+
"express-rate-limit": "8.5.2"
|
|
24
26
|
},
|
|
25
27
|
"license": "MIT",
|
|
26
28
|
"files": [
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { ActivityHandler, AgentApplication, AuthConfiguration, Request, TurnState } from '@microsoft/agents-hosting';
|
|
6
|
+
/**
|
|
7
|
+
* Minimal response interface describing the methods used by the Agent request handler.
|
|
8
|
+
* Any framework whose response object satisfies this shape is compatible.
|
|
9
|
+
*/
|
|
10
|
+
export interface WebResponse {
|
|
11
|
+
status(code: number): this;
|
|
12
|
+
setHeader(name: string, value: string): this;
|
|
13
|
+
send(body?: unknown): this;
|
|
14
|
+
end(): this;
|
|
15
|
+
headersSent: boolean;
|
|
16
|
+
writableEnded: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A request handler function signature that does not import Express types in its public API.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* Runtime processing still depends on the request/response behavior expected by `authorizeJWT` and `CloudAdapter.process`.
|
|
23
|
+
* Use this with Express directly, or with adapter layers that provide compatible request/response objects.
|
|
24
|
+
*/
|
|
25
|
+
export type AgentRequestHandler = (req: Request, res: WebResponse) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a request handler for processing Agent activities.
|
|
28
|
+
*
|
|
29
|
+
* This exposes a handler signature without requiring Express types in consumer code.
|
|
30
|
+
* It can be used with Express directly, or with frameworks that provide adapted objects compatible with
|
|
31
|
+
* the requirements of `authorizeJWT` and `CloudAdapter.process`.
|
|
32
|
+
*
|
|
33
|
+
* JWT authorization is applied within the handler before processing the activity.
|
|
34
|
+
* Requests must provide a parsed activity payload at `req.body`.
|
|
35
|
+
*
|
|
36
|
+
* @param agent - The AgentApplication or ActivityHandler instance to process incoming activities.
|
|
37
|
+
* @param authConfiguration - Optional custom authentication configuration. If not provided,
|
|
38
|
+
* configuration will be loaded from environment variables using loadAuthConfigFromEnv().
|
|
39
|
+
* @returns A request handler function `(req, res) => Promise<void>`.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* import express from 'express';
|
|
44
|
+
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
45
|
+
* import { createAgentRequestHandler } from '@microsoft/agents-hosting-express';
|
|
46
|
+
*
|
|
47
|
+
* const agent = new AgentApplication<TurnState>();
|
|
48
|
+
* const handler = createAgentRequestHandler(agent);
|
|
49
|
+
*
|
|
50
|
+
* const app = express();
|
|
51
|
+
* app.use(express.json());
|
|
52
|
+
* app.post('/api/messages', handler);
|
|
53
|
+
* app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
54
|
+
* app.listen(3978);
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare const createAgentRequestHandler: (agent: AgentApplication<TurnState<any, any>> | ActivityHandler, authConfiguration?: AuthConfiguration) => AgentRequestHandler;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createAgentRequestHandler = void 0;
|
|
8
|
+
const agents_hosting_1 = require("@microsoft/agents-hosting");
|
|
9
|
+
const createCloudAdapter_1 = require("./createCloudAdapter");
|
|
10
|
+
/**
|
|
11
|
+
* Creates a request handler for processing Agent activities.
|
|
12
|
+
*
|
|
13
|
+
* This exposes a handler signature without requiring Express types in consumer code.
|
|
14
|
+
* It can be used with Express directly, or with frameworks that provide adapted objects compatible with
|
|
15
|
+
* the requirements of `authorizeJWT` and `CloudAdapter.process`.
|
|
16
|
+
*
|
|
17
|
+
* JWT authorization is applied within the handler before processing the activity.
|
|
18
|
+
* Requests must provide a parsed activity payload at `req.body`.
|
|
19
|
+
*
|
|
20
|
+
* @param agent - The AgentApplication or ActivityHandler instance to process incoming activities.
|
|
21
|
+
* @param authConfiguration - Optional custom authentication configuration. If not provided,
|
|
22
|
+
* configuration will be loaded from environment variables using loadAuthConfigFromEnv().
|
|
23
|
+
* @returns A request handler function `(req, res) => Promise<void>`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import express from 'express';
|
|
28
|
+
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
29
|
+
* import { createAgentRequestHandler } from '@microsoft/agents-hosting-express';
|
|
30
|
+
*
|
|
31
|
+
* const agent = new AgentApplication<TurnState>();
|
|
32
|
+
* const handler = createAgentRequestHandler(agent);
|
|
33
|
+
*
|
|
34
|
+
* const app = express();
|
|
35
|
+
* app.use(express.json());
|
|
36
|
+
* app.post('/api/messages', handler);
|
|
37
|
+
* app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
38
|
+
* app.listen(3978);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
const createAgentRequestHandler = (agent, authConfiguration) => {
|
|
42
|
+
const authConfig = (0, agents_hosting_1.getAuthConfigWithDefaults)(authConfiguration);
|
|
43
|
+
const { adapter, headerPropagation } = (0, createCloudAdapter_1.createCloudAdapter)(agent, authConfig);
|
|
44
|
+
const jwtMiddleware = (0, agents_hosting_1.authorizeJWT)(authConfig);
|
|
45
|
+
return async (req, res) => {
|
|
46
|
+
let middlewareError;
|
|
47
|
+
let nextCalled = false;
|
|
48
|
+
await jwtMiddleware(req, res, (err) => {
|
|
49
|
+
nextCalled = true;
|
|
50
|
+
middlewareError = err;
|
|
51
|
+
});
|
|
52
|
+
if (middlewareError) {
|
|
53
|
+
throw middlewareError;
|
|
54
|
+
}
|
|
55
|
+
// If the middleware handled the response without calling next (e.g., 401), don't process the activity.
|
|
56
|
+
if (!nextCalled || res.headersSent) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await adapter.process(req, res, (context) => agent.run(context), headerPropagation);
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
exports.createAgentRequestHandler = createAgentRequestHandler;
|
|
63
|
+
//# sourceMappingURL=createAgentRequestHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAgentRequestHandler.js","sourceRoot":"","sources":["../../src/createAgentRequestHandler.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,8DAA6J;AAC7J,6DAAyD;AAwBzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACI,MAAM,yBAAyB,GAAG,CACvC,KAA8D,EAC9D,iBAAqC,EAChB,EAAE;IACvB,MAAM,UAAU,GAAG,IAAA,0CAAyB,EAAC,iBAAiB,CAAC,CAAA;IAC/D,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAA,uCAAkB,EAAC,KAAK,EAAE,UAAU,CAAC,CAAA;IAC5E,MAAM,aAAa,GAAG,IAAA,6BAAY,EAAC,UAAU,CAAC,CAAA;IAE9C,OAAO,KAAK,EAAE,GAAY,EAAE,GAAgB,EAAiB,EAAE;QAC7D,IAAI,eAAoB,CAAA;QACxB,IAAI,UAAU,GAAG,KAAK,CAAA;QAEtB,MAAM,aAAa,CAAC,GAAG,EAAE,GAAe,EAAE,CAAC,GAAS,EAAE,EAAE;YACtD,UAAU,GAAG,IAAI,CAAA;YACjB,eAAe,GAAG,GAAG,CAAA;QACvB,CAAC,CAAC,CAAA;QAEF,IAAI,eAAe,EAAE,CAAC;YACpB,MAAM,eAAe,CAAA;QACvB,CAAC;QAED,uGAAuG;QACvG,IAAI,CAAC,UAAU,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;YACnC,OAAM;QACR,CAAC;QAED,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,GAAe,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAA;IACjG,CAAC,CAAA;AACH,CAAC,CAAA;AA5BY,QAAA,yBAAyB,6BA4BrC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
import { ActivityHandler, AgentApplication, AuthConfiguration, CloudAdapter, HeaderPropagationDefinition, TurnState } from '@microsoft/agents-hosting';
|
|
6
|
+
/**
|
|
7
|
+
* Result of creating a CloudAdapter from an agent.
|
|
8
|
+
*/
|
|
9
|
+
export interface CloudAdapterResult {
|
|
10
|
+
adapter: CloudAdapter;
|
|
11
|
+
headerPropagation: HeaderPropagationDefinition | undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a CloudAdapter for the given agent.
|
|
15
|
+
*
|
|
16
|
+
* If the agent is an AgentApplication with a pre-configured adapter, that adapter is reused.
|
|
17
|
+
* Otherwise, a new CloudAdapter is created.
|
|
18
|
+
*
|
|
19
|
+
* @param agent - The AgentApplication or ActivityHandler instance.
|
|
20
|
+
* @param authConfig - Optional auth configuration used when creating a new CloudAdapter.
|
|
21
|
+
* If the agent already has an adapter, that adapter is reused and this value is ignored.
|
|
22
|
+
* @returns An object containing the CloudAdapter and optional header propagation configuration.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
27
|
+
* import { createCloudAdapter } from '@microsoft/agents-hosting-express';
|
|
28
|
+
*
|
|
29
|
+
* const app = new AgentApplication<TurnState>();
|
|
30
|
+
* const { adapter, headerPropagation } = createCloudAdapter(app, { clientId: process.env.CLIENT_ID });
|
|
31
|
+
*
|
|
32
|
+
* // Use the adapter directly with request/response objects compatible with CloudAdapter.process
|
|
33
|
+
* adapter.process(req, res, (context) => app.run(context), headerPropagation);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare const createCloudAdapter: (agent: AgentApplication<TurnState<any, any>> | ActivityHandler, authConfig?: AuthConfiguration) => CloudAdapterResult;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
4
|
+
* Licensed under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createCloudAdapter = void 0;
|
|
8
|
+
const agents_hosting_1 = require("@microsoft/agents-hosting");
|
|
9
|
+
/**
|
|
10
|
+
* Creates a CloudAdapter for the given agent.
|
|
11
|
+
*
|
|
12
|
+
* If the agent is an AgentApplication with a pre-configured adapter, that adapter is reused.
|
|
13
|
+
* Otherwise, a new CloudAdapter is created.
|
|
14
|
+
*
|
|
15
|
+
* @param agent - The AgentApplication or ActivityHandler instance.
|
|
16
|
+
* @param authConfig - Optional auth configuration used when creating a new CloudAdapter.
|
|
17
|
+
* If the agent already has an adapter, that adapter is reused and this value is ignored.
|
|
18
|
+
* @returns An object containing the CloudAdapter and optional header propagation configuration.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
23
|
+
* import { createCloudAdapter } from '@microsoft/agents-hosting-express';
|
|
24
|
+
*
|
|
25
|
+
* const app = new AgentApplication<TurnState>();
|
|
26
|
+
* const { adapter, headerPropagation } = createCloudAdapter(app, { clientId: process.env.CLIENT_ID });
|
|
27
|
+
*
|
|
28
|
+
* // Use the adapter directly with request/response objects compatible with CloudAdapter.process
|
|
29
|
+
* adapter.process(req, res, (context) => app.run(context), headerPropagation);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
const createCloudAdapter = (agent, authConfig) => {
|
|
33
|
+
let adapter;
|
|
34
|
+
let headerPropagation;
|
|
35
|
+
if (agent instanceof agents_hosting_1.ActivityHandler || !agent.adapter) {
|
|
36
|
+
adapter = new agents_hosting_1.CloudAdapter(authConfig);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
adapter = agent.adapter;
|
|
40
|
+
headerPropagation = agent === null || agent === void 0 ? void 0 : agent.options.headerPropagation;
|
|
41
|
+
}
|
|
42
|
+
return { adapter, headerPropagation };
|
|
43
|
+
};
|
|
44
|
+
exports.createCloudAdapter = createCloudAdapter;
|
|
45
|
+
//# sourceMappingURL=createCloudAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createCloudAdapter.js","sourceRoot":"","sources":["../../src/createCloudAdapter.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,8DAAsJ;AAUtJ;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACI,MAAM,kBAAkB,GAAG,CAChC,KAA8D,EAC9D,UAA8B,EACV,EAAE;IACtB,IAAI,OAAqB,CAAA;IACzB,IAAI,iBAA0D,CAAA;IAC9D,IAAI,KAAK,YAAY,gCAAe,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACvD,OAAO,GAAG,IAAI,6BAAY,CAAC,UAAU,CAAC,CAAA;IACxC,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,KAAK,CAAC,OAAuB,CAAA;QACvC,iBAAiB,GAAI,KAA+C,aAA/C,KAAK,uBAAL,KAAK,CAA4C,OAAO,CAAC,iBAAiB,CAAA;IACjG,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAA;AACvC,CAAC,CAAA;AAbY,QAAA,kBAAkB,sBAa9B"}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -15,4 +15,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./startServer"), exports);
|
|
18
|
+
__exportStar(require("./createCloudAdapter"), exports);
|
|
19
|
+
__exportStar(require("./createAgentRequestHandler"), exports);
|
|
18
20
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gDAA6B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gDAA6B;AAC7B,uDAAoC;AACpC,8DAA2C"}
|
|
@@ -4,32 +4,98 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import express from 'express';
|
|
6
6
|
import { ActivityHandler, AgentApplication, AuthConfiguration, TurnState } from '@microsoft/agents-hosting';
|
|
7
|
+
/**
|
|
8
|
+
* Options for configuring the Express server started by `startServer`.
|
|
9
|
+
*/
|
|
10
|
+
export interface StartServerOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Optional custom authentication configuration.
|
|
13
|
+
* If not provided, configuration will be loaded from environment variables using loadAuthConfigFromEnv().
|
|
14
|
+
*/
|
|
15
|
+
authConfig?: AuthConfiguration;
|
|
16
|
+
/**
|
|
17
|
+
* The port to listen on. Defaults to `process.env.PORT` or `3978`.
|
|
18
|
+
*/
|
|
19
|
+
port?: number | string;
|
|
20
|
+
/**
|
|
21
|
+
* The route path for the agent messages endpoint. Defaults to `'/api/messages'`.
|
|
22
|
+
*/
|
|
23
|
+
routePath?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional rate limiting configuration for the messages endpoint.
|
|
26
|
+
* If not provided, no rate limiting is applied.
|
|
27
|
+
* Specify windowMs (in milliseconds) and max (number of requests) to enable rate limiting.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* startServer(agent, {
|
|
32
|
+
* rateLimitOptions: {
|
|
33
|
+
* windowMs: 15 * 60 * 1000, // 15 minutes
|
|
34
|
+
* max: 1000 // limit each IP to 1000 requests per windowMs
|
|
35
|
+
* }
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
rateLimitOptions?: {
|
|
40
|
+
windowMs: number;
|
|
41
|
+
max: number;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* A callback invoked with the Express app before `listen()` is called.
|
|
45
|
+
* Use this to add custom routes, middleware, or static file serving.
|
|
46
|
+
*
|
|
47
|
+
* **Note:** The agent messages route is registered *after* this callback.
|
|
48
|
+
* Avoid adding catch-all routes (e.g., `app.all('*', ...)` or `app.use('*', ...)`)
|
|
49
|
+
* here, as they will shadow the messages endpoint.
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* startServer(agent, {
|
|
53
|
+
* beforeListen: (app) => {
|
|
54
|
+
* app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
55
|
+
* }
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
beforeListen?: (app: express.Express) => void;
|
|
60
|
+
}
|
|
7
61
|
/**
|
|
8
62
|
* Starts an Express server for handling Agent requests.
|
|
9
63
|
*
|
|
10
64
|
* @param agent - The AgentApplication or ActivityHandler instance to process incoming activities.
|
|
11
|
-
* @param
|
|
12
|
-
*
|
|
13
|
-
* @returns
|
|
65
|
+
* @param options - Optional configuration. Accepts either a `StartServerOptions` object or
|
|
66
|
+
* an `AuthConfiguration` for backward compatibility.
|
|
67
|
+
* @returns The Express server instance.
|
|
14
68
|
*
|
|
15
69
|
* @remarks
|
|
16
70
|
* This function sets up an Express server with the necessary middleware and routes for handling
|
|
17
|
-
* agent requests. It configures JWT authorization middleware and sets up the
|
|
18
|
-
* The server will listen on the port specified in the PORT environment variable
|
|
19
|
-
* and logs startup information including the SDK version and configured app ID.
|
|
71
|
+
* agent requests. It configures JWT authorization middleware on the messages route and sets up the endpoint.
|
|
72
|
+
* The server will listen on the port specified in options, the PORT environment variable, or 3978 by default.
|
|
20
73
|
*
|
|
21
74
|
* @example
|
|
22
75
|
* ```typescript
|
|
76
|
+
* // Basic usage
|
|
23
77
|
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
24
78
|
* import { startServer } from '@microsoft/agents-hosting-express';
|
|
25
79
|
*
|
|
26
80
|
* const app = new AgentApplication<TurnState>();
|
|
27
|
-
* app.onMessage('hello', async (context, state) => {
|
|
28
|
-
* await context.sendActivity('Hello, world!');
|
|
29
|
-
* });
|
|
30
|
-
*
|
|
31
81
|
* startServer(app);
|
|
32
82
|
* ```
|
|
33
83
|
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* // With options
|
|
87
|
+
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
88
|
+
* import { startServer } from '@microsoft/agents-hosting-express';
|
|
89
|
+
*
|
|
90
|
+
* const app = new AgentApplication<TurnState>();
|
|
91
|
+
* startServer(app, {
|
|
92
|
+
* port: 8080,
|
|
93
|
+
* routePath: '/bot/messages',
|
|
94
|
+
* beforeListen: (server) => {
|
|
95
|
+
* server.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
96
|
+
* }
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
34
99
|
*/
|
|
35
|
-
export declare
|
|
100
|
+
export declare function startServer(agent: AgentApplication<TurnState<any, any>> | ActivityHandler, options?: StartServerOptions): express.Express;
|
|
101
|
+
export declare function startServer(agent: AgentApplication<TurnState<any, any>> | ActivityHandler, authConfiguration?: AuthConfiguration): express.Express;
|
package/dist/src/startServer.js
CHANGED
|
@@ -7,58 +7,56 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
7
7
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
8
|
};
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.startServer =
|
|
10
|
+
exports.startServer = startServer;
|
|
11
11
|
const express_1 = __importDefault(require("express"));
|
|
12
|
+
const express_rate_limit_1 = __importDefault(require("express-rate-limit"));
|
|
12
13
|
const agents_hosting_1 = require("@microsoft/agents-hosting");
|
|
13
14
|
const package_json_1 = require("@microsoft/agents-hosting/package.json");
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
* The server will listen on the port specified in the PORT environment variable (or 3978 by default)
|
|
26
|
-
* and logs startup information including the SDK version and configured app ID.
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```typescript
|
|
30
|
-
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
31
|
-
* import { startServer } from '@microsoft/agents-hosting-express';
|
|
32
|
-
*
|
|
33
|
-
* const app = new AgentApplication<TurnState>();
|
|
34
|
-
* app.onMessage('hello', async (context, state) => {
|
|
35
|
-
* await context.sendActivity('Hello, world!');
|
|
36
|
-
* });
|
|
37
|
-
*
|
|
38
|
-
* startServer(app);
|
|
39
|
-
* ```
|
|
40
|
-
*
|
|
41
|
-
*/
|
|
42
|
-
const startServer = (agent, authConfiguration) => {
|
|
43
|
-
const authConfig = (0, agents_hosting_1.getAuthConfigWithDefaults)(authConfiguration);
|
|
44
|
-
let adapter;
|
|
45
|
-
let headerPropagation;
|
|
46
|
-
if (agent instanceof agents_hosting_1.ActivityHandler || !agent.adapter) {
|
|
47
|
-
adapter = new agents_hosting_1.CloudAdapter();
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
adapter = agent.adapter;
|
|
51
|
-
headerPropagation = agent === null || agent === void 0 ? void 0 : agent.options.headerPropagation;
|
|
52
|
-
}
|
|
15
|
+
const agents_telemetry_1 = require("@microsoft/agents-telemetry");
|
|
16
|
+
const createCloudAdapter_1 = require("./createCloudAdapter");
|
|
17
|
+
const logger = (0, agents_telemetry_1.debug)('agents:hosting-express');
|
|
18
|
+
function startServer(agent, optionsOrAuth) {
|
|
19
|
+
var _a, _b, _c;
|
|
20
|
+
const isOptions = typeof optionsOrAuth === 'object' && optionsOrAuth !== null &&
|
|
21
|
+
('authConfig' in optionsOrAuth || 'port' in optionsOrAuth || 'routePath' in optionsOrAuth || 'rateLimitOptions' in optionsOrAuth || 'beforeListen' in optionsOrAuth);
|
|
22
|
+
const opts = isOptions ? optionsOrAuth : { authConfig: optionsOrAuth };
|
|
23
|
+
const authConfig = (0, agents_hosting_1.getAuthConfigWithDefaults)(opts.authConfig);
|
|
24
|
+
const routePath = (_a = opts.routePath) !== null && _a !== void 0 ? _a : '/api/messages';
|
|
25
|
+
const { adapter, headerPropagation } = (0, createCloudAdapter_1.createCloudAdapter)(agent, authConfig);
|
|
53
26
|
const server = (0, express_1.default)();
|
|
54
27
|
server.use(express_1.default.json());
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
28
|
+
if (opts.beforeListen && typeof opts.beforeListen === 'function') {
|
|
29
|
+
opts.beforeListen(server);
|
|
30
|
+
}
|
|
31
|
+
const middlewares = [(0, agents_hosting_1.authorizeJWT)(authConfig)];
|
|
32
|
+
if (opts.rateLimitOptions) {
|
|
33
|
+
const messagesRateLimiter = (0, express_rate_limit_1.default)({
|
|
34
|
+
windowMs: opts.rateLimitOptions.windowMs,
|
|
35
|
+
max: opts.rateLimitOptions.max,
|
|
36
|
+
standardHeaders: true,
|
|
37
|
+
legacyHeaders: false
|
|
38
|
+
});
|
|
39
|
+
middlewares.unshift(messagesRateLimiter);
|
|
40
|
+
}
|
|
41
|
+
server.post(routePath, ...middlewares, (req, res) => adapter.process(req, res, (context) => agent.run(context), headerPropagation));
|
|
42
|
+
const port = (_c = (_b = opts.port) !== null && _b !== void 0 ? _b : process.env.PORT) !== null && _c !== void 0 ? _c : 3978;
|
|
43
|
+
const className = (obj) => { var _a, _b; return (_b = (_a = obj === null || obj === void 0 ? void 0 : obj.constructor) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : (obj ? 'custom' : undefined); };
|
|
44
|
+
logger.info('Express server settings loaded', {
|
|
45
|
+
messageEndpoint: `POST ${routePath}`,
|
|
46
|
+
port: {
|
|
47
|
+
value: port,
|
|
48
|
+
source: opts.port ? 'options' : process.env.PORT ? 'env' : 'default',
|
|
49
|
+
},
|
|
50
|
+
middlewares: ['express.json', 'authorizeJWT'],
|
|
51
|
+
adapter: {
|
|
52
|
+
className: className(adapter),
|
|
53
|
+
source: agent instanceof agents_hosting_1.ActivityHandler || !agent.adapter ? 'created' : 'agent.adapter',
|
|
54
|
+
},
|
|
55
|
+
headerPropagation: headerPropagation !== undefined ? 'enabled' : 'disabled',
|
|
56
|
+
});
|
|
58
57
|
server.listen(port, async () => {
|
|
59
58
|
console.log(`\nServer listening to port ${port} on sdk ${package_json_1.version} for appId ${authConfig.clientId} debug ${process.env.DEBUG}`);
|
|
60
59
|
}).on('error', console.error);
|
|
61
60
|
return server;
|
|
62
|
-
}
|
|
63
|
-
exports.startServer = startServer;
|
|
61
|
+
}
|
|
64
62
|
//# sourceMappingURL=startServer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startServer.js","sourceRoot":"","sources":["../../src/startServer.ts"],"names":[],"mappings":";AAAA;;;GAGG
|
|
1
|
+
{"version":3,"file":"startServer.js","sourceRoot":"","sources":["../../src/startServer.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@microsoft/agents-hosting-express",
|
|
4
|
-
"version": "1.6.0-beta.
|
|
4
|
+
"version": "1.6.0-beta.30.g545c41a189",
|
|
5
5
|
"homepage": "https://github.com/microsoft/Agents-for-js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -19,8 +19,10 @@
|
|
|
19
19
|
"main": "dist/src/index.js",
|
|
20
20
|
"types": "dist/src/index.d.ts",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@microsoft/agents-hosting": "1.6.0-beta.
|
|
23
|
-
"
|
|
22
|
+
"@microsoft/agents-hosting": "1.6.0-beta.30.g545c41a189",
|
|
23
|
+
"@microsoft/agents-telemetry": "1.6.0-beta.30.g545c41a189",
|
|
24
|
+
"express": "5.2.1",
|
|
25
|
+
"express-rate-limit": "8.5.2"
|
|
24
26
|
},
|
|
25
27
|
"license": "MIT",
|
|
26
28
|
"files": [
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { type Response } from 'express'
|
|
7
|
+
import { ActivityHandler, AgentApplication, AuthConfiguration, authorizeJWT, getAuthConfigWithDefaults, Request, TurnState } from '@microsoft/agents-hosting'
|
|
8
|
+
import { createCloudAdapter } from './createCloudAdapter'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Minimal response interface describing the methods used by the Agent request handler.
|
|
12
|
+
* Any framework whose response object satisfies this shape is compatible.
|
|
13
|
+
*/
|
|
14
|
+
export interface WebResponse {
|
|
15
|
+
status (code: number): this
|
|
16
|
+
setHeader (name: string, value: string): this
|
|
17
|
+
send (body?: unknown): this
|
|
18
|
+
end (): this
|
|
19
|
+
headersSent: boolean
|
|
20
|
+
writableEnded: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A request handler function signature that does not import Express types in its public API.
|
|
25
|
+
*
|
|
26
|
+
* @remarks
|
|
27
|
+
* Runtime processing still depends on the request/response behavior expected by `authorizeJWT` and `CloudAdapter.process`.
|
|
28
|
+
* Use this with Express directly, or with adapter layers that provide compatible request/response objects.
|
|
29
|
+
*/
|
|
30
|
+
export type AgentRequestHandler = (req: Request, res: WebResponse) => Promise<void>
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates a request handler for processing Agent activities.
|
|
34
|
+
*
|
|
35
|
+
* This exposes a handler signature without requiring Express types in consumer code.
|
|
36
|
+
* It can be used with Express directly, or with frameworks that provide adapted objects compatible with
|
|
37
|
+
* the requirements of `authorizeJWT` and `CloudAdapter.process`.
|
|
38
|
+
*
|
|
39
|
+
* JWT authorization is applied within the handler before processing the activity.
|
|
40
|
+
* Requests must provide a parsed activity payload at `req.body`.
|
|
41
|
+
*
|
|
42
|
+
* @param agent - The AgentApplication or ActivityHandler instance to process incoming activities.
|
|
43
|
+
* @param authConfiguration - Optional custom authentication configuration. If not provided,
|
|
44
|
+
* configuration will be loaded from environment variables using loadAuthConfigFromEnv().
|
|
45
|
+
* @returns A request handler function `(req, res) => Promise<void>`.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import express from 'express';
|
|
50
|
+
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
51
|
+
* import { createAgentRequestHandler } from '@microsoft/agents-hosting-express';
|
|
52
|
+
*
|
|
53
|
+
* const agent = new AgentApplication<TurnState>();
|
|
54
|
+
* const handler = createAgentRequestHandler(agent);
|
|
55
|
+
*
|
|
56
|
+
* const app = express();
|
|
57
|
+
* app.use(express.json());
|
|
58
|
+
* app.post('/api/messages', handler);
|
|
59
|
+
* app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
60
|
+
* app.listen(3978);
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export const createAgentRequestHandler = (
|
|
64
|
+
agent: AgentApplication<TurnState<any, any>> | ActivityHandler,
|
|
65
|
+
authConfiguration?: AuthConfiguration
|
|
66
|
+
): AgentRequestHandler => {
|
|
67
|
+
const authConfig = getAuthConfigWithDefaults(authConfiguration)
|
|
68
|
+
const { adapter, headerPropagation } = createCloudAdapter(agent, authConfig)
|
|
69
|
+
const jwtMiddleware = authorizeJWT(authConfig)
|
|
70
|
+
|
|
71
|
+
return async (req: Request, res: WebResponse): Promise<void> => {
|
|
72
|
+
let middlewareError: any
|
|
73
|
+
let nextCalled = false
|
|
74
|
+
|
|
75
|
+
await jwtMiddleware(req, res as Response, (err?: any) => {
|
|
76
|
+
nextCalled = true
|
|
77
|
+
middlewareError = err
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
if (middlewareError) {
|
|
81
|
+
throw middlewareError
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// If the middleware handled the response without calling next (e.g., 401), don't process the activity.
|
|
85
|
+
if (!nextCalled || res.headersSent) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await adapter.process(req, res as Response, (context) => agent.run(context), headerPropagation)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { ActivityHandler, AgentApplication, AuthConfiguration, CloudAdapter, HeaderPropagationDefinition, TurnState } from '@microsoft/agents-hosting'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Result of creating a CloudAdapter from an agent.
|
|
10
|
+
*/
|
|
11
|
+
export interface CloudAdapterResult {
|
|
12
|
+
adapter: CloudAdapter
|
|
13
|
+
headerPropagation: HeaderPropagationDefinition | undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a CloudAdapter for the given agent.
|
|
18
|
+
*
|
|
19
|
+
* If the agent is an AgentApplication with a pre-configured adapter, that adapter is reused.
|
|
20
|
+
* Otherwise, a new CloudAdapter is created.
|
|
21
|
+
*
|
|
22
|
+
* @param agent - The AgentApplication or ActivityHandler instance.
|
|
23
|
+
* @param authConfig - Optional auth configuration used when creating a new CloudAdapter.
|
|
24
|
+
* If the agent already has an adapter, that adapter is reused and this value is ignored.
|
|
25
|
+
* @returns An object containing the CloudAdapter and optional header propagation configuration.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
30
|
+
* import { createCloudAdapter } from '@microsoft/agents-hosting-express';
|
|
31
|
+
*
|
|
32
|
+
* const app = new AgentApplication<TurnState>();
|
|
33
|
+
* const { adapter, headerPropagation } = createCloudAdapter(app, { clientId: process.env.CLIENT_ID });
|
|
34
|
+
*
|
|
35
|
+
* // Use the adapter directly with request/response objects compatible with CloudAdapter.process
|
|
36
|
+
* adapter.process(req, res, (context) => app.run(context), headerPropagation);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export const createCloudAdapter = (
|
|
40
|
+
agent: AgentApplication<TurnState<any, any>> | ActivityHandler,
|
|
41
|
+
authConfig?: AuthConfiguration
|
|
42
|
+
): CloudAdapterResult => {
|
|
43
|
+
let adapter: CloudAdapter
|
|
44
|
+
let headerPropagation: HeaderPropagationDefinition | undefined
|
|
45
|
+
if (agent instanceof ActivityHandler || !agent.adapter) {
|
|
46
|
+
adapter = new CloudAdapter(authConfig)
|
|
47
|
+
} else {
|
|
48
|
+
adapter = agent.adapter as CloudAdapter
|
|
49
|
+
headerPropagation = (agent as AgentApplication<TurnState<any, any>>)?.options.headerPropagation
|
|
50
|
+
}
|
|
51
|
+
return { adapter, headerPropagation }
|
|
52
|
+
}
|
package/src/index.ts
CHANGED
package/src/startServer.ts
CHANGED
|
@@ -4,58 +4,159 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import express, { Response } from 'express'
|
|
7
|
-
import
|
|
7
|
+
import rateLimit from 'express-rate-limit'
|
|
8
|
+
import { ActivityHandler, AgentApplication, AuthConfiguration, authorizeJWT, getAuthConfigWithDefaults, Request, TurnState } from '@microsoft/agents-hosting'
|
|
8
9
|
import { version } from '@microsoft/agents-hosting/package.json'
|
|
10
|
+
import { debug } from '@microsoft/agents-telemetry'
|
|
11
|
+
import { createCloudAdapter } from './createCloudAdapter'
|
|
12
|
+
|
|
13
|
+
const logger = debug('agents:hosting-express')
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Options for configuring the Express server started by `startServer`.
|
|
17
|
+
*/
|
|
18
|
+
export interface StartServerOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Optional custom authentication configuration.
|
|
21
|
+
* If not provided, configuration will be loaded from environment variables using loadAuthConfigFromEnv().
|
|
22
|
+
*/
|
|
23
|
+
authConfig?: AuthConfiguration
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The port to listen on. Defaults to `process.env.PORT` or `3978`.
|
|
27
|
+
*/
|
|
28
|
+
port?: number | string
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The route path for the agent messages endpoint. Defaults to `'/api/messages'`.
|
|
32
|
+
*/
|
|
33
|
+
routePath?: string
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Optional rate limiting configuration for the messages endpoint.
|
|
37
|
+
* If not provided, no rate limiting is applied.
|
|
38
|
+
* Specify windowMs (in milliseconds) and max (number of requests) to enable rate limiting.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* startServer(agent, {
|
|
43
|
+
* rateLimitOptions: {
|
|
44
|
+
* windowMs: 15 * 60 * 1000, // 15 minutes
|
|
45
|
+
* max: 1000 // limit each IP to 1000 requests per windowMs
|
|
46
|
+
* }
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
rateLimitOptions?: { windowMs: number; max: number }
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A callback invoked with the Express app before `listen()` is called.
|
|
54
|
+
* Use this to add custom routes, middleware, or static file serving.
|
|
55
|
+
*
|
|
56
|
+
* **Note:** The agent messages route is registered *after* this callback.
|
|
57
|
+
* Avoid adding catch-all routes (e.g., `app.all('*', ...)` or `app.use('*', ...)`)
|
|
58
|
+
* here, as they will shadow the messages endpoint.
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* startServer(agent, {
|
|
62
|
+
* beforeListen: (app) => {
|
|
63
|
+
* app.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
64
|
+
* }
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
beforeListen?: (app: express.Express) => void
|
|
69
|
+
}
|
|
70
|
+
|
|
9
71
|
/**
|
|
10
72
|
* Starts an Express server for handling Agent requests.
|
|
11
73
|
*
|
|
12
74
|
* @param agent - The AgentApplication or ActivityHandler instance to process incoming activities.
|
|
13
|
-
* @param
|
|
14
|
-
*
|
|
15
|
-
* @returns
|
|
75
|
+
* @param options - Optional configuration. Accepts either a `StartServerOptions` object or
|
|
76
|
+
* an `AuthConfiguration` for backward compatibility.
|
|
77
|
+
* @returns The Express server instance.
|
|
16
78
|
*
|
|
17
79
|
* @remarks
|
|
18
80
|
* This function sets up an Express server with the necessary middleware and routes for handling
|
|
19
|
-
* agent requests. It configures JWT authorization middleware and sets up the
|
|
20
|
-
* The server will listen on the port specified in the PORT environment variable
|
|
21
|
-
* and logs startup information including the SDK version and configured app ID.
|
|
81
|
+
* agent requests. It configures JWT authorization middleware on the messages route and sets up the endpoint.
|
|
82
|
+
* The server will listen on the port specified in options, the PORT environment variable, or 3978 by default.
|
|
22
83
|
*
|
|
23
84
|
* @example
|
|
24
85
|
* ```typescript
|
|
86
|
+
* // Basic usage
|
|
25
87
|
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
26
88
|
* import { startServer } from '@microsoft/agents-hosting-express';
|
|
27
89
|
*
|
|
28
90
|
* const app = new AgentApplication<TurnState>();
|
|
29
|
-
* app.onMessage('hello', async (context, state) => {
|
|
30
|
-
* await context.sendActivity('Hello, world!');
|
|
31
|
-
* });
|
|
32
|
-
*
|
|
33
91
|
* startServer(app);
|
|
34
92
|
* ```
|
|
35
93
|
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* // With options
|
|
97
|
+
* import { AgentApplication, TurnState } from '@microsoft/agents-hosting';
|
|
98
|
+
* import { startServer } from '@microsoft/agents-hosting-express';
|
|
99
|
+
*
|
|
100
|
+
* const app = new AgentApplication<TurnState>();
|
|
101
|
+
* startServer(app, {
|
|
102
|
+
* port: 8080,
|
|
103
|
+
* routePath: '/bot/messages',
|
|
104
|
+
* beforeListen: (server) => {
|
|
105
|
+
* server.get('/health', (req, res) => res.json({ status: 'ok' }));
|
|
106
|
+
* }
|
|
107
|
+
* });
|
|
108
|
+
* ```
|
|
36
109
|
*/
|
|
37
|
-
export
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
110
|
+
export function startServer (agent: AgentApplication<TurnState<any, any>> | ActivityHandler, options?: StartServerOptions): express.Express
|
|
111
|
+
export function startServer (agent: AgentApplication<TurnState<any, any>> | ActivityHandler, authConfiguration?: AuthConfiguration): express.Express
|
|
112
|
+
export function startServer (agent: AgentApplication<TurnState<any, any>> | ActivityHandler, optionsOrAuth?: StartServerOptions | AuthConfiguration): express.Express {
|
|
113
|
+
const isOptions = typeof optionsOrAuth === 'object' && optionsOrAuth !== null &&
|
|
114
|
+
('authConfig' in optionsOrAuth || 'port' in optionsOrAuth || 'routePath' in optionsOrAuth || 'rateLimitOptions' in optionsOrAuth || 'beforeListen' in optionsOrAuth)
|
|
115
|
+
|
|
116
|
+
const opts: StartServerOptions = isOptions ? optionsOrAuth as StartServerOptions : { authConfig: optionsOrAuth as AuthConfiguration | undefined }
|
|
117
|
+
const authConfig: AuthConfiguration = getAuthConfigWithDefaults(opts.authConfig)
|
|
118
|
+
const routePath = opts.routePath ?? '/api/messages'
|
|
119
|
+
const { adapter, headerPropagation } = createCloudAdapter(agent, authConfig)
|
|
47
120
|
|
|
48
121
|
const server = express()
|
|
49
122
|
server.use(express.json())
|
|
50
|
-
server.use(authorizeJWT(authConfig))
|
|
51
123
|
|
|
52
|
-
|
|
124
|
+
if (opts.beforeListen && typeof opts.beforeListen === 'function') {
|
|
125
|
+
opts.beforeListen(server)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const middlewares: express.RequestHandler[] = [authorizeJWT(authConfig)]
|
|
129
|
+
if (opts.rateLimitOptions) {
|
|
130
|
+
const messagesRateLimiter = rateLimit({
|
|
131
|
+
windowMs: opts.rateLimitOptions.windowMs,
|
|
132
|
+
max: opts.rateLimitOptions.max,
|
|
133
|
+
standardHeaders: true,
|
|
134
|
+
legacyHeaders: false
|
|
135
|
+
})
|
|
136
|
+
middlewares.unshift(messagesRateLimiter)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
server.post(routePath, ...middlewares, (req: Request, res: Response) =>
|
|
53
140
|
adapter.process(req, res, (context) =>
|
|
54
141
|
agent.run(context)
|
|
55
142
|
, headerPropagation)
|
|
56
143
|
)
|
|
57
144
|
|
|
58
|
-
const port = process.env.PORT
|
|
145
|
+
const port = opts.port ?? process.env.PORT ?? 3978
|
|
146
|
+
const className = (obj: any) => obj?.constructor?.name ?? (obj ? 'custom' : undefined)
|
|
147
|
+
logger.info('Express server settings loaded', {
|
|
148
|
+
messageEndpoint: `POST ${routePath}`,
|
|
149
|
+
port: {
|
|
150
|
+
value: port,
|
|
151
|
+
source: opts.port ? 'options' : process.env.PORT ? 'env' : 'default',
|
|
152
|
+
},
|
|
153
|
+
middlewares: ['express.json', 'authorizeJWT'],
|
|
154
|
+
adapter: {
|
|
155
|
+
className: className(adapter),
|
|
156
|
+
source: agent instanceof ActivityHandler || !agent.adapter ? 'created' : 'agent.adapter',
|
|
157
|
+
},
|
|
158
|
+
headerPropagation: headerPropagation !== undefined ? 'enabled' : 'disabled',
|
|
159
|
+
})
|
|
59
160
|
server.listen(port, async () => {
|
|
60
161
|
console.log(`\nServer listening to port ${port} on sdk ${version} for appId ${authConfig.clientId} debug ${process.env.DEBUG}`)
|
|
61
162
|
}).on('error', console.error)
|