@kya-os/mcp-i-cloudflare 1.4.1-canary.1 → 1.4.1-canary.2

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.
@@ -0,0 +1,489 @@
1
+ /**
2
+ * Consent Page Renderer
3
+ *
4
+ * Renders secure consent pages with comprehensive XSS prevention.
5
+ * All user input is sanitized and escaped before rendering.
6
+ *
7
+ * Security Requirements:
8
+ * - All user input escaped with escapeHtml()
9
+ * - URLs validated before use
10
+ * - CSS colors validated
11
+ * - CSP headers enforced
12
+ * - No eval() or innerHTML with user data
13
+ *
14
+ * Related Spec: MCP-I Phase 0 Implementation Plan, Task B.4
15
+ */
16
+ /**
17
+ * Consent Page Renderer
18
+ *
19
+ * Renders HTML consent pages with security-first approach
20
+ */
21
+ export class ConsentPageRenderer {
22
+ /**
23
+ * Render consent page HTML
24
+ *
25
+ * @param config - Consent page configuration (will be sanitized)
26
+ * @returns HTML string
27
+ */
28
+ render(config) {
29
+ const sanitized = this.sanitizeConfig(config);
30
+ return `<!DOCTYPE html>
31
+ <html lang="en">
32
+ <head>
33
+ <meta charset="UTF-8">
34
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
35
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; style-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; img-src 'self' data: https:; font-src 'self' data:;">
36
+ <title>Authorize ${sanitized.tool}</title>
37
+ <script src="https://cdn.tailwindcss.com"></script>
38
+ </head>
39
+ <body class="bg-gray-50 min-h-screen flex items-center justify-center p-4">
40
+ <div class="bg-white rounded-lg shadow-lg max-w-md w-full p-6">
41
+ ${this.renderHeader(sanitized)}
42
+ ${this.renderScopes(sanitized.scopes)}
43
+ ${this.renderTerms(sanitized.terms)}
44
+ ${this.renderCustomFields(sanitized.customFields)}
45
+ ${this.renderForm(sanitized)}
46
+ </div>
47
+ ${this.renderScript(sanitized)}
48
+ </body>
49
+ </html>`;
50
+ }
51
+ /**
52
+ * Render success page HTML
53
+ *
54
+ * @param config - Success page configuration
55
+ * @returns HTML string
56
+ */
57
+ renderSuccess(config) {
58
+ const sanitizedDelegationId = this.escapeHtml(config.delegationId);
59
+ const autoCloseScript = config.autoClose
60
+ ? "<script>setTimeout(function(){window.close();}, 2000);</script>"
61
+ : "";
62
+ return `<!DOCTYPE html>
63
+ <html lang="en">
64
+ <head>
65
+ <meta charset="UTF-8">
66
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
67
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; style-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com;">
68
+ <title>Authorization Successful</title>
69
+ <script src="https://cdn.tailwindcss.com"></script>
70
+ </head>
71
+ <body class="bg-gray-50 min-h-screen flex items-center justify-center">
72
+ <div class="bg-white rounded-lg shadow-lg p-6 text-center">
73
+ <svg class="mx-auto h-12 w-12 text-green-500 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
74
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
75
+ </svg>
76
+ <h1 class="text-2xl font-bold mb-2">Authorization Successful</h1>
77
+ <p class="text-gray-600">You can now close this window.</p>
78
+ <p class="text-sm text-gray-500 mt-2">Delegation ID: <code class="bg-gray-100 px-2 py-1 rounded">${sanitizedDelegationId}</code></p>
79
+ ${autoCloseScript}
80
+ </div>
81
+ </body>
82
+ </html>`;
83
+ }
84
+ /**
85
+ * Escape HTML special characters
86
+ *
87
+ * Prevents XSS by escaping characters that have special meaning in HTML
88
+ *
89
+ * @param text - Text to escape
90
+ * @returns Escaped text
91
+ */
92
+ escapeHtml(text) {
93
+ const map = {
94
+ "&": "&amp;",
95
+ "<": "&lt;",
96
+ ">": "&gt;",
97
+ '"': "&quot;",
98
+ "'": "&#039;",
99
+ "/": "&#x2F;",
100
+ };
101
+ return text.replace(/[&<>"'/]/g, (m) => map[m]);
102
+ }
103
+ /**
104
+ * Validate and sanitize URL
105
+ *
106
+ * @param url - URL to validate
107
+ * @returns Sanitized URL or original if invalid (for fallback)
108
+ */
109
+ validateUrl(url) {
110
+ try {
111
+ const parsed = new URL(url);
112
+ // Only allow http/https protocols
113
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
114
+ return url; // Return original for fallback (will be handled by caller)
115
+ }
116
+ return url; // Return original URL (already safe after validation)
117
+ }
118
+ catch {
119
+ return url; // Return original for fallback
120
+ }
121
+ }
122
+ /**
123
+ * Validate CSS color (hex format)
124
+ *
125
+ * @param color - Color to validate
126
+ * @returns Validated color or default
127
+ */
128
+ validateColor(color) {
129
+ // Must be valid hex color (#RRGGBB)
130
+ if (/^#[0-9A-Fa-f]{6}$/.test(color)) {
131
+ return color;
132
+ }
133
+ // Return safe default
134
+ return "#2563eb";
135
+ }
136
+ /**
137
+ * Sanitize consent page configuration
138
+ *
139
+ * Deep sanitization of all user input fields
140
+ * Note: serverUrl is validated but not escaped (needed for JavaScript)
141
+ *
142
+ * @param config - Configuration to sanitize
143
+ * @returns Sanitized configuration
144
+ */
145
+ sanitizeConfig(config) {
146
+ // Validate serverUrl but don't escape it (needed in JavaScript)
147
+ // If validation fails, keep original (will be handled by form action)
148
+ const validatedServerUrl = this.validateUrl(config.serverUrl);
149
+ return {
150
+ ...config,
151
+ tool: this.escapeHtml(config.tool),
152
+ toolDescription: this.escapeHtml(config.toolDescription),
153
+ scopes: config.scopes.map((s) => this.escapeHtml(s)),
154
+ agentDid: this.escapeHtml(config.agentDid),
155
+ sessionId: this.escapeHtml(config.sessionId),
156
+ projectId: this.escapeHtml(config.projectId),
157
+ serverUrl: validatedServerUrl, // Validated but not escaped (safe for JS)
158
+ branding: config.branding
159
+ ? this.sanitizeBranding(config.branding)
160
+ : undefined,
161
+ terms: config.terms ? this.sanitizeTerms(config.terms) : undefined,
162
+ customFields: config.customFields
163
+ ? config.customFields.map((f) => this.sanitizeCustomField(f))
164
+ : undefined,
165
+ };
166
+ }
167
+ /**
168
+ * Sanitize branding configuration
169
+ *
170
+ * @param branding - Branding to sanitize
171
+ * @returns Sanitized branding
172
+ */
173
+ sanitizeBranding(branding) {
174
+ // Validate logoUrl - if invalid, set to undefined to prevent rendering
175
+ let logoUrl = branding.logoUrl;
176
+ if (logoUrl) {
177
+ const validated = this.validateUrl(logoUrl);
178
+ // If validation failed (returned original but protocol is invalid), don't use it
179
+ try {
180
+ const parsed = new URL(validated);
181
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
182
+ logoUrl = undefined; // Invalid protocol, don't render
183
+ }
184
+ }
185
+ catch {
186
+ logoUrl = undefined; // Invalid URL, don't render
187
+ }
188
+ }
189
+ return {
190
+ ...branding,
191
+ primaryColor: branding.primaryColor
192
+ ? this.validateColor(branding.primaryColor)
193
+ : undefined,
194
+ logoUrl,
195
+ companyName: branding.companyName
196
+ ? this.escapeHtml(branding.companyName)
197
+ : undefined,
198
+ theme: branding.theme === "light" ||
199
+ branding.theme === "dark" ||
200
+ branding.theme === "auto"
201
+ ? branding.theme
202
+ : undefined,
203
+ };
204
+ }
205
+ /**
206
+ * Sanitize terms configuration
207
+ *
208
+ * @param terms - Terms to sanitize
209
+ * @returns Sanitized terms
210
+ */
211
+ sanitizeTerms(terms) {
212
+ return {
213
+ ...terms,
214
+ text: terms.text ? this.escapeHtml(terms.text) : undefined,
215
+ url: terms.url ? this.validateUrl(terms.url) : undefined,
216
+ version: terms.version ? this.escapeHtml(terms.version) : undefined,
217
+ };
218
+ }
219
+ /**
220
+ * Sanitize custom field
221
+ *
222
+ * @param field - Field to sanitize
223
+ * @returns Sanitized field
224
+ */
225
+ sanitizeCustomField(field) {
226
+ return {
227
+ ...field,
228
+ name: this.escapeHtml(field.name),
229
+ label: this.escapeHtml(field.label),
230
+ placeholder: field.placeholder
231
+ ? this.escapeHtml(field.placeholder)
232
+ : undefined,
233
+ options: field.options
234
+ ? field.options.map((opt) => ({
235
+ value: this.escapeHtml(opt.value),
236
+ label: this.escapeHtml(opt.label),
237
+ }))
238
+ : undefined,
239
+ pattern: field.pattern ? this.escapeHtml(field.pattern) : undefined,
240
+ };
241
+ }
242
+ /**
243
+ * Render page header
244
+ *
245
+ * @param config - Sanitized configuration
246
+ * @returns HTML string
247
+ */
248
+ renderHeader(config) {
249
+ const branding = config.branding;
250
+ const logoHtml = branding?.logoUrl
251
+ ? `<img src="${branding.logoUrl}" alt="${branding.companyName || "Logo"}" class="h-12 w-auto mb-4" />`
252
+ : "";
253
+ const companyName = branding?.companyName
254
+ ? `<p class="text-sm text-gray-600 mb-2">${branding.companyName}</p>`
255
+ : "";
256
+ const primaryColor = branding?.primaryColor || "#2563eb";
257
+ return `
258
+ <div class="text-center mb-6">
259
+ ${logoHtml}
260
+ ${companyName}
261
+ <h1 class="text-2xl font-bold mb-2" style="color: ${primaryColor};">Authorize ${config.tool}</h1>
262
+ <p class="text-gray-600">${config.toolDescription}</p>
263
+ </div>`;
264
+ }
265
+ /**
266
+ * Render scopes list
267
+ *
268
+ * @param scopes - Sanitized scopes
269
+ * @returns HTML string
270
+ */
271
+ renderScopes(scopes) {
272
+ if (scopes.length === 0) {
273
+ return '<div class="mb-4"><p class="text-sm text-gray-600">No specific permissions required.</p></div>';
274
+ }
275
+ const scopeItems = scopes
276
+ .map((scope) => `
277
+ <li class="flex items-start">
278
+ <svg class="h-5 w-5 text-green-500 mr-2 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
279
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
280
+ </svg>
281
+ <span class="text-sm text-gray-700">${scope}</span>
282
+ </li>`)
283
+ .join("");
284
+ return `
285
+ <div class="mb-6">
286
+ <h2 class="text-lg font-semibold mb-3">Permissions Requested</h2>
287
+ <ul class="space-y-2">
288
+ ${scopeItems}
289
+ </ul>
290
+ </div>`;
291
+ }
292
+ /**
293
+ * Render terms section
294
+ *
295
+ * @param terms - Sanitized terms
296
+ * @returns HTML string
297
+ */
298
+ renderTerms(terms) {
299
+ if (!terms) {
300
+ return "";
301
+ }
302
+ const termsText = terms.text
303
+ ? `<p class="text-sm text-gray-600 mb-4">${terms.text}</p>`
304
+ : "";
305
+ const termsLink = terms.url
306
+ ? `<a href="${terms.url}" target="_blank" rel="noopener noreferrer" class="text-blue-600 hover:underline text-sm">View Terms</a>`
307
+ : "";
308
+ return `
309
+ <div class="mb-6">
310
+ ${termsText}
311
+ ${termsLink}
312
+ </div>`;
313
+ }
314
+ /**
315
+ * Render custom fields
316
+ *
317
+ * @param fields - Sanitized custom fields
318
+ * @returns HTML string
319
+ */
320
+ renderCustomFields(fields) {
321
+ if (!fields || fields.length === 0) {
322
+ return "";
323
+ }
324
+ const fieldHtml = fields
325
+ .map((field) => {
326
+ const requiredAttr = field.required ? "required" : "";
327
+ const patternAttr = field.pattern ? `pattern="${field.pattern}"` : "";
328
+ const placeholderAttr = field.placeholder
329
+ ? `placeholder="${field.placeholder}"`
330
+ : "";
331
+ if (field.type === "checkbox") {
332
+ return `
333
+ <div class="mb-4">
334
+ <label class="flex items-center">
335
+ <input type="checkbox" name="${field.name}" ${requiredAttr} class="mr-2" />
336
+ <span class="text-sm text-gray-700">${field.label}</span>
337
+ </label>
338
+ </div>`;
339
+ }
340
+ else if (field.type === "select") {
341
+ const options = field.options
342
+ ? field.options
343
+ .map((opt) => `<option value="${opt.value}">${opt.label}</option>`)
344
+ .join("")
345
+ : "";
346
+ return `
347
+ <div class="mb-4">
348
+ <label class="block text-sm font-medium text-gray-700 mb-1">${field.label}</label>
349
+ <select name="${field.name}" ${requiredAttr} class="w-full px-3 py-2 border border-gray-300 rounded-md">
350
+ ${options}
351
+ </select>
352
+ </div>`;
353
+ }
354
+ else if (field.type === "textarea") {
355
+ return `
356
+ <div class="mb-4">
357
+ <label class="block text-sm font-medium text-gray-700 mb-1">${field.label}</label>
358
+ <textarea name="${field.name}" ${requiredAttr} ${placeholderAttr} rows="3" class="w-full px-3 py-2 border border-gray-300 rounded-md"></textarea>
359
+ </div>`;
360
+ }
361
+ else {
362
+ // text field
363
+ return `
364
+ <div class="mb-4">
365
+ <label class="block text-sm font-medium text-gray-700 mb-1">${field.label}</label>
366
+ <input type="text" name="${field.name}" ${requiredAttr} ${placeholderAttr} ${patternAttr} class="w-full px-3 py-2 border border-gray-300 rounded-md" />
367
+ </div>`;
368
+ }
369
+ })
370
+ .join("");
371
+ return `
372
+ <div class="mb-6">
373
+ <h2 class="text-lg font-semibold mb-3">Additional Information</h2>
374
+ ${fieldHtml}
375
+ </div>`;
376
+ }
377
+ /**
378
+ * Render form
379
+ *
380
+ * @param config - Sanitized configuration
381
+ * @returns HTML string
382
+ */
383
+ renderForm(config) {
384
+ const scopesJson = JSON.stringify(config.scopes);
385
+ const agentDid = config.agentDid;
386
+ const sessionId = config.sessionId;
387
+ const projectId = config.projectId;
388
+ const tool = config.tool;
389
+ const serverUrl = config.serverUrl; // Already validated
390
+ return `
391
+ <form id="consent-form" method="POST" action="${serverUrl}/consent/approve" class="space-y-4">
392
+ <input type="hidden" name="tool" value="${tool}" />
393
+ <input type="hidden" name="scopes" value="${scopesJson}" />
394
+ <input type="hidden" name="agent_did" value="${agentDid}" />
395
+ <input type="hidden" name="session_id" value="${sessionId}" />
396
+ <input type="hidden" name="project_id" value="${projectId}" />
397
+
398
+ ${config.terms?.required
399
+ ? `
400
+ <div class="mb-4">
401
+ <label class="flex items-start">
402
+ <input type="checkbox" name="termsAccepted" required class="mr-2 mt-1" />
403
+ <span class="text-sm text-gray-700">I accept the terms and conditions</span>
404
+ </label>
405
+ </div>
406
+ `
407
+ : ""}
408
+
409
+ <div class="flex gap-3">
410
+ <button type="submit" class="flex-1 bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
411
+ Approve
412
+ </button>
413
+ <button type="button" onclick="window.close()" class="flex-1 bg-gray-200 text-gray-700 px-4 py-2 rounded-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2">
414
+ Cancel
415
+ </button>
416
+ </div>
417
+ </form>`;
418
+ }
419
+ /**
420
+ * Render JavaScript for form handling
421
+ *
422
+ * @param config - Sanitized configuration
423
+ * @returns HTML string
424
+ */
425
+ renderScript(config) {
426
+ // serverUrl is already validated, but we need to escape it for JavaScript string
427
+ // Use JSON.stringify to safely embed the URL in JavaScript
428
+ const serverUrlJs = JSON.stringify(config.serverUrl);
429
+ return `
430
+ <script>
431
+ (function() {
432
+ const form = document.getElementById('consent-form');
433
+ if (!form) return;
434
+
435
+ const serverUrl = ${serverUrlJs};
436
+
437
+ form.addEventListener('submit', async function(e) {
438
+ e.preventDefault();
439
+
440
+ const formData = new FormData(form);
441
+ const data = {
442
+ tool: formData.get('tool'),
443
+ scopes: JSON.parse(formData.get('scopes')),
444
+ agent_did: formData.get('agent_did'),
445
+ session_id: formData.get('session_id'),
446
+ project_id: formData.get('project_id'),
447
+ termsAccepted: formData.get('termsAccepted') === 'on',
448
+ customFields: {}
449
+ };
450
+
451
+ // Collect custom fields
452
+ ${config.customFields
453
+ ?.map((field) => {
454
+ // Field names are already sanitized, but escape for JS string safety
455
+ const fieldNameJs = JSON.stringify(field.name);
456
+ if (field.type === "checkbox") {
457
+ return `data.customFields[${fieldNameJs}] = formData.get(${fieldNameJs}) === 'on';`;
458
+ }
459
+ else {
460
+ return `const ${field.name.replace(/[^a-zA-Z0-9_]/g, "_")}Value = formData.get(${fieldNameJs}); if (${field.name.replace(/[^a-zA-Z0-9_]/g, "_")}Value) data.customFields[${fieldNameJs}] = ${field.name.replace(/[^a-zA-Z0-9_]/g, "_")}Value;`;
461
+ }
462
+ })
463
+ .join("\n ") || ""}
464
+
465
+ try {
466
+ const response = await fetch(serverUrl + '/consent/approve', {
467
+ method: 'POST',
468
+ headers: {
469
+ 'Content-Type': 'application/json'
470
+ },
471
+ body: JSON.stringify(data)
472
+ });
473
+
474
+ const result = await response.json();
475
+
476
+ if (result.success) {
477
+ window.location.href = serverUrl + '/consent/success?delegation_id=' + encodeURIComponent(result.delegation_id);
478
+ } else {
479
+ alert('Authorization failed: ' + (result.error || 'Unknown error'));
480
+ }
481
+ } catch (error) {
482
+ alert('Error submitting authorization: ' + error.message);
483
+ }
484
+ });
485
+ })();
486
+ </script>`;
487
+ }
488
+ }
489
+ //# sourceMappingURL=consent-page-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent-page-renderer.js","sourceRoot":"","sources":["../../src/services/consent-page-renderer.ts"],"names":[],"mappings":"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"}
@@ -2,24 +2,78 @@
2
2
  * Consent Service
3
3
  *
4
4
  * Handles consent page rendering and approval handling.
5
- * Structure only - full implementation in Phase 0.
5
+ * Complete implementation for Phase 0.
6
+ *
7
+ * Related Spec: MCP-I Phase 0 Implementation Plan, Task B.5
6
8
  */
9
+ import type { CloudflareEnv } from '../types';
10
+ import type { CloudflareRuntime } from '../runtime';
7
11
  export declare class ConsentService {
12
+ private configService;
13
+ private renderer;
14
+ private env;
15
+ private runtime?;
16
+ constructor(env: CloudflareEnv, runtime?: CloudflareRuntime);
8
17
  /**
9
18
  * Handle consent requests
19
+ *
20
+ * Routes:
21
+ * - GET /consent - Render consent page
22
+ * - POST /consent/approve - Handle approval
23
+ * - GET /consent/success - Success page
24
+ *
10
25
  * @param request - Incoming request
11
26
  * @returns Response
12
27
  */
13
28
  handle(request: Request): Promise<Response>;
14
29
  /**
15
30
  * Render consent page
16
- * TODO: Implement in Phase 0
31
+ *
32
+ * Query parameters:
33
+ * - tool: Tool name
34
+ * - scopes: Comma-separated scopes
35
+ * - agent_did: Agent DID
36
+ * - session_id: Session ID
37
+ * - project_id: Project ID
38
+ *
39
+ * @param params - URL search parameters
40
+ * @returns HTML response
17
41
  */
18
42
  private renderConsentPage;
19
43
  /**
20
44
  * Handle consent approval
21
- * TODO: Implement in Phase 0
45
+ *
46
+ * Validates request, creates delegation via AgentShield API,
47
+ * stores token in KV, and returns success response.
48
+ *
49
+ * @param request - Approval request
50
+ * @returns JSON response
22
51
  */
23
52
  private handleApproval;
53
+ /**
54
+ * Create delegation via AgentShield API
55
+ *
56
+ * @param request - Approval request
57
+ * @returns Delegation creation result
58
+ */
59
+ private createDelegation;
60
+ /**
61
+ * Store delegation token in KV
62
+ *
63
+ * Stores token using both session ID and agent DID keys for resilience.
64
+ *
65
+ * @param sessionId - Session ID
66
+ * @param agentDid - Agent DID
67
+ * @param token - Delegation token
68
+ * @param delegationId - Delegation ID
69
+ */
70
+ private storeDelegationToken;
71
+ /**
72
+ * Render success page
73
+ *
74
+ * @param params - URL search parameters (delegation_id)
75
+ * @returns HTML response
76
+ */
77
+ private renderSuccessPage;
24
78
  }
25
79
  //# sourceMappingURL=consent.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"consent.service.d.ts","sourceRoot":"","sources":["../../src/services/consent.service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qBAAa,cAAc;IACzB;;;;OAIG;IACG,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAgBjD;;;OAGG;YACW,iBAAiB;IAQ/B;;;OAGG;YACW,cAAc;CAO7B"}
1
+ {"version":3,"file":"consent.service.d.ts","sourceRoot":"","sources":["../../src/services/consent.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgBpD,qBAAa,cAAc;IACzB,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,GAAG,CAAgB;IAC3B,OAAO,CAAC,OAAO,CAAC,CAAoB;gBAExB,GAAG,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,iBAAiB;IAO3D;;;;;;;;;;OAUG;IACG,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAqBjD;;;;;;;;;;;;OAYG;YACW,iBAAiB;IA6F/B;;;;;;;;OAQG;YACW,cAAc;IA2F5B;;;;;OAKG;YACW,gBAAgB;IAqG9B;;;;;;;;;OASG;YACW,oBAAoB;IA4ClC;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;CAkC1B"}