@getpultdev/mcp-server 0.1.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 ADDED
@@ -0,0 +1,336 @@
1
+ # @getpultdev/mcp-server
2
+
3
+ MCP server for [Pult](https://pult.rest) — manage apps, deployments, databases, domains, and more from AI tools.
4
+
5
+ Works with [Claude Code](https://docs.anthropic.com/en/docs/claude-code), Cursor, Windsurf, and any [MCP](https://modelcontextprotocol.io)-compatible client.
6
+
7
+ ## Setup
8
+
9
+ ### Claude Code
10
+
11
+ ```bash
12
+ claude mcp add pult -- npx @getpultdev/mcp-server
13
+ ```
14
+
15
+ Set your API key:
16
+
17
+ ```bash
18
+ export PULT_API_KEY=your-api-key
19
+ ```
20
+
21
+ ### claude_desktop_config.json / Cursor / Windsurf
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "pult": {
27
+ "command": "npx",
28
+ "args": ["@getpultdev/mcp-server"],
29
+ "env": {
30
+ "PULT_API_KEY": "your-api-key"
31
+ }
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ ## Tools (183)
38
+
39
+ ### Apps & Regions
40
+ | Tool | Description |
41
+ |------|-------------|
42
+ | `list-apps` | List all your Pult apps |
43
+ | `get-app` | Get app details |
44
+ | `create-app` | Create a new app |
45
+ | `delete-app` | Delete an app |
46
+ | `list-regions` | List available regions for an app |
47
+ | `add-region` | Add a deployment region |
48
+ | `remove-region` | Remove a deployment region |
49
+ | `get-app-metrics` | Get app resource metrics |
50
+ | `purge-cache` | Purge CDN cache |
51
+ | `get-project-url` | Get API, database, auth, and storage URLs |
52
+
53
+ ### Deployments
54
+ | Tool | Description |
55
+ |------|-------------|
56
+ | `deploy-app` | Trigger a deployment |
57
+ | `list-deployments` | List recent deployments |
58
+ | `get-deployment` | Get deployment details |
59
+ | `get-build-logs` | Get build logs for a deployment |
60
+ | `rollback-deployment` | Rollback to the previous deployment |
61
+
62
+ ### Environment Variables
63
+ | Tool | Description |
64
+ |------|-------------|
65
+ | `list-env` | List environment variables |
66
+ | `set-env` | Set environment variables (key-value pairs) |
67
+ | `delete-env` | Delete an environment variable |
68
+ | `reveal-env-var` | Reveal the decrypted value of an env var |
69
+
70
+ ### Domains
71
+ | Tool | Description |
72
+ |------|-------------|
73
+ | `list-domains` | List custom domains |
74
+ | `add-domain` | Add a custom domain |
75
+ | `verify-domain` | Verify domain DNS |
76
+ | `delete-domain` | Remove a domain |
77
+
78
+ ### Database
79
+ | Tool | Description |
80
+ |------|-------------|
81
+ | `get-database` | Get database status and connection info |
82
+ | `create-database` | Provision a managed PostgreSQL database |
83
+ | `delete-database` | Delete the managed database |
84
+ | `query-database` | Execute SQL queries |
85
+ | `list-tables` | List all tables |
86
+ | `get-table-schema` | Get column definitions for a table |
87
+ | `create-table` | Create a new table |
88
+ | `drop-table` | Drop a table |
89
+ | `insert-rows` | Insert rows into a table |
90
+ | `update-rows` | Update rows in a table |
91
+ | `delete-rows` | Delete rows from a table |
92
+ | `get-table-row-count` | Get row count for a table |
93
+ | `list-rls-policies` | List Row Level Security policies |
94
+ | `create-rls-policy` | Create an RLS policy |
95
+ | `delete-rls-policy` | Delete an RLS policy |
96
+ | `list-db-functions` | List database functions |
97
+ | `list-db-roles` | List database roles |
98
+ | `list-backups` | List database backups |
99
+ | `create-backup` | Create a database backup |
100
+ | `get-db-stats` | Get database statistics |
101
+ | `create-db-replica` | Create a read replica |
102
+ | `list-db-replicas` | List read replicas |
103
+ | `delete-db-replica` | Delete a read replica |
104
+ | `list-migrations` | List applied migrations |
105
+ | `apply-migration` | Apply a SQL migration |
106
+ | `list-extensions` | List PostgreSQL extensions |
107
+ | `enable-extension` | Enable a PostgreSQL extension |
108
+
109
+ ### Environments (Branching)
110
+ | Tool | Description |
111
+ |------|-------------|
112
+ | `list-environments` | List environments (production, preview, staging) |
113
+ | `create-environment` | Create a new environment |
114
+ | `get-environment` | Get environment details |
115
+ | `delete-environment` | Delete an environment |
116
+ | `promote-environment` | Promote to production |
117
+
118
+ ### Logs
119
+ | Tool | Description |
120
+ |------|-------------|
121
+ | `get-logs` | Get runtime logs |
122
+
123
+ ### Services
124
+ | Tool | Description |
125
+ |------|-------------|
126
+ | `list-services` | List services |
127
+ | `get-service` | Get service details |
128
+ | `create-service` | Create a new service |
129
+ | `update-service` | Update service config |
130
+ | `delete-service` | Delete a service |
131
+ | `deploy-service` | Deploy a specific service |
132
+ | `list-service-env` | List service env vars |
133
+ | `set-service-env` | Set service env vars |
134
+ | `reveal-service-env` | Reveal encrypted service env var |
135
+
136
+ ### Auth
137
+ | Tool | Description |
138
+ |------|-------------|
139
+ | `get-auth-status` | Get auth service status |
140
+ | `enable-auth` | Enable authentication |
141
+ | `disable-auth` | Disable authentication |
142
+ | `list-auth-users` | List registered users |
143
+ | `get-auth-user` | Get user details |
144
+ | `create-auth-user` | Create a user |
145
+ | `ban-auth-user` | Ban a user |
146
+ | `unban-auth-user` | Unban a user |
147
+ | `delete-auth-user` | Delete a user |
148
+ | `list-auth-providers` | List auth providers |
149
+ | `update-auth-provider` | Configure a provider |
150
+ | `get-auth-settings` | Get auth settings |
151
+ | `update-auth-settings` | Update auth settings |
152
+ | `list-auth-logs` | List auth event logs |
153
+ | `get-auth-hooks` | Get auth hooks config |
154
+ | `update-auth-hooks` | Update auth hooks |
155
+ | `get-auth-templates` | Get email templates |
156
+ | `update-auth-template` | Update an email template |
157
+
158
+ ### Storage
159
+ | Tool | Description |
160
+ |------|-------------|
161
+ | `get-storage` | Get storage info |
162
+ | `enable-storage` | Enable storage |
163
+ | `update-storage` | Update storage config |
164
+ | `presign-upload` | Get a presigned upload URL |
165
+ | `delete-storage` | Disable storage |
166
+ | `list-storage-buckets` | List storage buckets |
167
+ | `create-storage-bucket` | Create a bucket |
168
+ | `delete-storage-bucket` | Delete a bucket |
169
+ | `list-storage-files` | List files |
170
+ | `delete-storage-files` | Delete files |
171
+ | `move-storage-file` | Move/rename a file |
172
+ | `list-storage-policies` | List access policies |
173
+ | `create-storage-policy` | Create an access policy |
174
+ | `delete-storage-policy` | Delete an access policy |
175
+ | `get-storage-settings` | Get storage settings |
176
+ | `update-storage-settings` | Update storage settings |
177
+
178
+ ### Redis
179
+ | Tool | Description |
180
+ |------|-------------|
181
+ | `get-redis-status` | Get Redis instance status |
182
+ | `enable-redis` | Enable Redis (Valkey) |
183
+ | `disable-redis` | Disable Redis |
184
+ | `get-redis-info` | Get server info (memory, clients, uptime) |
185
+ | `list-redis-keys` | List keys matching a pattern |
186
+ | `get-redis-key` | Get key value and metadata |
187
+ | `delete-redis-key` | Delete a key |
188
+ | `execute-redis-command` | Execute a raw Redis command |
189
+ | `get-redis-settings` | Get Redis settings |
190
+ | `update-redis-settings` | Update Redis settings |
191
+
192
+ ### Realtime
193
+ | Tool | Description |
194
+ |------|-------------|
195
+ | `get-realtime-status` | Get WebSocket service status |
196
+ | `enable-realtime` | Enable realtime |
197
+ | `disable-realtime` | Disable realtime |
198
+ | `list-realtime-channels` | List active channels |
199
+ | `broadcast-realtime` | Broadcast a message |
200
+ | `get-realtime-settings` | Get realtime settings |
201
+ | `update-realtime-settings` | Update realtime settings |
202
+
203
+ ### Queue
204
+ | Tool | Description |
205
+ |------|-------------|
206
+ | `get-queue-stats` | Get queue statistics |
207
+ | `list-queue-jobs` | List queue jobs |
208
+ | `retry-queue-job` | Retry a failed job |
209
+ | `delete-queue-job` | Delete a job |
210
+
211
+ ### Cron Jobs
212
+ | Tool | Description |
213
+ |------|-------------|
214
+ | `list-cron-jobs` | List scheduled cron jobs |
215
+ | `create-cron-job` | Create a cron job |
216
+ | `delete-cron-job` | Delete a cron job |
217
+ | `toggle-cron-job` | Enable or disable a cron job |
218
+
219
+ ### AI Gateway
220
+ | Tool | Description |
221
+ |------|-------------|
222
+ | `list-ai-keys` | List configured provider keys |
223
+ | `add-ai-key` | Add a provider API key |
224
+ | `delete-ai-key` | Remove a provider key |
225
+ | `test-ai-key` | Test a provider key |
226
+ | `get-ai-config` | Get gateway configuration |
227
+ | `update-ai-config` | Update gateway configuration |
228
+ | `get-ai-usage` | Get usage overview (requests, tokens, cost) |
229
+ | `get-ai-logs` | Get request logs |
230
+ | `get-ai-stats` | Get usage stats by model |
231
+ | `get-ai-models` | List available AI models |
232
+
233
+ ### Vector Store
234
+ | Tool | Description |
235
+ |------|-------------|
236
+ | `list-vector-collections` | List vector collections |
237
+ | `create-vector-collection` | Create a collection |
238
+ | `browse-vectors` | Browse vectors |
239
+ | `upsert-vectors` | Insert or update vectors |
240
+ | `search-vectors` | Search by similarity |
241
+ | `delete-vectors` | Delete vectors |
242
+ | `drop-vector-collection` | Drop a collection |
243
+ | `list-vector-documents` | List ingested documents |
244
+ | `ingest-vectors` | Ingest documents for embedding |
245
+ | `chunk-upload-vectors` | Upload a chunk for large docs |
246
+ | `finalize-chunk-upload` | Finalize chunked upload |
247
+ | `get-vector-job-status` | Get ingest job status |
248
+ | `list-vector-jobs` | List ingest jobs |
249
+ | `get-vector-history` | Get ingest history |
250
+
251
+ ### Git & GitHub
252
+ | Tool | Description |
253
+ |------|-------------|
254
+ | `get-git-status` | Check if a repo is connected |
255
+ | `connect-git` | Connect a GitHub repo |
256
+ | `disconnect-git` | Disconnect the repo |
257
+ | `get-github-status` | Check GitHub account connection |
258
+ | `list-github-repos` | List accessible repositories |
259
+ | `get-repo-contents` | Browse repo file tree |
260
+
261
+ ### Teams
262
+ | Tool | Description |
263
+ |------|-------------|
264
+ | `list-teams` | List all teams |
265
+ | `create-team` | Create a team |
266
+ | `update-team` | Update team name |
267
+ | `delete-team` | Delete a team |
268
+ | `list-team-members` | List members |
269
+ | `add-team-member` | Invite a member |
270
+ | `update-member-role` | Update a member's role |
271
+ | `remove-team-member` | Remove a member |
272
+ | `list-team-invites` | List pending invitations |
273
+ | `delete-team-invite` | Cancel an invitation |
274
+ | `accept-invite` | Accept a team invitation |
275
+
276
+ ### Billing
277
+ | Tool | Description |
278
+ |------|-------------|
279
+ | `get-billing-status` | Get plan and subscription info |
280
+ | `get-subscription` | Get subscription details |
281
+ | `get-billing-usage` | Get resource usage |
282
+ | `list-invoices` | List recent invoices |
283
+ | `upgrade-plan` | Generate upgrade checkout URL |
284
+ | `get-billing-portal` | Get Stripe billing portal link |
285
+ | `cancel-plan` | Cancel subscription |
286
+ | `resume-plan` | Resume cancelled subscription |
287
+ | `update-payment-method` | Update payment method |
288
+
289
+ ### Analytics
290
+ | Tool | Description |
291
+ |------|-------------|
292
+ | `get-analytics-overview` | Get visitors, requests, errors, latency |
293
+ | `get-web-analytics` | Get top pages, referrers, countries |
294
+ | `get-realtime-visitors` | Get live visitor count |
295
+ | `get-analytics-timeseries` | Get timeseries data |
296
+ | `get-request-analytics` | Get request-level analytics |
297
+ | `get-web-vitals` | Get Core Web Vitals |
298
+ | `get-custom-events` | Get custom analytics events |
299
+
300
+ ### Account
301
+ | Tool | Description |
302
+ |------|-------------|
303
+ | `get-me` | Get your profile |
304
+ | `update-me` | Update your profile |
305
+ | `list-api-keys` | List your API keys |
306
+ | `create-api-key` | Create an API key |
307
+ | `delete-api-key` | Delete an API key |
308
+ | `sign-out-all` | Invalidate all sessions |
309
+
310
+ ### Documentation
311
+ | Tool | Description |
312
+ |------|-------------|
313
+ | `search-docs` | Search Pult documentation |
314
+
315
+ ## Resources
316
+
317
+ | URI | Description |
318
+ |-----|-------------|
319
+ | `pult://apps` | All apps |
320
+ | `pult://apps/{id}` | App details |
321
+ | `pult://apps/{id}/deployments` | Deployments |
322
+ | `pult://apps/{id}/env` | Environment variables |
323
+ | `pult://apps/{id}/domains` | Custom domains |
324
+ | `pult://apps/{id}/logs` | Runtime logs |
325
+ | `pult://config/api-url` | Current API URL |
326
+
327
+ ## Environment Variables
328
+
329
+ | Variable | Required | Default | Description |
330
+ |----------|----------|---------|-------------|
331
+ | `PULT_API_KEY` | Yes | — | Your Pult API key |
332
+ | `PULT_API_URL` | No | `https://api.pult.rest` | API base URL |
333
+
334
+ ## License
335
+
336
+ MIT
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, { get: all[name], enumerable: true });
6
+ };
7
+
8
+ // src/http.ts
9
+ import { AsyncLocalStorage } from "async_hooks";
10
+ var apiUrl = process.env["PULT_API_URL"] || "https://api.pult.rest";
11
+ var apiKey = process.env["PULT_API_KEY"] || "";
12
+ var tokenOverride = new AsyncLocalStorage();
13
+ function configure(url, key) {
14
+ apiUrl = url.replace(/\/+$/, "");
15
+ apiKey = key;
16
+ }
17
+ function getApiUrl() {
18
+ return apiUrl;
19
+ }
20
+ function withToken(token, fn) {
21
+ return tokenOverride.run(token, fn);
22
+ }
23
+ async function apiGet(path, params) {
24
+ return request(buildUrl(path, params), { method: "GET" });
25
+ }
26
+ async function apiPost(path, body) {
27
+ return request(buildUrl(path), {
28
+ method: "POST",
29
+ body: body !== void 0 ? JSON.stringify(body) : void 0
30
+ });
31
+ }
32
+ async function apiPatch(path, body) {
33
+ return request(buildUrl(path), {
34
+ method: "PATCH",
35
+ body: body !== void 0 ? JSON.stringify(body) : void 0
36
+ });
37
+ }
38
+ async function apiPut(path, body) {
39
+ return request(buildUrl(path), {
40
+ method: "PUT",
41
+ body: body !== void 0 ? JSON.stringify(body) : void 0
42
+ });
43
+ }
44
+ async function apiDelete(path, body) {
45
+ return request(buildUrl(path), {
46
+ method: "DELETE",
47
+ body: body !== void 0 ? JSON.stringify(body) : void 0
48
+ });
49
+ }
50
+ function buildUrl(path, params) {
51
+ const url = new URL(path, apiUrl);
52
+ if (params) {
53
+ for (const [key, value] of Object.entries(params)) {
54
+ url.searchParams.set(key, value);
55
+ }
56
+ }
57
+ return url.toString();
58
+ }
59
+ async function request(url, init) {
60
+ try {
61
+ const effectiveKey = tokenOverride.getStore() ?? apiKey;
62
+ const response = await fetch(url, {
63
+ ...init,
64
+ headers: {
65
+ "Content-Type": "application/json",
66
+ ...effectiveKey ? { Authorization: `Bearer ${effectiveKey}` } : {}
67
+ }
68
+ });
69
+ if (!response.ok) {
70
+ const body = await response.json().catch(() => ({ error: response.statusText }));
71
+ const message = typeof body["error"] === "string" ? body["error"] : `HTTP ${response.status}: ${response.statusText}`;
72
+ return { data: null, error: message };
73
+ }
74
+ if (response.status === 204 || response.headers.get("content-length") === "0") {
75
+ return { data: null, error: null };
76
+ }
77
+ const data = await response.json();
78
+ return { data, error: null };
79
+ } catch (err) {
80
+ return { data: null, error: err instanceof Error ? err.message : "Unknown network error" };
81
+ }
82
+ }
83
+
84
+ export {
85
+ __export,
86
+ configure,
87
+ getApiUrl,
88
+ withToken,
89
+ apiGet,
90
+ apiPost,
91
+ apiPatch,
92
+ apiPut,
93
+ apiDelete
94
+ };
95
+ //# sourceMappingURL=chunk-2CFKYLYU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/http.ts"],"sourcesContent":["import { AsyncLocalStorage } from \"node:async_hooks\"\n\ninterface ApiResponse<T> {\n data: T | null\n error: string | null\n}\n\nlet apiUrl = process.env[\"PULT_API_URL\"] || \"https://api.pult.rest\"\nlet apiKey = process.env[\"PULT_API_KEY\"] || \"\"\n\nconst tokenOverride = new AsyncLocalStorage<string>()\n\nexport function configure(url: string, key: string): void {\n apiUrl = url.replace(/\\/+$/, \"\")\n apiKey = key\n}\n\nexport function getApiUrl(): string {\n return apiUrl\n}\n\nexport function withToken<T>(token: string, fn: () => T): T {\n return tokenOverride.run(token, fn)\n}\n\nexport async function apiGet<T>(path: string, params?: Record<string, string>): Promise<ApiResponse<T>> {\n return request<T>(buildUrl(path, params), { method: \"GET\" })\n}\n\nexport async function apiPost<T>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>(buildUrl(path), {\n method: \"POST\",\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n}\n\nexport async function apiPatch<T>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>(buildUrl(path), {\n method: \"PATCH\",\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n}\n\nexport async function apiPut<T>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>(buildUrl(path), {\n method: \"PUT\",\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n}\n\nexport async function apiDelete<T>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>(buildUrl(path), {\n method: \"DELETE\",\n body: body !== undefined ? JSON.stringify(body) : undefined,\n })\n}\n\nfunction buildUrl(path: string, params?: Record<string, string>): string {\n const url = new URL(path, apiUrl)\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n url.searchParams.set(key, value)\n }\n }\n return url.toString()\n}\n\nasync function request<T>(url: string, init: RequestInit): Promise<ApiResponse<T>> {\n try {\n const effectiveKey = tokenOverride.getStore() ?? apiKey\n const response = await fetch(url, {\n ...init,\n headers: {\n \"Content-Type\": \"application/json\",\n ...(effectiveKey ? { Authorization: `Bearer ${effectiveKey}` } : {}),\n },\n })\n\n if (!response.ok) {\n const body = await response.json().catch(() => ({ error: response.statusText })) as Record<string, unknown>\n const message = typeof body[\"error\"] === \"string\" ? body[\"error\"] : `HTTP ${response.status}: ${response.statusText}`\n return { data: null, error: message }\n }\n\n if (response.status === 204 || response.headers.get(\"content-length\") === \"0\") {\n return { data: null as T, error: null }\n }\n\n const data = (await response.json()) as T\n return { data, error: null }\n } catch (err) {\n return { data: null, error: err instanceof Error ? err.message : \"Unknown network error\" }\n }\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,yBAAyB;AAOlC,IAAI,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5C,IAAI,SAAS,QAAQ,IAAI,cAAc,KAAK;AAE5C,IAAM,gBAAgB,IAAI,kBAA0B;AAE7C,SAAS,UAAU,KAAa,KAAmB;AACxD,WAAS,IAAI,QAAQ,QAAQ,EAAE;AAC/B,WAAS;AACX;AAEO,SAAS,YAAoB;AAClC,SAAO;AACT;AAEO,SAAS,UAAa,OAAe,IAAgB;AAC1D,SAAO,cAAc,IAAI,OAAO,EAAE;AACpC;AAEA,eAAsB,OAAU,MAAc,QAA0D;AACtG,SAAO,QAAW,SAAS,MAAM,MAAM,GAAG,EAAE,QAAQ,MAAM,CAAC;AAC7D;AAEA,eAAsB,QAAW,MAAc,MAAyC;AACtF,SAAO,QAAW,SAAS,IAAI,GAAG;AAAA,IAChC,QAAQ;AAAA,IACR,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,EACpD,CAAC;AACH;AAEA,eAAsB,SAAY,MAAc,MAAyC;AACvF,SAAO,QAAW,SAAS,IAAI,GAAG;AAAA,IAChC,QAAQ;AAAA,IACR,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,EACpD,CAAC;AACH;AAEA,eAAsB,OAAU,MAAc,MAAyC;AACrF,SAAO,QAAW,SAAS,IAAI,GAAG;AAAA,IAChC,QAAQ;AAAA,IACR,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,EACpD,CAAC;AACH;AAEA,eAAsB,UAAa,MAAc,MAAyC;AACxF,SAAO,QAAW,SAAS,IAAI,GAAG;AAAA,IAChC,QAAQ;AAAA,IACR,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,EACpD,CAAC;AACH;AAEA,SAAS,SAAS,MAAc,QAAyC;AACvE,QAAM,MAAM,IAAI,IAAI,MAAM,MAAM;AAChC,MAAI,QAAQ;AACV,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,UAAI,aAAa,IAAI,KAAK,KAAK;AAAA,IACjC;AAAA,EACF;AACA,SAAO,IAAI,SAAS;AACtB;AAEA,eAAe,QAAW,KAAa,MAA4C;AACjF,MAAI;AACF,UAAM,eAAe,cAAc,SAAS,KAAK;AACjD,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,GAAG;AAAA,MACH,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAI,eAAe,EAAE,eAAe,UAAU,YAAY,GAAG,IAAI,CAAC;AAAA,MACpE;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,SAAS,WAAW,EAAE;AAC/E,YAAM,UAAU,OAAO,KAAK,OAAO,MAAM,WAAW,KAAK,OAAO,IAAI,QAAQ,SAAS,MAAM,KAAK,SAAS,UAAU;AACnH,aAAO,EAAE,MAAM,MAAM,OAAO,QAAQ;AAAA,IACtC;AAEA,QAAI,SAAS,WAAW,OAAO,SAAS,QAAQ,IAAI,gBAAgB,MAAM,KAAK;AAC7E,aAAO,EAAE,MAAM,MAAW,OAAO,KAAK;AAAA,IACxC;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,EAAE,MAAM,OAAO,KAAK;AAAA,EAC7B,SAAS,KAAK;AACZ,WAAO,EAAE,MAAM,MAAM,OAAO,eAAe,QAAQ,IAAI,UAAU,wBAAwB;AAAA,EAC3F;AACF;","names":[]}