@1mcp/agent 0.10.3 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -17
- package/build/auth/authManager.d.ts +109 -0
- package/build/auth/authManager.d.ts.map +1 -0
- package/build/auth/authManager.js +173 -0
- package/build/auth/authManager.js.map +1 -0
- package/build/auth/authMiddleware.d.ts +16 -0
- package/build/auth/authMiddleware.d.ts.map +1 -0
- package/build/auth/authMiddleware.js +45 -0
- package/build/auth/authMiddleware.js.map +1 -0
- package/build/auth/sessionManager.d.ts +197 -0
- package/build/auth/sessionManager.d.ts.map +1 -0
- package/build/auth/sessionManager.js +408 -0
- package/build/auth/sessionManager.js.map +1 -0
- package/build/capabilities/capabilityManager.d.ts +1 -1
- package/build/capabilities/capabilityManager.d.ts.map +1 -1
- package/build/capabilities/capabilityManager.js +39 -6
- package/build/capabilities/capabilityManager.js.map +1 -1
- package/build/config/configManager.d.ts +7 -1
- package/build/config/configManager.d.ts.map +1 -1
- package/build/config/configManager.js +24 -2
- package/build/config/configManager.js.map +1 -1
- package/build/constants.d.ts +25 -1
- package/build/constants.d.ts.map +1 -1
- package/build/constants.js +31 -2
- package/build/constants.js.map +1 -1
- package/build/{client.d.ts → core/client/clientFactory.d.ts} +1 -1
- package/build/core/client/clientFactory.d.ts.map +1 -0
- package/build/{client.js → core/client/clientFactory.js} +2 -2
- package/build/core/client/clientFactory.js.map +1 -0
- package/build/{clients → core/client}/clientManager.d.ts +3 -2
- package/build/core/client/clientManager.d.ts.map +1 -0
- package/build/{clients → core/client}/clientManager.js +10 -6
- package/build/core/client/clientManager.js.map +1 -0
- package/build/core/server/serverConfig.d.ts +100 -0
- package/build/core/server/serverConfig.d.ts.map +1 -0
- package/build/core/server/serverConfig.js +117 -0
- package/build/core/server/serverConfig.js.map +1 -0
- package/build/{serverManager.d.ts → core/server/serverManager.d.ts} +7 -2
- package/build/core/server/serverManager.d.ts.map +1 -0
- package/build/core/server/serverManager.js +159 -0
- package/build/core/server/serverManager.js.map +1 -0
- package/build/core/types/client.d.ts +38 -0
- package/build/core/types/client.d.ts.map +1 -0
- package/build/core/types/client.js +13 -0
- package/build/core/types/client.js.map +1 -0
- package/build/core/types/index.d.ts +4 -0
- package/build/core/types/index.d.ts.map +1 -0
- package/build/core/types/index.js +5 -0
- package/build/core/types/index.js.map +1 -0
- package/build/core/types/server.d.ts +14 -0
- package/build/core/types/server.d.ts.map +1 -0
- package/build/core/types/server.js +2 -0
- package/build/core/types/server.js.map +1 -0
- package/build/{types.d.ts → core/types/transport.d.ts} +1 -48
- package/build/core/types/transport.d.ts.map +1 -0
- package/build/{types.js → core/types/transport.js} +1 -13
- package/build/core/types/transport.js.map +1 -0
- package/build/handlers/notificationHandlers.d.ts +1 -1
- package/build/handlers/notificationHandlers.d.ts.map +1 -1
- package/build/handlers/notificationHandlers.js +41 -15
- package/build/handlers/notificationHandlers.js.map +1 -1
- package/build/handlers/requestHandlers.d.ts +1 -1
- package/build/handlers/requestHandlers.d.ts.map +1 -1
- package/build/handlers/requestHandlers.js +65 -2
- package/build/handlers/requestHandlers.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +44 -5
- package/build/index.js.map +1 -1
- package/build/logger/logger.js +1 -1
- package/build/logger/logger.js.map +1 -1
- package/build/server.d.ts +1 -1
- package/build/server.d.ts.map +1 -1
- package/build/server.js +6 -3
- package/build/server.js.map +1 -1
- package/build/services/configReloadService.d.ts +15 -5
- package/build/services/configReloadService.d.ts.map +1 -1
- package/build/services/configReloadService.js +28 -11
- package/build/services/configReloadService.js.map +1 -1
- package/build/{config/transportConfig.d.ts → transport/config.d.ts} +2 -2
- package/build/transport/config.d.ts.map +1 -0
- package/build/{config/transportConfig.js → transport/config.js} +2 -2
- package/build/transport/config.js.map +1 -0
- package/build/transport/http/errorHandler.d.ts.map +1 -0
- package/build/{server → transport/http}/errorHandler.js +1 -1
- package/build/transport/http/errorHandler.js.map +1 -0
- package/build/transport/http/middleware/loggingMiddleware.d.ts.map +1 -0
- package/build/{middleware → transport/http/middleware}/loggingMiddleware.js +11 -7
- package/build/transport/http/middleware/loggingMiddleware.js.map +1 -0
- package/build/transport/http/routes/oauthRoutes.d.ts +14 -0
- package/build/transport/http/routes/oauthRoutes.d.ts.map +1 -0
- package/build/transport/http/routes/oauthRoutes.js +132 -0
- package/build/transport/http/routes/oauthRoutes.js.map +1 -0
- package/build/transport/http/routes/sseRoutes.d.ts +4 -0
- package/build/transport/http/routes/sseRoutes.d.ts.map +1 -0
- package/build/transport/http/routes/sseRoutes.js +64 -0
- package/build/transport/http/routes/sseRoutes.js.map +1 -0
- package/build/transport/http/routes/streamableHttpRoutes.d.ts +4 -0
- package/build/transport/http/routes/streamableHttpRoutes.d.ts.map +1 -0
- package/build/transport/http/routes/streamableHttpRoutes.js +121 -0
- package/build/transport/http/routes/streamableHttpRoutes.js.map +1 -0
- package/build/transport/http/server.d.ts +69 -0
- package/build/transport/http/server.d.ts.map +1 -0
- package/build/transport/http/server.js +115 -0
- package/build/transport/http/server.js.map +1 -0
- package/build/utils/clientFiltering.d.ts +1 -1
- package/build/utils/clientFiltering.d.ts.map +1 -1
- package/build/utils/errorHandling.d.ts +1 -0
- package/build/utils/errorHandling.d.ts.map +1 -1
- package/build/utils/errorHandling.js +29 -5
- package/build/utils/errorHandling.js.map +1 -1
- package/build/utils/errorTypes.d.ts +4 -1
- package/build/utils/errorTypes.d.ts.map +1 -1
- package/build/utils/errorTypes.js +12 -4
- package/build/utils/errorTypes.js.map +1 -1
- package/build/utils/pagination.d.ts +1 -1
- package/build/utils/pagination.d.ts.map +1 -1
- package/build/utils/pagination.js +84 -5
- package/build/utils/pagination.js.map +1 -1
- package/build/utils/tagsExtractor.d.ts.map +1 -0
- package/build/utils/tagsExtractor.js.map +1 -0
- package/package.json +14 -5
- package/build/client.d.ts.map +0 -1
- package/build/client.js.map +0 -1
- package/build/clients/clientManager.d.ts.map +0 -1
- package/build/clients/clientManager.js.map +0 -1
- package/build/config/transportConfig.d.ts.map +0 -1
- package/build/config/transportConfig.js.map +0 -1
- package/build/middleware/loggingMiddleware.d.ts.map +0 -1
- package/build/middleware/loggingMiddleware.js.map +0 -1
- package/build/server/errorHandler.d.ts.map +0 -1
- package/build/server/errorHandler.js.map +0 -1
- package/build/server/expressServer.d.ts +0 -11
- package/build/server/expressServer.d.ts.map +0 -1
- package/build/server/expressServer.js +0 -177
- package/build/server/expressServer.js.map +0 -1
- package/build/server/tagsExtractor.d.ts.map +0 -1
- package/build/server/tagsExtractor.js.map +0 -1
- package/build/serverManager.d.ts.map +0 -1
- package/build/serverManager.js +0 -87
- package/build/serverManager.js.map +0 -1
- package/build/types.d.ts.map +0 -1
- package/build/types.js.map +0 -1
- /package/build/{server → transport/http}/errorHandler.d.ts +0 -0
- /package/build/{middleware → transport/http/middleware}/loggingMiddleware.d.ts +0 -0
- /package/build/{server → utils}/tagsExtractor.d.ts +0 -0
- /package/build/{server → utils}/tagsExtractor.js +0 -0
package/README.md
CHANGED
|
@@ -22,19 +22,21 @@ A unified Model Context Protocol server implementation that aggregates multiple
|
|
|
22
22
|
To enable Cursor to use existing MCP servers already configured in Claude Desktop, follow these steps:
|
|
23
23
|
|
|
24
24
|
1. Run the 1MCP server with the Claude Desktop config file:
|
|
25
|
+
|
|
25
26
|
```bash
|
|
26
27
|
npx -y @1mcp/agent --config ~/Library/Application\ Support/Claude/claude_desktop_config.json
|
|
27
28
|
```
|
|
28
29
|
|
|
29
30
|
2. Add the 1MCP server to your Cursor config file (`~/.cursor/mcp.json`):
|
|
31
|
+
|
|
30
32
|
```json
|
|
31
33
|
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"1mcp": {
|
|
36
|
+
"type": "http",
|
|
37
|
+
"url": "http://localhost:3050/sse"
|
|
37
38
|
}
|
|
39
|
+
}
|
|
38
40
|
}
|
|
39
41
|
```
|
|
40
42
|
|
|
@@ -59,14 +61,20 @@ npx -y @1mcp/agent --help
|
|
|
59
61
|
```
|
|
60
62
|
|
|
61
63
|
Available options:
|
|
64
|
+
|
|
62
65
|
- `--transport, -t`: Choose transport type ("stdio" or "http", default: "http")
|
|
63
66
|
- `--config, -c`: Use a specific config file
|
|
64
67
|
- `--port, -P`: Change HTTP port (default: 3050)
|
|
65
68
|
- `--host, -H`: Change HTTP host (default: localhost)
|
|
66
69
|
- `--tags, -g`: Filter servers by tags (see Tags section below)
|
|
70
|
+
- `--pagination, -p`: Enable pagination for client/server lists (boolean, default: false)
|
|
71
|
+
- `--auth`: Enable authentication (OAuth 2.1) (boolean, default: false)
|
|
72
|
+
- `--session-ttl`: Session expiry time in minutes (number, default: 1440)
|
|
73
|
+
- `--session-storage-path`: Custom session storage directory path (string, default: undefined)
|
|
67
74
|
- `--help, -h`: Show help
|
|
68
75
|
|
|
69
76
|
Example with environment variables:
|
|
77
|
+
|
|
70
78
|
```bash
|
|
71
79
|
# Using environment variables
|
|
72
80
|
ONE_MCP_PORT=3051 ONE_MCP_TAGS=network,filesystem npx -y @1mcp/agent
|
|
@@ -96,6 +104,7 @@ docker run -i ghcr.io/1mcp-app/agent --transport stdio
|
|
|
96
104
|
```
|
|
97
105
|
|
|
98
106
|
Available image tags:
|
|
107
|
+
|
|
99
108
|
- `latest`: Latest stable release
|
|
100
109
|
- `vX.Y.Z`: Specific version (e.g. `v1.0.0`)
|
|
101
110
|
- `sha-<commit>`: Specific commit
|
|
@@ -103,13 +112,19 @@ Available image tags:
|
|
|
103
112
|
### Environment Variables
|
|
104
113
|
|
|
105
114
|
You can configure 1MCP using environment variables prefixed with `ONE_MCP_`:
|
|
115
|
+
|
|
106
116
|
- `ONE_MCP_TRANSPORT`: Transport type ("stdio" or "http", default: "http")
|
|
107
117
|
- `ONE_MCP_PORT`: HTTP port (default: 3050)
|
|
108
118
|
- `ONE_MCP_HOST`: HTTP host (default: "localhost")
|
|
109
119
|
- `ONE_MCP_CONFIG`: Path to config file
|
|
110
120
|
- `ONE_MCP_TAGS`: Comma-separated list of tags to filter servers
|
|
121
|
+
- `ONE_MCP_PAGINATION`: Enable pagination for client/server lists (boolean, default: false)
|
|
122
|
+
- `ONE_MCP_AUTH`: Enable authentication (OAuth 2.1) (boolean, default: false)
|
|
123
|
+
- `ONE_MCP_SESSION_TTL`: Session expiry time in minutes (number, default: 1440)
|
|
124
|
+
- `ONE_MCP_SESSION_STORAGE_PATH`: Custom session storage directory path (string, default: undefined)
|
|
111
125
|
|
|
112
126
|
Example with environment variables:
|
|
127
|
+
|
|
113
128
|
```bash
|
|
114
129
|
docker run -p 3051:3051 \
|
|
115
130
|
-e ONE_MCP_PORT=3051 \
|
|
@@ -124,6 +139,7 @@ Tags help you control which MCP servers are available to different clients. Thin
|
|
|
124
139
|
#### How to Use Tags
|
|
125
140
|
|
|
126
141
|
1. **In your server config**: Add tags to each server to describe its capabilities
|
|
142
|
+
|
|
127
143
|
```json
|
|
128
144
|
{
|
|
129
145
|
"mcpServers": {
|
|
@@ -144,6 +160,7 @@ Tags help you control which MCP servers are available to different clients. Thin
|
|
|
144
160
|
```
|
|
145
161
|
|
|
146
162
|
2. **When starting 1MCP in stdio mode**: You can filter servers by tags
|
|
163
|
+
|
|
147
164
|
```bash
|
|
148
165
|
# Only start servers with the "network" tag
|
|
149
166
|
npx -y @1mcp/agent --transport stdio --tags "network"
|
|
@@ -153,18 +170,20 @@ npx -y @1mcp/agent --transport stdio --tags "network,filesystem"
|
|
|
153
170
|
```
|
|
154
171
|
|
|
155
172
|
3. **When using SSE transport**: Clients can request servers with specific tags
|
|
173
|
+
|
|
156
174
|
```json
|
|
157
175
|
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
176
|
+
"mcpServers": {
|
|
177
|
+
"1mcp": {
|
|
178
|
+
"type": "http",
|
|
179
|
+
"url": "http://localhost:3050/sse?tags=network" // Only connect to network-capable servers
|
|
163
180
|
}
|
|
181
|
+
}
|
|
164
182
|
}
|
|
165
183
|
```
|
|
166
184
|
|
|
167
185
|
Example tags:
|
|
186
|
+
|
|
168
187
|
- `network`: For servers that make web requests
|
|
169
188
|
- `filesystem`: For servers that handle file operations
|
|
170
189
|
- `memory`: For servers that provide memory/storage
|
|
@@ -187,17 +206,12 @@ The server automatically manages configuration in a global location:
|
|
|
187
206
|
"mcpServers": {
|
|
188
207
|
"mcp-server-fetch": {
|
|
189
208
|
"command": "uvx",
|
|
190
|
-
"args": [
|
|
191
|
-
"mcp-server-fetch"
|
|
192
|
-
],
|
|
209
|
+
"args": ["mcp-server-fetch"],
|
|
193
210
|
"disabled": false
|
|
194
211
|
},
|
|
195
212
|
"server-memory": {
|
|
196
213
|
"command": "npx",
|
|
197
|
-
"args": [
|
|
198
|
-
"-y",
|
|
199
|
-
"@modelcontextprotocol/server-memory"
|
|
200
|
-
],
|
|
214
|
+
"args": ["-y", "@modelcontextprotocol/server-memory"],
|
|
201
215
|
"disabled": false
|
|
202
216
|
}
|
|
203
217
|
}
|
|
@@ -263,21 +277,25 @@ sequenceDiagram
|
|
|
263
277
|
## Development
|
|
264
278
|
|
|
265
279
|
Install dependencies:
|
|
280
|
+
|
|
266
281
|
```bash
|
|
267
282
|
pnpm install
|
|
268
283
|
```
|
|
269
284
|
|
|
270
285
|
Build the server:
|
|
286
|
+
|
|
271
287
|
```bash
|
|
272
288
|
pnpm build
|
|
273
289
|
```
|
|
274
290
|
|
|
275
291
|
For development with auto-rebuild:
|
|
292
|
+
|
|
276
293
|
```bash
|
|
277
294
|
pnpm watch
|
|
278
295
|
```
|
|
279
296
|
|
|
280
297
|
Run the server:
|
|
298
|
+
|
|
281
299
|
```bash
|
|
282
300
|
pnpm dev
|
|
283
301
|
```
|
|
@@ -291,3 +309,9 @@ pnpm inspector
|
|
|
291
309
|
```
|
|
292
310
|
|
|
293
311
|
The Inspector will provide a URL to access debugging tools in your browser.
|
|
312
|
+
|
|
313
|
+
## Debugging & Source Maps
|
|
314
|
+
|
|
315
|
+
This project uses [source-map-support](https://www.npmjs.com/package/source-map-support) to enhance stack traces. When you run the server, stack traces will reference the original TypeScript source files instead of the compiled JavaScript. This makes debugging much easier, as error locations and line numbers will match your source code.
|
|
316
|
+
|
|
317
|
+
No extra setup is required—this is enabled by default. If you see a stack trace, it will point to `.ts` files and the correct line numbers. 🗺️
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { SessionData } from './sessionManager.js';
|
|
2
|
+
/**
|
|
3
|
+
* AuthManager handles OAuth 2.1 authentication flow and token validation.
|
|
4
|
+
*
|
|
5
|
+
* This class orchestrates the OAuth 2.1 authorization code grant flow,
|
|
6
|
+
* manages access token validation, and provides session information.
|
|
7
|
+
* It integrates with SessionManager for persistent storage and
|
|
8
|
+
* ServerConfigManager for configuration settings.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - OAuth 2.1 authorization code grant flow
|
|
12
|
+
* - Access token validation with prefix support
|
|
13
|
+
* - Session information retrieval
|
|
14
|
+
* - Configurable authentication settings
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const authManager = new AuthManager('/custom/sessions');
|
|
19
|
+
* const token = authManager.exchangeCodeForToken('code-123', 'client-456');
|
|
20
|
+
* const session = authManager.validateAccessToken('tk-sess-789');
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class AuthManager {
|
|
24
|
+
private sessionManager;
|
|
25
|
+
private configManager;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new AuthManager instance.
|
|
28
|
+
*
|
|
29
|
+
* Initializes the session manager with optional custom storage path
|
|
30
|
+
* and retrieves the server configuration manager instance.
|
|
31
|
+
*
|
|
32
|
+
* @param sessionStoragePath - Optional custom path for session storage
|
|
33
|
+
*/
|
|
34
|
+
constructor(sessionStoragePath?: string);
|
|
35
|
+
/**
|
|
36
|
+
* Checks if authentication is currently enabled.
|
|
37
|
+
*
|
|
38
|
+
* @returns True if OAuth 2.1 authentication is enabled, false otherwise
|
|
39
|
+
*/
|
|
40
|
+
isAuthEnabled(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Validates an access token and returns session data.
|
|
43
|
+
*
|
|
44
|
+
* If authentication is disabled, returns null (always valid).
|
|
45
|
+
* For enabled authentication, strips the token prefix if present
|
|
46
|
+
* and validates the underlying session.
|
|
47
|
+
*
|
|
48
|
+
* @param token - The access token to validate (with or without prefix)
|
|
49
|
+
* @returns Session data if token is valid, null otherwise
|
|
50
|
+
*/
|
|
51
|
+
validateAccessToken(token: string): SessionData | null;
|
|
52
|
+
/**
|
|
53
|
+
* Creates an authorization code for OAuth 2.1 flow.
|
|
54
|
+
*
|
|
55
|
+
* Delegates to the session manager to create a prefixed authorization
|
|
56
|
+
* code with the configured time-to-live.
|
|
57
|
+
*
|
|
58
|
+
* @param clientId - The client identifier
|
|
59
|
+
* @param redirectUri - The redirect URI for this code
|
|
60
|
+
* @param resource - The resource this code grants access to
|
|
61
|
+
* @returns The generated authorization code with prefix
|
|
62
|
+
*/
|
|
63
|
+
createAuthCode(clientId: string, redirectUri: string, resource: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Exchanges an authorization code for an access token.
|
|
66
|
+
*
|
|
67
|
+
* Validates the authorization code, checks client ID and redirect URI
|
|
68
|
+
* matches, then creates a new access token with the configured TTL.
|
|
69
|
+
* The authorization code is deleted after successful exchange.
|
|
70
|
+
*
|
|
71
|
+
* @param code - The authorization code to exchange
|
|
72
|
+
* @param clientId - The client identifier
|
|
73
|
+
* @param redirectUri - Optional redirect URI for validation
|
|
74
|
+
* @param resource - Optional resource for validation
|
|
75
|
+
* @returns Access token with prefix if exchange successful, null otherwise
|
|
76
|
+
*/
|
|
77
|
+
exchangeCodeForToken(code: string, clientId: string, redirectUri?: string, resource?: string): string | null;
|
|
78
|
+
/**
|
|
79
|
+
* Gets information about an access token.
|
|
80
|
+
*
|
|
81
|
+
* Retrieves session data for the token and returns formatted
|
|
82
|
+
* information including client ID, resource, and time until expiration.
|
|
83
|
+
*
|
|
84
|
+
* @param token - The access token to get information for
|
|
85
|
+
* @returns Token information if valid, null otherwise
|
|
86
|
+
*/
|
|
87
|
+
getTokenInfo(token: string): {
|
|
88
|
+
clientId: string;
|
|
89
|
+
resource: string;
|
|
90
|
+
expiresIn: number;
|
|
91
|
+
} | null;
|
|
92
|
+
/**
|
|
93
|
+
* Revokes an access token.
|
|
94
|
+
*
|
|
95
|
+
* Removes the session associated with the token from storage.
|
|
96
|
+
*
|
|
97
|
+
* @param token - The access token to revoke
|
|
98
|
+
* @returns True if token was revoked, false if it didn't exist
|
|
99
|
+
*/
|
|
100
|
+
revokeToken(token: string): boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Performs graceful shutdown of the AuthManager.
|
|
103
|
+
*
|
|
104
|
+
* Delegates shutdown to the session manager to ensure proper
|
|
105
|
+
* cleanup of resources and timers.
|
|
106
|
+
*/
|
|
107
|
+
shutdown(): void;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=authManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authManager.d.ts","sourceRoot":"","sources":["../../src/auth/authManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAKlE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,aAAa,CAAsB;IAE3C;;;;;;;OAOG;gBACS,kBAAkB,CAAC,EAAE,MAAM;IAKvC;;;;OAIG;IACI,aAAa,IAAI,OAAO;IAI/B;;;;;;;;;OASG;IACI,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAmB7D;;;;;;;;;;OAUG;IACI,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAKtF;;;;;;;;;;;;OAYG;IACI,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAoCnH;;;;;;;;OAQG;IACI,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAapG;;;;;;;OAOG;IACI,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI1C;;;;;OAKG;IACI,QAAQ,IAAI,IAAI;CAGxB"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import logger from '../logger/logger.js';
|
|
2
|
+
import { SessionManager } from './sessionManager.js';
|
|
3
|
+
import { ServerConfigManager } from '../core/server/serverConfig.js';
|
|
4
|
+
import { AUTH_CONFIG } from '../constants.js';
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
/**
|
|
7
|
+
* AuthManager handles OAuth 2.1 authentication flow and token validation.
|
|
8
|
+
*
|
|
9
|
+
* This class orchestrates the OAuth 2.1 authorization code grant flow,
|
|
10
|
+
* manages access token validation, and provides session information.
|
|
11
|
+
* It integrates with SessionManager for persistent storage and
|
|
12
|
+
* ServerConfigManager for configuration settings.
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - OAuth 2.1 authorization code grant flow
|
|
16
|
+
* - Access token validation with prefix support
|
|
17
|
+
* - Session information retrieval
|
|
18
|
+
* - Configurable authentication settings
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const authManager = new AuthManager('/custom/sessions');
|
|
23
|
+
* const token = authManager.exchangeCodeForToken('code-123', 'client-456');
|
|
24
|
+
* const session = authManager.validateAccessToken('tk-sess-789');
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export class AuthManager {
|
|
28
|
+
sessionManager;
|
|
29
|
+
configManager;
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new AuthManager instance.
|
|
32
|
+
*
|
|
33
|
+
* Initializes the session manager with optional custom storage path
|
|
34
|
+
* and retrieves the server configuration manager instance.
|
|
35
|
+
*
|
|
36
|
+
* @param sessionStoragePath - Optional custom path for session storage
|
|
37
|
+
*/
|
|
38
|
+
constructor(sessionStoragePath) {
|
|
39
|
+
this.sessionManager = new SessionManager(sessionStoragePath);
|
|
40
|
+
this.configManager = ServerConfigManager.getInstance();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Checks if authentication is currently enabled.
|
|
44
|
+
*
|
|
45
|
+
* @returns True if OAuth 2.1 authentication is enabled, false otherwise
|
|
46
|
+
*/
|
|
47
|
+
isAuthEnabled() {
|
|
48
|
+
return this.configManager.isAuthEnabled();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validates an access token and returns session data.
|
|
52
|
+
*
|
|
53
|
+
* If authentication is disabled, returns null (always valid).
|
|
54
|
+
* For enabled authentication, strips the token prefix if present
|
|
55
|
+
* and validates the underlying session.
|
|
56
|
+
*
|
|
57
|
+
* @param token - The access token to validate (with or without prefix)
|
|
58
|
+
* @returns Session data if token is valid, null otherwise
|
|
59
|
+
*/
|
|
60
|
+
validateAccessToken(token) {
|
|
61
|
+
if (!this.isAuthEnabled()) {
|
|
62
|
+
return null; // Auth disabled, always valid
|
|
63
|
+
}
|
|
64
|
+
if (!token) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
// Strip token prefix if present
|
|
68
|
+
const tokenId = token.startsWith(AUTH_CONFIG.PREFIXES.ACCESS_TOKEN)
|
|
69
|
+
? token.slice(AUTH_CONFIG.PREFIXES.ACCESS_TOKEN.length)
|
|
70
|
+
: token;
|
|
71
|
+
// Session is stored with 'sess-' + tokenId
|
|
72
|
+
const sessionId = AUTH_CONFIG.PREFIXES.SESSION_ID + tokenId;
|
|
73
|
+
return this.sessionManager.getSession(sessionId);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Creates an authorization code for OAuth 2.1 flow.
|
|
77
|
+
*
|
|
78
|
+
* Delegates to the session manager to create a prefixed authorization
|
|
79
|
+
* code with the configured time-to-live.
|
|
80
|
+
*
|
|
81
|
+
* @param clientId - The client identifier
|
|
82
|
+
* @param redirectUri - The redirect URI for this code
|
|
83
|
+
* @param resource - The resource this code grants access to
|
|
84
|
+
* @returns The generated authorization code with prefix
|
|
85
|
+
*/
|
|
86
|
+
createAuthCode(clientId, redirectUri, resource) {
|
|
87
|
+
const ttlMs = this.configManager.getOAuthCodeTtlMs();
|
|
88
|
+
return this.sessionManager.createAuthCode(clientId, redirectUri, resource, ttlMs);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Exchanges an authorization code for an access token.
|
|
92
|
+
*
|
|
93
|
+
* Validates the authorization code, checks client ID and redirect URI
|
|
94
|
+
* matches, then creates a new access token with the configured TTL.
|
|
95
|
+
* The authorization code is deleted after successful exchange.
|
|
96
|
+
*
|
|
97
|
+
* @param code - The authorization code to exchange
|
|
98
|
+
* @param clientId - The client identifier
|
|
99
|
+
* @param redirectUri - Optional redirect URI for validation
|
|
100
|
+
* @param resource - Optional resource for validation
|
|
101
|
+
* @returns Access token with prefix if exchange successful, null otherwise
|
|
102
|
+
*/
|
|
103
|
+
exchangeCodeForToken(code, clientId, redirectUri, resource) {
|
|
104
|
+
const codeData = this.sessionManager.getAuthCode(code);
|
|
105
|
+
if (!codeData) {
|
|
106
|
+
logger.warn(`Invalid or expired auth code: ${code}`);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
if (codeData.clientId !== clientId) {
|
|
110
|
+
logger.warn(`Client ID mismatch for code ${code}: expected ${codeData.clientId}, got ${clientId}`);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
if (redirectUri && codeData.redirectUri !== redirectUri) {
|
|
114
|
+
logger.warn(`Redirect URI mismatch for code ${code}: expected ${codeData.redirectUri}, got ${redirectUri}`);
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if (resource && codeData.resource && codeData.resource !== resource) {
|
|
118
|
+
logger.warn(`Resource mismatch for code ${code}: expected ${codeData.resource}, got ${resource}`);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
// Delete the auth code after successful exchange
|
|
122
|
+
this.sessionManager.deleteAuthCode(code);
|
|
123
|
+
// Create access token with prefix and a new UUID
|
|
124
|
+
const rawTokenId = uuidv4();
|
|
125
|
+
const accessToken = AUTH_CONFIG.PREFIXES.ACCESS_TOKEN + rawTokenId;
|
|
126
|
+
const ttlMs = this.configManager.getOAuthTokenTtlMs();
|
|
127
|
+
// Store the session using the rawTokenId (not prefixed)
|
|
128
|
+
this.sessionManager.createSessionWithId(rawTokenId, clientId, codeData.resource, ttlMs);
|
|
129
|
+
logger.info(`Exchanged code ${code} for token ${accessToken} for client ${clientId}`);
|
|
130
|
+
return accessToken;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Gets information about an access token.
|
|
134
|
+
*
|
|
135
|
+
* Retrieves session data for the token and returns formatted
|
|
136
|
+
* information including client ID, resource, and time until expiration.
|
|
137
|
+
*
|
|
138
|
+
* @param token - The access token to get information for
|
|
139
|
+
* @returns Token information if valid, null otherwise
|
|
140
|
+
*/
|
|
141
|
+
getTokenInfo(token) {
|
|
142
|
+
const session = this.sessionManager.getSession(token);
|
|
143
|
+
if (!session) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
clientId: session.clientId,
|
|
148
|
+
resource: session.resource,
|
|
149
|
+
expiresIn: Math.max(0, Math.floor((session.expires - Date.now()) / 1000)),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Revokes an access token.
|
|
154
|
+
*
|
|
155
|
+
* Removes the session associated with the token from storage.
|
|
156
|
+
*
|
|
157
|
+
* @param token - The access token to revoke
|
|
158
|
+
* @returns True if token was revoked, false if it didn't exist
|
|
159
|
+
*/
|
|
160
|
+
revokeToken(token) {
|
|
161
|
+
return this.sessionManager.deleteSession(token);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Performs graceful shutdown of the AuthManager.
|
|
165
|
+
*
|
|
166
|
+
* Delegates shutdown to the session manager to ensure proper
|
|
167
|
+
* cleanup of resources and timers.
|
|
168
|
+
*/
|
|
169
|
+
shutdown() {
|
|
170
|
+
this.sessionManager.shutdown();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=authManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authManager.js","sourceRoot":"","sources":["../../src/auth/authManager.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,qBAAqB,CAAC;AACzC,OAAO,EAAE,cAAc,EAAe,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,WAAW;IACd,cAAc,CAAiB;IAC/B,aAAa,CAAsB;IAE3C;;;;;;;OAOG;IACH,YAAY,kBAA2B;QACrC,IAAI,CAAC,cAAc,GAAG,IAAI,cAAc,CAAC,kBAAkB,CAAC,CAAC;QAC7D,IAAI,CAAC,aAAa,GAAG,mBAAmB,CAAC,WAAW,EAAE,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACI,aAAa;QAClB,OAAO,IAAI,CAAC,aAAa,CAAC,aAAa,EAAE,CAAC;IAC5C,CAAC;IAED;;;;;;;;;OASG;IACI,mBAAmB,CAAC,KAAa;QACtC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,CAAC,8BAA8B;QAC7C,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,IAAI,CAAC;QACd,CAAC;QAED,gCAAgC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC;YACjE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC;YACvD,CAAC,CAAC,KAAK,CAAC;QAEV,2CAA2C;QAC3C,MAAM,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAC,UAAU,GAAG,OAAO,CAAC;QAC5D,OAAO,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;;;;;OAUG;IACI,cAAc,CAAC,QAAgB,EAAE,WAAmB,EAAE,QAAgB;QAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IACpF,CAAC;IAED;;;;;;;;;;;;OAYG;IACI,oBAAoB,CAAC,IAAY,EAAE,QAAgB,EAAE,WAAoB,EAAE,QAAiB;QACjG,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,+BAA+B,IAAI,cAAc,QAAQ,CAAC,QAAQ,SAAS,QAAQ,EAAE,CAAC,CAAC;YACnG,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,WAAW,IAAI,QAAQ,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;YACxD,MAAM,CAAC,IAAI,CAAC,kCAAkC,IAAI,cAAc,QAAQ,CAAC,WAAW,SAAS,WAAW,EAAE,CAAC,CAAC;YAC5G,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,QAAQ,IAAI,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACpE,MAAM,CAAC,IAAI,CAAC,8BAA8B,IAAI,cAAc,QAAQ,CAAC,QAAQ,SAAS,QAAQ,EAAE,CAAC,CAAC;YAClG,OAAO,IAAI,CAAC;QACd,CAAC;QAED,iDAAiD;QACjD,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAEzC,iDAAiD;QACjD,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,YAAY,GAAG,UAAU,CAAC;QACnE,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,kBAAkB,EAAE,CAAC;QACtD,wDAAwD;QACxD,IAAI,CAAC,cAAc,CAAC,mBAAmB,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QAExF,MAAM,CAAC,IAAI,CAAC,kBAAkB,IAAI,cAAc,WAAW,eAAe,QAAQ,EAAE,CAAC,CAAC;QACtF,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;;;;;OAQG;IACI,YAAY,CAAC,KAAa;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO;YACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;SAC1E,CAAC;IACJ,CAAC;IAED;;;;;;;OAOG;IACI,WAAW,CAAC,KAAa;QAC9B,OAAO,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED;;;;;OAKG;IACI,QAAQ;QACb,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { AuthManager } from './authManager.js';
|
|
3
|
+
export declare function createAuthMiddleware(authManager: AuthManager): (req: express.Request, res: express.Response, next: express.NextFunction) => void;
|
|
4
|
+
declare global {
|
|
5
|
+
namespace Express {
|
|
6
|
+
interface Request {
|
|
7
|
+
sessionData?: {
|
|
8
|
+
clientId: string;
|
|
9
|
+
resource: string;
|
|
10
|
+
expires: number;
|
|
11
|
+
createdAt: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=authMiddleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authMiddleware.d.ts","sourceRoot":"","sources":["../../src/auth/authMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,IACnD,KAAK,OAAO,CAAC,OAAO,EAAE,KAAK,OAAO,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,YAAY,UA8ChF;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,WAAW,CAAC,EAAE;gBACZ,QAAQ,EAAE,MAAM,CAAC;gBACjB,QAAQ,EAAE,MAAM,CAAC;gBACjB,OAAO,EAAE,MAAM,CAAC;gBAChB,SAAS,EAAE,MAAM,CAAC;aACnB,CAAC;SACH;KACF;CACF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import logger from '../logger/logger.js';
|
|
2
|
+
export function createAuthMiddleware(authManager) {
|
|
3
|
+
return (req, res, next) => {
|
|
4
|
+
// Skip auth if disabled
|
|
5
|
+
if (!authManager.isAuthEnabled()) {
|
|
6
|
+
return next();
|
|
7
|
+
}
|
|
8
|
+
const auth = req.headers['authorization'];
|
|
9
|
+
if (!auth || !auth.startsWith('Bearer ')) {
|
|
10
|
+
logger.warn('Missing Bearer token in request', {
|
|
11
|
+
url: req.url,
|
|
12
|
+
method: req.method,
|
|
13
|
+
headers: req.headers,
|
|
14
|
+
});
|
|
15
|
+
res.status(401).json({
|
|
16
|
+
error: 'invalid_token',
|
|
17
|
+
error_description: 'Missing Bearer token',
|
|
18
|
+
});
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const token = auth.slice('Bearer '.length);
|
|
22
|
+
const sessionData = authManager.validateAccessToken(token);
|
|
23
|
+
if (!sessionData) {
|
|
24
|
+
logger.warn('Invalid or expired token', {
|
|
25
|
+
url: req.url,
|
|
26
|
+
method: req.method,
|
|
27
|
+
token: token.substring(0, 8) + '...',
|
|
28
|
+
});
|
|
29
|
+
res.status(401).json({
|
|
30
|
+
error: 'invalid_token',
|
|
31
|
+
error_description: 'Invalid or expired token',
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Add session data to request for downstream use
|
|
36
|
+
req.sessionData = sessionData;
|
|
37
|
+
logger.debug('Valid token for request', {
|
|
38
|
+
url: req.url,
|
|
39
|
+
method: req.method,
|
|
40
|
+
clientId: sessionData.clientId,
|
|
41
|
+
});
|
|
42
|
+
next();
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=authMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../src/auth/authMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,qBAAqB,CAAC;AAGzC,MAAM,UAAU,oBAAoB,CAAC,WAAwB;IAC3D,OAAO,CAAC,GAAoB,EAAE,GAAqB,EAAE,IAA0B,EAAE,EAAE;QACjF,wBAAwB;QACxB,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,EAAE,CAAC;YACjC,OAAO,IAAI,EAAE,CAAC;QAChB,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE;gBAC7C,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,OAAO,EAAE,GAAG,CAAC,OAAO;aACrB,CAAC,CAAC;YACH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,eAAe;gBACtB,iBAAiB,EAAE,sBAAsB;aAC1C,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,WAAW,GAAG,WAAW,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAE3D,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE;gBACtC,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK;aACrC,CAAC,CAAC;YACH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,eAAe;gBACtB,iBAAiB,EAAE,0BAA0B;aAC9C,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,iDAAiD;QACjD,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE;YACtC,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,QAAQ,EAAE,WAAW,CAAC,QAAQ;SAC/B,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
|