mcp-node-red 1.0.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.
Files changed (41) hide show
  1. package/.github/workflows/ci.yml +37 -0
  2. package/.github/workflows/release.yml +30 -0
  3. package/.mcp.json.example +11 -0
  4. package/.releaserc.json +17 -0
  5. package/CHANGELOG.md +8 -0
  6. package/CLAUDE.md +147 -0
  7. package/LICENSE +21 -0
  8. package/README.md +330 -0
  9. package/dist/client.d.ts +20 -0
  10. package/dist/client.d.ts.map +1 -0
  11. package/dist/client.js +110 -0
  12. package/dist/client.js.map +1 -0
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +7 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/schemas.d.ts +252 -0
  18. package/dist/schemas.d.ts.map +1 -0
  19. package/dist/schemas.js +44 -0
  20. package/dist/schemas.js.map +1 -0
  21. package/dist/server.d.ts +26 -0
  22. package/dist/server.d.ts.map +1 -0
  23. package/dist/server.js +121 -0
  24. package/dist/server.js.map +1 -0
  25. package/dist/tools/create-flow.d.ts +8 -0
  26. package/dist/tools/create-flow.d.ts.map +1 -0
  27. package/dist/tools/create-flow.js +26 -0
  28. package/dist/tools/create-flow.js.map +1 -0
  29. package/dist/tools/get-flows.d.ts +8 -0
  30. package/dist/tools/get-flows.d.ts.map +1 -0
  31. package/dist/tools/get-flows.js +12 -0
  32. package/dist/tools/get-flows.js.map +1 -0
  33. package/dist/tools/update-flow.d.ts +8 -0
  34. package/dist/tools/update-flow.d.ts.map +1 -0
  35. package/dist/tools/update-flow.js +32 -0
  36. package/dist/tools/update-flow.js.map +1 -0
  37. package/dist/tools/validate-flow.d.ts +8 -0
  38. package/dist/tools/validate-flow.d.ts.map +1 -0
  39. package/dist/tools/validate-flow.js +36 -0
  40. package/dist/tools/validate-flow.js.map +1 -0
  41. package/package.json +46 -0
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ node-version: [20, 22]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - name: Use Node.js ${{ matrix.node-version }}
18
+ uses: actions/setup-node@v4
19
+ with:
20
+ node-version: ${{ matrix.node-version }}
21
+ cache: 'npm'
22
+ - run: npm ci
23
+ - run: npm run build
24
+ - run: npm test
25
+ - run: npm run lint
26
+
27
+ coverage:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: actions/setup-node@v4
32
+ with:
33
+ node-version: 22
34
+ cache: 'npm'
35
+ - run: npm ci
36
+ - run: npm run build
37
+ - run: npm run test:coverage
@@ -0,0 +1,30 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ permissions:
8
+ contents: write
9
+ issues: write
10
+ pull-requests: write
11
+ id-token: write
12
+
13
+ jobs:
14
+ release:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ fetch-depth: 0
20
+ - uses: actions/setup-node@v4
21
+ with:
22
+ node-version: 22
23
+ - run: npm ci
24
+ - run: npm run build
25
+ - run: npm test
26
+ - run: npm run lint
27
+ - run: npx semantic-release
28
+ env:
29
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,11 @@
1
+ {
2
+ "mcpServers": {
3
+ "node-red": {
4
+ "command": "node",
5
+ "args": ["/absolute/path/to/mcp-node-red/dist/index.js"],
6
+ "env": {
7
+ "NODE_RED_URL": "http://localhost:1880"
8
+ }
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "branches": ["main"],
3
+ "plugins": [
4
+ "@semantic-release/commit-analyzer",
5
+ "@semantic-release/release-notes-generator",
6
+ "@semantic-release/changelog",
7
+ "@semantic-release/npm",
8
+ "@semantic-release/github",
9
+ [
10
+ "@semantic-release/git",
11
+ {
12
+ "assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
13
+ "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
14
+ }
15
+ ]
16
+ ]
17
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # 1.0.0 (2025-10-08)
2
+
3
+
4
+ ### Features
5
+
6
+ * add MCP server for Node-RED workflow management ([4330d3a](https://github.com/fx/mcp-node-red/commit/4330d3a84f4341b000e12859d02da0604d0cf4bf))
7
+ * add semantic-release for automated releases ([654965d](https://github.com/fx/mcp-node-red/commit/654965d940b859d784d753172f7f6f57a08bc18c))
8
+ * initial commit from template ([5ef23d6](https://github.com/fx/mcp-node-red/commit/5ef23d6ea3b7584c9116a1de1263e4f937e3b2c4))
package/CLAUDE.md ADDED
@@ -0,0 +1,147 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ MCP (Model Context Protocol) server that provides Node-RED workflow management capabilities via stdio transport. Exposes 4 tools for AI agents to interact with Node-RED Admin API v2: get flows, create flow, update flow, and validate flow.
8
+
9
+ ## Commands
10
+
11
+ ```bash
12
+ # Build (required after any code changes)
13
+ npm run build
14
+
15
+ # Development with auto-rebuild
16
+ npm run dev
17
+
18
+ # Run all tests
19
+ npm test
20
+
21
+ # Run tests with coverage
22
+ npm run test:coverage
23
+
24
+ # Run single test file
25
+ npx vitest tests/client.test.ts
26
+
27
+ # Lint
28
+ npm run lint
29
+
30
+ # Fix lint issues
31
+ npm run lint:fix
32
+
33
+ # Format code
34
+ npm run format
35
+ ```
36
+
37
+ ## Architecture
38
+
39
+ ### Core Components
40
+
41
+ **Server Layer** (`src/server.ts`):
42
+ - Creates MCP server using `@modelcontextprotocol/sdk`
43
+ - Reads config from env vars: `NODE_RED_URL` (required), `NODE_RED_TOKEN` (optional)
44
+ - Registers 4 MCP tools with schemas (get_flows, create_flow, update_flow, validate_flow)
45
+ - Routes tool calls to individual tool handlers
46
+ - Catches errors and returns MCP-formatted error responses
47
+
48
+ **HTTP Client** (`src/client.ts`):
49
+ - `NodeRedClient` class wraps Node-RED Admin API v2
50
+ - Handles two auth modes:
51
+ - Bearer token via `NODE_RED_TOKEN` env var
52
+ - Basic auth extracted from URL credentials (e.g., `http://user:pass@host:1880`)
53
+ - Always sets `Node-RED-API-Version: v2` header
54
+ - Uses `undici` for HTTP requests
55
+
56
+ **Tools** (`src/tools/*.ts`):
57
+ - Each tool is a standalone async function
58
+ - Takes `NodeRedClient` instance and tool arguments
59
+ - Returns MCP tool response format: `{ content: [{ type: 'text', text: '...' }] }`
60
+ - `create-flow.ts` uses POST /flow to create new flows
61
+ - `update-flow.ts` uses PUT /flow/:id to update individual flows safely
62
+
63
+ **Schemas** (`src/schemas.ts`):
64
+ - Zod schemas for validation
65
+ - `NodeRedItemSchema` is a union of flows (type: "tab") and nodes (other types)
66
+ - All items have `id` and `type`, flows also require `label`
67
+ - Node references to parent flows via `z` property
68
+ - `UpdateFlowRequestSchema` defines structure for PUT /flow/:id requests
69
+
70
+ ### Key Design Decisions
71
+
72
+ **Stdio Transport**: Uses stdin/stdout for MCP communication, not HTTP. Server runs as child process.
73
+
74
+ **API v2 with Optimistic Locking**: Uses `rev` field in responses to prevent conflicts.
75
+
76
+ **Authentication Flexibility**: Supports both Bearer tokens (standalone Node-RED) and Basic auth (Home Assistant add-on).
77
+
78
+ **JSON String Parameters**: MCP tool parameters receive flows as JSON strings, not objects. Tools parse and validate them.
79
+
80
+ **Individual Flow Updates**: Uses PUT /flow/:id to update one flow at a time, preventing accidental destruction of other flows.
81
+
82
+ ## Node-RED Admin API v2
83
+
84
+ **GET /flows**:
85
+ - Returns all flows: `{rev: "...", flows: [...]}`
86
+
87
+ **POST /flow**:
88
+ - Creates a new flow
89
+ - Request: `{id, label, nodes: [], configs: []}`
90
+ - Response: 200 or 204 with `{id: "..."}` in body
91
+ - Flow ID is optional - auto-generated if not provided
92
+
93
+ **PUT /flow/:id**:
94
+ - Updates a single flow by ID
95
+ - Request: `{id, label, nodes: [], configs: []}`
96
+ - Response: 204 with `{id: "..."}` in body
97
+ - Only affects the specified flow, all other flows remain untouched
98
+
99
+ ## Testing
100
+
101
+ Tests use vitest with mocked `undici` requests. Each component has dedicated test file:
102
+ - `tests/client.test.ts` - HTTP client + auth modes
103
+ - `tests/tools.test.ts` - Tool handlers
104
+ - `tests/server.test.ts` - MCP server setup
105
+
106
+ ## CRITICAL: Flow Update Safety
107
+
108
+ **Always use PUT /flow/:id for individual flow updates**
109
+
110
+ The MCP server uses `PUT /flow/:id` which:
111
+ - Updates ONLY the specified flow
112
+ - Leaves all other flows completely untouched
113
+ - No risk of destroying unrelated workflows
114
+ - Simpler and safer than POST /flows
115
+
116
+ **Flow update format**:
117
+ ```json
118
+ {
119
+ "id": "flow-id",
120
+ "label": "Flow Name",
121
+ "nodes": [...],
122
+ "configs": [...]
123
+ }
124
+ ```
125
+
126
+ ## CRITICAL: ALWAYS Use MCP Tools
127
+
128
+ **NEVER bypass MCP tools to interact with Node-RED directly**
129
+
130
+ - NEVER use `curl` to call Node-RED API directly
131
+ - NEVER use Bash to make HTTP requests to Node-RED
132
+ - ALWAYS use the MCP tools: `get_flows`, `create_flow`, `update_flow`, `validate_flow`
133
+
134
+ The entire purpose of this MCP server is to provide safe, validated access to Node-RED through MCP tools. Bypassing them defeats the purpose and removes safety checks.
135
+
136
+ If MCP tools appear to fail:
137
+ 1. Debug the MCP tool issue
138
+ 2. Fix the tool implementation
139
+ 3. Test the fix
140
+ 4. NEVER work around it with direct API calls
141
+
142
+ ## Configuration
143
+
144
+ Required env var: `NODE_RED_URL`
145
+ Optional env var: `NODE_RED_TOKEN`
146
+
147
+ For Home Assistant add-on deployments, embed credentials in URL: `http://username:password@host:1880`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,330 @@
1
+ # Node-RED MCP Server
2
+
3
+ MCP server for Node-RED workflow management using stdio transport. Provides tools to get, update, and validate individual Node-RED flows through the Admin API v2.
4
+
5
+ ## Features
6
+
7
+ - **get_flows**: Retrieve all flows from Node-RED instance
8
+ - **create_flow**: Create new flow using POST /flow
9
+ - **update_flow**: Update specific flow by ID using PUT /flow/:id
10
+ - **validate_flow**: Validate flow configuration without deploying
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install
16
+ npm run build
17
+ ```
18
+
19
+ ## Configuration
20
+
21
+ Set environment variables:
22
+
23
+ ```bash
24
+ export NODE_RED_URL=http://localhost:1880
25
+ export NODE_RED_TOKEN=your-api-token # Optional
26
+ ```
27
+
28
+ ### Node-RED Setup
29
+
30
+ #### Standalone Node-RED
31
+
32
+ 1. Enable Admin API in Node-RED `settings.js`:
33
+ ```javascript
34
+ adminAuth: {
35
+ type: "credentials",
36
+ users: [{
37
+ username: "admin",
38
+ password: "$2a$08$...", // bcrypt hash
39
+ permissions: "*"
40
+ }]
41
+ }
42
+ ```
43
+
44
+ 2. Generate API token (if auth enabled):
45
+ ```bash
46
+ curl -X POST http://localhost:1880/auth/token \
47
+ -H "Content-Type: application/json" \
48
+ -d '{"client_id":"node-red-admin","grant_type":"password","scope":"*","username":"admin","password":"your-password"}'
49
+ ```
50
+
51
+ #### Home Assistant Add-on (hassio-addons/addon-node-red)
52
+
53
+ When running Node-RED via Home Assistant add-on, authentication uses Home Assistant credentials with Basic Auth:
54
+
55
+ ```bash
56
+ # Test connection with HA credentials
57
+ curl http://USERNAME:PASSWORD@homeassistant.local:1880/flows
58
+ ```
59
+
60
+ **Configuration**:
61
+ ```bash
62
+ # Use basic auth in URL
63
+ export NODE_RED_URL=http://admin:your-ha-password@192.168.0.232:1880
64
+ # No NODE_RED_TOKEN needed for HA add-on
65
+ ```
66
+
67
+ **Note**: Home Assistant add-on does not use `/auth/token` endpoint. API authentication is handled via HTTP Basic Auth using your Home Assistant credentials.
68
+
69
+ ## Clients
70
+
71
+ <details>
72
+ <summary>Claude Code</summary>
73
+
74
+ Create `.mcp.json` in your project (copy from `.mcp.json.example`):
75
+
76
+ ```json
77
+ {
78
+ "mcpServers": {
79
+ "node-red": {
80
+ "command": "node",
81
+ "args": ["/path/to/mcp-node-red/dist/index.js"],
82
+ "env": {
83
+ "NODE_RED_URL": "http://localhost:1880",
84
+ "NODE_RED_TOKEN": "your-api-token"
85
+ }
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ Load the config:
92
+ ```bash
93
+ claude --mcp-config .mcp.json
94
+ ```
95
+
96
+ </details>
97
+
98
+ <details>
99
+ <summary>Claude Desktop</summary>
100
+
101
+ Add to `~/.config/claude-code/claude_desktop_config.json`:
102
+
103
+ ```json
104
+ {
105
+ "mcpServers": {
106
+ "node-red": {
107
+ "command": "node",
108
+ "args": ["/path/to/mcp-node-red/dist/index.js"],
109
+ "env": {
110
+ "NODE_RED_URL": "http://localhost:1880",
111
+ "NODE_RED_TOKEN": "your-api-token"
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
118
+ Or using npx:
119
+
120
+ ```json
121
+ {
122
+ "mcpServers": {
123
+ "node-red": {
124
+ "command": "npx",
125
+ "args": ["-y", "mcp-node-red"],
126
+ "env": {
127
+ "NODE_RED_URL": "http://localhost:1880",
128
+ "NODE_RED_TOKEN": "your-api-token"
129
+ }
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ Restart Claude Desktop to apply changes.
136
+
137
+ </details>
138
+
139
+ ## Usage
140
+
141
+ ### Get Flows
142
+
143
+ ```
144
+ Get all flows from my Node-RED instance
145
+ ```
146
+
147
+ Returns current flows with revision number for optimistic locking.
148
+
149
+ ### Create Flow
150
+
151
+ ```
152
+ Create a new flow with label "My New Flow"
153
+ ```
154
+
155
+ Creates a new flow using POST /flow endpoint. Flow ID can be provided or will be auto-generated.
156
+
157
+ **Flow format**:
158
+ ```json
159
+ {
160
+ "id": "optional-id",
161
+ "label": "My Flow",
162
+ "nodes": [],
163
+ "configs": []
164
+ }
165
+ ```
166
+
167
+ ### Update Flow
168
+
169
+ ```
170
+ Update flow "flow1" with label "New Name"
171
+ ```
172
+
173
+ Updates specific flow using PUT /flow/:id endpoint. Only affects the specified flow, leaving all other flows untouched.
174
+
175
+ **Flow format**:
176
+ ```json
177
+ {
178
+ "id": "flow1",
179
+ "label": "My Flow",
180
+ "nodes": [],
181
+ "configs": []
182
+ }
183
+ ```
184
+
185
+ ### Validate Flow
186
+
187
+ ```
188
+ Validate this flow configuration:
189
+ {
190
+ "id": "test",
191
+ "label": "Test Flow",
192
+ "nodes": []
193
+ }
194
+ ```
195
+
196
+ Checks for:
197
+ - Required fields (id, label)
198
+ - Valid node references
199
+ - Structural integrity
200
+
201
+ ## API Reference
202
+
203
+ ### get_flows
204
+
205
+ Get all flows from Node-RED.
206
+
207
+ **Input**: None
208
+
209
+ **Output**:
210
+ ```json
211
+ {
212
+ "rev": "abc123",
213
+ "flows": [...]
214
+ }
215
+ ```
216
+
217
+ ### create_flow
218
+
219
+ Create a new flow using POST /flow.
220
+
221
+ **Input**:
222
+ - `flow` (string): JSON string containing flow data with format: `{id, label, nodes: [], configs: []}`
223
+
224
+ **Output**:
225
+ ```json
226
+ {
227
+ "id": "flow1"
228
+ }
229
+ ```
230
+
231
+ **Important**: Flow ID is optional - Node-RED will auto-generate if not provided. Returns 200 or 204 with the flow ID.
232
+
233
+ ### update_flow
234
+
235
+ Update specific flow by ID using PUT /flow/:id.
236
+
237
+ **Input**:
238
+ - `flowId` (string): Flow ID to update
239
+ - `updates` (string): JSON string containing flow data with format: `{id, label, nodes: [], configs: []}`
240
+
241
+ **Output**:
242
+ ```json
243
+ {
244
+ "id": "flow1"
245
+ }
246
+ ```
247
+
248
+ **Important**: This endpoint updates ONLY the specified flow. All other flows remain completely untouched. No risk of destroying unrelated workflows.
249
+
250
+ ### validate_flow
251
+
252
+ Validate flow configuration.
253
+
254
+ **Input**:
255
+ - `flow` (string): JSON string containing flow data with format: `{id, label, nodes: [], configs: []}`
256
+
257
+ **Output**:
258
+ ```json
259
+ {
260
+ "valid": true,
261
+ "errors": ["error1", "error2"]
262
+ }
263
+ ```
264
+
265
+ ## Development
266
+
267
+ ```bash
268
+ # Build
269
+ npm run build
270
+
271
+ # Watch mode
272
+ npm run dev
273
+
274
+ # Test
275
+ npm test
276
+
277
+ # Coverage
278
+ npm run test:coverage
279
+
280
+ # Lint
281
+ npm run lint
282
+ npm run lint:fix
283
+
284
+ # Format
285
+ npm run format
286
+ ```
287
+
288
+ ## Node-RED Admin API v2
289
+
290
+ The server uses Admin API v2 endpoints:
291
+
292
+ ### GET /flows
293
+ - Returns all flows: `{rev: "...", flows: [...]}`
294
+ - Headers: `Node-RED-API-Version: v2`, `Authorization`
295
+
296
+ ### POST /flow
297
+ - Creates a new flow
298
+ - Request: `{id, label, nodes: [], configs: []}`
299
+ - Response: 200 or 204 with `{id: "..."}` in body
300
+ - Flow ID is optional - auto-generated if not provided
301
+
302
+ ### PUT /flow/:id
303
+ - Updates a single flow by ID
304
+ - Request: `{id, label, nodes: [], configs: []}`
305
+ - Response: 204 with `{id: "..."}` in body
306
+ - Only affects the specified flow, all other flows remain untouched
307
+
308
+ ## Error Handling
309
+
310
+ All tools return errors in content:
311
+
312
+ ```json
313
+ {
314
+ "content": [{
315
+ "type": "text",
316
+ "text": "Error: ..."
317
+ }],
318
+ "isError": true
319
+ }
320
+ ```
321
+
322
+ Common errors:
323
+ - Invalid JSON in request
324
+ - Flow not found
325
+ - Validation failures
326
+ - Network/API errors
327
+
328
+ ## License
329
+
330
+ MIT
@@ -0,0 +1,20 @@
1
+ import type { Config, NodeRedFlowsResponse, UpdateFlowRequest } from './schemas.js';
2
+ export declare class NodeRedClient {
3
+ private readonly baseUrl;
4
+ private readonly token?;
5
+ private readonly basicAuth?;
6
+ constructor(config: Config);
7
+ private getHeaders;
8
+ getFlows(): Promise<NodeRedFlowsResponse>;
9
+ createFlow(flowData: UpdateFlowRequest): Promise<{
10
+ id: string;
11
+ }>;
12
+ updateFlow(flowId: string, flowData: UpdateFlowRequest): Promise<{
13
+ id: string;
14
+ }>;
15
+ validateFlow(flowData: UpdateFlowRequest): Promise<{
16
+ valid: boolean;
17
+ errors?: string[];
18
+ }>;
19
+ }
20
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGpF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAS;gBAExB,MAAM,EAAE,MAAM;IAc1B,OAAO,CAAC,UAAU;IAeZ,QAAQ,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAezC,UAAU,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAiBhE,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IAiBhF,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CAyChG"}