@mcp-z/oauth-google 1.0.0 ā 1.0.1
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 +8 -0
- package/dist/cjs/index.d.cts +2 -1
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/dcr-router.js.map +1 -1
- package/dist/cjs/lib/dcr-utils.js.map +1 -1
- package/dist/cjs/lib/dcr-verify.js.map +1 -1
- package/dist/cjs/lib/fetch-with-timeout.js.map +1 -1
- package/dist/cjs/lib/loopback-router.d.cts +8 -0
- package/dist/cjs/lib/loopback-router.d.ts +8 -0
- package/dist/cjs/lib/loopback-router.js +219 -0
- package/dist/cjs/lib/loopback-router.js.map +1 -0
- package/dist/cjs/lib/token-verifier.js.map +1 -1
- package/dist/cjs/providers/dcr.js.map +1 -1
- package/dist/cjs/providers/loopback-oauth.d.cts +15 -17
- package/dist/cjs/providers/loopback-oauth.d.ts +15 -17
- package/dist/cjs/providers/loopback-oauth.js +223 -156
- package/dist/cjs/providers/loopback-oauth.js.map +1 -1
- package/dist/cjs/providers/service-account.js.map +1 -1
- package/dist/cjs/schemas/index.js.map +1 -1
- package/dist/cjs/setup/config.d.cts +6 -1
- package/dist/cjs/setup/config.d.ts +6 -1
- package/dist/cjs/setup/config.js +3 -0
- package/dist/cjs/setup/config.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/dcr-router.js.map +1 -1
- package/dist/esm/lib/dcr-utils.js.map +1 -1
- package/dist/esm/lib/dcr-verify.js.map +1 -1
- package/dist/esm/lib/fetch-with-timeout.js.map +1 -1
- package/dist/esm/lib/loopback-router.d.ts +8 -0
- package/dist/esm/lib/loopback-router.js +32 -0
- package/dist/esm/lib/loopback-router.js.map +1 -0
- package/dist/esm/lib/token-verifier.js.map +1 -1
- package/dist/esm/providers/dcr.js.map +1 -1
- package/dist/esm/providers/loopback-oauth.d.ts +15 -17
- package/dist/esm/providers/loopback-oauth.js +152 -114
- package/dist/esm/providers/loopback-oauth.js.map +1 -1
- package/dist/esm/providers/service-account.js.map +1 -1
- package/dist/esm/schemas/index.js.map +1 -1
- package/dist/esm/setup/config.d.ts +6 -1
- package/dist/esm/setup/config.js +4 -0
- package/dist/esm/setup/config.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Implements OAuth 2.0 Authorization Code Flow with PKCE using loopback interface redirection.
|
|
5
5
|
* Uses ephemeral local server with OS-assigned port (RFC 8252 Section 8.3).
|
|
6
|
-
*/ import { addAccount, generatePKCE, getActiveAccount, getErrorTemplate, getSuccessTemplate, getToken,
|
|
6
|
+
*/ import { addAccount, generatePKCE, getActiveAccount, getErrorTemplate, getSuccessTemplate, getToken, setAccountInfo, setActiveAccount, setToken } from '@mcp-z/oauth';
|
|
7
|
+
import { randomUUID } from 'crypto';
|
|
7
8
|
import { OAuth2Client } from 'google-auth-library';
|
|
8
9
|
import * as http from 'http';
|
|
9
10
|
import open from 'open';
|
|
@@ -66,65 +67,56 @@ import { AuthRequiredError } from '../types.js';
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
|
-
// No valid token or no account -
|
|
70
|
-
const {
|
|
71
|
-
if (
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
// No valid token or no account - need OAuth authentication
|
|
71
|
+
const { clientId, scope, redirectUri } = this.config;
|
|
72
|
+
if (redirectUri) {
|
|
73
|
+
// Persistent callback mode (cloud deployment with configured redirect_uri)
|
|
74
|
+
const { verifier: codeVerifier, challenge: codeChallenge } = generatePKCE();
|
|
75
|
+
const stateId = randomUUID();
|
|
76
|
+
// Store PKCE verifier for callback (5 minute TTL)
|
|
77
|
+
await tokenStore.set(`${service}:pending:${stateId}`, {
|
|
78
|
+
codeVerifier,
|
|
79
|
+
createdAt: Date.now()
|
|
80
|
+
}, 5 * 60 * 1000);
|
|
81
|
+
// Build auth URL with configured redirect_uri
|
|
80
82
|
const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
|
|
81
83
|
authUrl.searchParams.set('client_id', clientId);
|
|
84
|
+
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
82
85
|
authUrl.searchParams.set('response_type', 'code');
|
|
83
86
|
authUrl.searchParams.set('scope', scope);
|
|
84
87
|
authUrl.searchParams.set('access_type', 'offline');
|
|
88
|
+
authUrl.searchParams.set('code_challenge', codeChallenge);
|
|
89
|
+
authUrl.searchParams.set('code_challenge_method', 'S256');
|
|
90
|
+
authUrl.searchParams.set('state', stateId);
|
|
85
91
|
authUrl.searchParams.set('prompt', 'consent');
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
} else {
|
|
92
|
-
hint = 'Use account-add to authenticate interactively';
|
|
93
|
-
}
|
|
94
|
-
const baseDescriptor = {
|
|
92
|
+
logger.info('OAuth required - persistent callback mode', {
|
|
93
|
+
service,
|
|
94
|
+
redirectUri
|
|
95
|
+
});
|
|
96
|
+
throw new AuthRequiredError({
|
|
95
97
|
kind: 'auth_url',
|
|
96
|
-
provider:
|
|
97
|
-
url: authUrl.toString()
|
|
98
|
-
|
|
99
|
-
};
|
|
100
|
-
const descriptor = effectiveAccountId ? {
|
|
101
|
-
...baseDescriptor,
|
|
102
|
-
accountId: effectiveAccountId
|
|
103
|
-
} : baseDescriptor;
|
|
104
|
-
throw new AuthRequiredError(descriptor);
|
|
98
|
+
provider: service,
|
|
99
|
+
url: authUrl.toString()
|
|
100
|
+
});
|
|
105
101
|
}
|
|
106
|
-
//
|
|
102
|
+
// Ephemeral callback mode (local development)
|
|
107
103
|
logger.info('Starting ephemeral OAuth flow', {
|
|
108
104
|
service,
|
|
109
|
-
headless
|
|
105
|
+
headless: this.config.headless
|
|
110
106
|
});
|
|
111
107
|
const { token, email } = await this.performEphemeralOAuthFlow();
|
|
112
|
-
// Store token with email as accountId
|
|
113
108
|
await setToken(tokenStore, {
|
|
114
109
|
accountId: email,
|
|
115
110
|
service
|
|
116
111
|
}, token);
|
|
117
|
-
// Register account in account management system
|
|
118
112
|
await addAccount(tokenStore, {
|
|
119
113
|
service,
|
|
120
114
|
accountId: email
|
|
121
115
|
});
|
|
122
|
-
// Set as active account so subsequent getAccessToken() calls find it
|
|
123
116
|
await setActiveAccount(tokenStore, {
|
|
124
117
|
service,
|
|
125
118
|
accountId: email
|
|
126
119
|
});
|
|
127
|
-
// Store account metadata (email, added timestamp)
|
|
128
120
|
await setAccountInfo(tokenStore, {
|
|
129
121
|
service,
|
|
130
122
|
accountId: email
|
|
@@ -170,51 +162,6 @@ import { AuthRequiredError } from '../types.js';
|
|
|
170
162
|
return client;
|
|
171
163
|
}
|
|
172
164
|
/**
|
|
173
|
-
* Authenticate new account with OAuth flow
|
|
174
|
-
* Triggers account selection, stores token, registers account
|
|
175
|
-
*
|
|
176
|
-
* @returns Email address of newly authenticated account
|
|
177
|
-
* @throws Error in headless mode (cannot open browser for OAuth)
|
|
178
|
-
*/ async authenticateNewAccount() {
|
|
179
|
-
const { logger, headless, service, tokenStore } = this.config;
|
|
180
|
-
if (headless) {
|
|
181
|
-
throw new Error('Cannot authenticate new account in headless mode - interactive OAuth required');
|
|
182
|
-
}
|
|
183
|
-
logger.info('Starting new account authentication', {
|
|
184
|
-
service
|
|
185
|
-
});
|
|
186
|
-
// Trigger OAuth with account selection
|
|
187
|
-
const { token, email } = await this.performEphemeralOAuthFlow();
|
|
188
|
-
// Store token
|
|
189
|
-
await setToken(tokenStore, {
|
|
190
|
-
accountId: email,
|
|
191
|
-
service
|
|
192
|
-
}, token);
|
|
193
|
-
// Register account
|
|
194
|
-
await addAccount(tokenStore, {
|
|
195
|
-
service,
|
|
196
|
-
accountId: email
|
|
197
|
-
});
|
|
198
|
-
// Set as active account
|
|
199
|
-
await setActiveAccount(tokenStore, {
|
|
200
|
-
service,
|
|
201
|
-
accountId: email
|
|
202
|
-
});
|
|
203
|
-
// Store account metadata
|
|
204
|
-
await setAccountInfo(tokenStore, {
|
|
205
|
-
service,
|
|
206
|
-
accountId: email
|
|
207
|
-
}, {
|
|
208
|
-
email,
|
|
209
|
-
addedAt: new Date().toISOString()
|
|
210
|
-
});
|
|
211
|
-
logger.info('New account authenticated', {
|
|
212
|
-
service,
|
|
213
|
-
email
|
|
214
|
-
});
|
|
215
|
-
return email;
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
165
|
* Get user email from Google's userinfo endpoint (pure query)
|
|
219
166
|
* Used to query email for existing authenticated account
|
|
220
167
|
*
|
|
@@ -235,16 +182,6 @@ import { AuthRequiredError } from '../types.js';
|
|
|
235
182
|
const userInfo = await response.json();
|
|
236
183
|
return userInfo.email;
|
|
237
184
|
}
|
|
238
|
-
/**
|
|
239
|
-
* Check for existing accounts in token storage (incremental OAuth detection)
|
|
240
|
-
*
|
|
241
|
-
* Uses key-utils helper for forward compatibility with key format changes.
|
|
242
|
-
*
|
|
243
|
-
* @returns Array of account IDs that have tokens for this service
|
|
244
|
-
*/ async getExistingAccounts() {
|
|
245
|
-
const { service, tokenStore } = this.config;
|
|
246
|
-
return listAccountIds(tokenStore, service);
|
|
247
|
-
}
|
|
248
185
|
isTokenValid(token) {
|
|
249
186
|
if (!token.expiresAt) return true; // No expiry = assume valid
|
|
250
187
|
return Date.now() < token.expiresAt - 60000; // 1 minute buffer
|
|
@@ -275,42 +212,46 @@ import { AuthRequiredError } from '../types.js';
|
|
|
275
212
|
}
|
|
276
213
|
async performEphemeralOAuthFlow() {
|
|
277
214
|
const { clientId, scope, headless, logger, redirectUri: configRedirectUri } = this.config;
|
|
278
|
-
//
|
|
279
|
-
let
|
|
280
|
-
let
|
|
281
|
-
|
|
215
|
+
// Server listen configuration (where ephemeral server binds)
|
|
216
|
+
let listenHost = 'localhost'; // Default: localhost for ephemeral loopback
|
|
217
|
+
let listenPort = 0; // Default: OS-assigned ephemeral port
|
|
218
|
+
// Redirect URI configuration (what goes in auth URL and token exchange)
|
|
282
219
|
let callbackPath = '/callback'; // Default callback path
|
|
283
220
|
let useConfiguredUri = false;
|
|
284
221
|
if (configRedirectUri) {
|
|
285
222
|
try {
|
|
286
223
|
const parsed = new URL(configRedirectUri);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
targetPort = Number.parseInt(parsed.port, 10);
|
|
224
|
+
const isLoopback = parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';
|
|
225
|
+
if (isLoopback) {
|
|
226
|
+
// Local development: Listen on specific loopback address/port
|
|
227
|
+
listenHost = parsed.hostname;
|
|
228
|
+
listenPort = parsed.port ? Number.parseInt(parsed.port, 10) : 0;
|
|
293
229
|
} else {
|
|
294
|
-
|
|
230
|
+
// Cloud deployment: Listen on 0.0.0.0 with PORT from environment
|
|
231
|
+
// The redirectUri is the PUBLIC URL (e.g., https://example.com/oauth/callback)
|
|
232
|
+
// The server listens on 0.0.0.0:PORT and the load balancer routes to it
|
|
233
|
+
listenHost = '0.0.0.0';
|
|
234
|
+
const envPort = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : undefined;
|
|
235
|
+
listenPort = envPort && Number.isFinite(envPort) ? envPort : 8080;
|
|
295
236
|
}
|
|
296
|
-
// Extract
|
|
237
|
+
// Extract callback path from URL
|
|
297
238
|
if (parsed.pathname && parsed.pathname !== '/') {
|
|
298
239
|
callbackPath = parsed.pathname;
|
|
299
240
|
}
|
|
300
241
|
useConfiguredUri = true;
|
|
301
242
|
logger.debug('Using configured redirect URI', {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
243
|
+
listenHost,
|
|
244
|
+
listenPort,
|
|
245
|
+
callbackPath,
|
|
246
|
+
redirectUri: configRedirectUri,
|
|
247
|
+
isLoopback
|
|
307
248
|
});
|
|
308
249
|
} catch (error) {
|
|
309
250
|
logger.warn('Failed to parse redirectUri, using ephemeral defaults', {
|
|
310
251
|
redirectUri: configRedirectUri,
|
|
311
252
|
error: error instanceof Error ? error.message : String(error)
|
|
312
253
|
});
|
|
313
|
-
// Continue with defaults (
|
|
254
|
+
// Continue with defaults (localhost, port 0, http, /callback)
|
|
314
255
|
}
|
|
315
256
|
}
|
|
316
257
|
return new Promise((resolve, reject)=>{
|
|
@@ -397,8 +338,8 @@ import { AuthRequiredError } from '../types.js';
|
|
|
397
338
|
res.end('Not Found');
|
|
398
339
|
}
|
|
399
340
|
});
|
|
400
|
-
// Listen on
|
|
401
|
-
server.listen(
|
|
341
|
+
// Listen on configured host/port
|
|
342
|
+
server.listen(listenPort, listenHost, ()=>{
|
|
402
343
|
const address = server === null || server === void 0 ? void 0 : server.address();
|
|
403
344
|
if (!address || typeof address === 'string') {
|
|
404
345
|
server === null || server === void 0 ? void 0 : server.close();
|
|
@@ -408,11 +349,11 @@ import { AuthRequiredError } from '../types.js';
|
|
|
408
349
|
serverPort = address.port;
|
|
409
350
|
// Construct final redirect URI
|
|
410
351
|
if (useConfiguredUri && configRedirectUri) {
|
|
411
|
-
// Use configured redirect URI as-is for
|
|
352
|
+
// Use configured redirect URI as-is (public URL for cloud, or specific local URL)
|
|
412
353
|
finalRedirectUri = configRedirectUri;
|
|
413
354
|
} else {
|
|
414
|
-
// Construct ephemeral redirect URI with actual server port
|
|
415
|
-
finalRedirectUri =
|
|
355
|
+
// Construct ephemeral redirect URI with actual server port (default local behavior)
|
|
356
|
+
finalRedirectUri = `http://localhost:${serverPort}${callbackPath}`;
|
|
416
357
|
}
|
|
417
358
|
// Build auth URL
|
|
418
359
|
const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
|
|
@@ -518,6 +459,103 @@ import { AuthRequiredError } from '../types.js';
|
|
|
518
459
|
};
|
|
519
460
|
}
|
|
520
461
|
/**
|
|
462
|
+
* Handle OAuth callback from persistent endpoint.
|
|
463
|
+
* Used by HTTP servers with configured redirectUri.
|
|
464
|
+
*
|
|
465
|
+
* @param params - OAuth callback parameters
|
|
466
|
+
* @returns Email and cached token
|
|
467
|
+
*/ async handleOAuthCallback(params) {
|
|
468
|
+
const { code, state } = params;
|
|
469
|
+
const { logger, service, tokenStore, clientId, clientSecret, redirectUri } = this.config;
|
|
470
|
+
if (!state) {
|
|
471
|
+
throw new Error('Missing state parameter in OAuth callback');
|
|
472
|
+
}
|
|
473
|
+
if (!redirectUri) {
|
|
474
|
+
throw new Error('handleOAuthCallback requires configured redirectUri');
|
|
475
|
+
}
|
|
476
|
+
// Load pending auth (includes PKCE verifier)
|
|
477
|
+
const pendingKey = `${service}:pending:${state}`;
|
|
478
|
+
const pendingAuth = await tokenStore.get(pendingKey);
|
|
479
|
+
if (!pendingAuth) {
|
|
480
|
+
throw new Error('Invalid or expired OAuth state. Please try again.');
|
|
481
|
+
}
|
|
482
|
+
// Check TTL (5 minutes)
|
|
483
|
+
if (Date.now() - pendingAuth.createdAt > 5 * 60 * 1000) {
|
|
484
|
+
await tokenStore.delete(pendingKey);
|
|
485
|
+
throw new Error('OAuth state expired. Please try again.');
|
|
486
|
+
}
|
|
487
|
+
logger.info('Processing OAuth callback', {
|
|
488
|
+
service,
|
|
489
|
+
state
|
|
490
|
+
});
|
|
491
|
+
// Exchange code for token
|
|
492
|
+
const body = new URLSearchParams({
|
|
493
|
+
code,
|
|
494
|
+
client_id: clientId,
|
|
495
|
+
...clientSecret && {
|
|
496
|
+
client_secret: clientSecret
|
|
497
|
+
},
|
|
498
|
+
redirect_uri: redirectUri,
|
|
499
|
+
grant_type: 'authorization_code',
|
|
500
|
+
code_verifier: pendingAuth.codeVerifier
|
|
501
|
+
});
|
|
502
|
+
const response = await fetch('https://oauth2.googleapis.com/token', {
|
|
503
|
+
method: 'POST',
|
|
504
|
+
headers: {
|
|
505
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
506
|
+
},
|
|
507
|
+
body: body.toString()
|
|
508
|
+
});
|
|
509
|
+
if (!response.ok) {
|
|
510
|
+
const errorText = await response.text();
|
|
511
|
+
throw new Error(`Token exchange failed: ${response.status} ${errorText}`);
|
|
512
|
+
}
|
|
513
|
+
const tokenResponse = await response.json();
|
|
514
|
+
// Fetch user email
|
|
515
|
+
const email = await this.fetchUserEmailFromToken(tokenResponse.access_token);
|
|
516
|
+
// Create cached token
|
|
517
|
+
const cachedToken = {
|
|
518
|
+
accessToken: tokenResponse.access_token,
|
|
519
|
+
refreshToken: tokenResponse.refresh_token,
|
|
520
|
+
expiresAt: tokenResponse.expires_in ? Date.now() + tokenResponse.expires_in * 1000 : undefined,
|
|
521
|
+
...tokenResponse.scope !== undefined && {
|
|
522
|
+
scope: tokenResponse.scope
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
// Store token
|
|
526
|
+
await setToken(tokenStore, {
|
|
527
|
+
accountId: email,
|
|
528
|
+
service
|
|
529
|
+
}, cachedToken);
|
|
530
|
+
// Add account and set as active
|
|
531
|
+
await addAccount(tokenStore, {
|
|
532
|
+
service,
|
|
533
|
+
accountId: email
|
|
534
|
+
});
|
|
535
|
+
await setActiveAccount(tokenStore, {
|
|
536
|
+
service,
|
|
537
|
+
accountId: email
|
|
538
|
+
});
|
|
539
|
+
// Store account metadata
|
|
540
|
+
await setAccountInfo(tokenStore, {
|
|
541
|
+
service,
|
|
542
|
+
accountId: email
|
|
543
|
+
}, {
|
|
544
|
+
email,
|
|
545
|
+
addedAt: new Date().toISOString()
|
|
546
|
+
});
|
|
547
|
+
// Clean up pending auth
|
|
548
|
+
await tokenStore.delete(pendingKey);
|
|
549
|
+
logger.info('OAuth callback completed', {
|
|
550
|
+
service,
|
|
551
|
+
email
|
|
552
|
+
});
|
|
553
|
+
return {
|
|
554
|
+
email,
|
|
555
|
+
token: cachedToken
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
521
559
|
* Create authentication middleware for MCP tools, resources, and prompts
|
|
522
560
|
*
|
|
523
561
|
* Returns position-aware middleware wrappers that enrich handlers with authentication context.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/providers/loopback-oauth.ts"],"sourcesContent":["/**\n * Loopback OAuth Implementation (RFC 8252)\n *\n * Implements OAuth 2.0 Authorization Code Flow with PKCE using loopback interface redirection.\n * Uses ephemeral local server with OS-assigned port (RFC 8252 Section 8.3).\n */\n\nimport { addAccount, generatePKCE, getActiveAccount, getErrorTemplate, getSuccessTemplate, getToken, listAccountIds, type OAuth2TokenStorageProvider, setAccountInfo, setActiveAccount, setToken } from '@mcp-z/oauth';\nimport { OAuth2Client } from 'google-auth-library';\nimport * as http from 'http';\nimport open from 'open';\nimport { type AuthContext, type AuthFlowDescriptor, AuthRequiredError, type CachedToken, type EnrichedExtra, type LoopbackOAuthConfig } from '../types.ts';\n\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * Loopback OAuth Client (RFC 8252 Section 7.3)\n *\n * Implements OAuth 2.0 Authorization Code Flow with PKCE for native applications\n * using loopback interface redirection. Manages ephemeral OAuth flows and token persistence\n * with Keyv for key-based token storage using compound keys.\n *\n * Token key format: {accountId}:{service}:token (e.g., \"user@example.com:gmail:token\")\n */\nexport class LoopbackOAuthProvider implements OAuth2TokenStorageProvider {\n private config: LoopbackOAuthConfig;\n\n constructor(config: LoopbackOAuthConfig) {\n this.config = config;\n }\n\n /**\n * Get access token from Keyv using compound key\n *\n * @param accountId - Account identifier (email address). Required for loopback OAuth.\n * @returns Access token for API requests\n */\n async getAccessToken(accountId?: string): Promise<string> {\n const { logger, service, tokenStore } = this.config;\n\n // Use active account if no accountId specified\n const effectiveAccountId = accountId ?? (await getActiveAccount(tokenStore, { service }));\n\n // If we have an accountId, try to use existing token\n if (effectiveAccountId) {\n logger.debug('Getting access token', { service, accountId: effectiveAccountId });\n\n // Check Keyv for token using new key format\n const storedToken = await getToken<CachedToken>(tokenStore, { accountId: effectiveAccountId, service });\n\n if (storedToken && this.isTokenValid(storedToken)) {\n logger.debug('Using stored access token', { accountId: effectiveAccountId });\n return storedToken.accessToken;\n }\n\n // If stored token expired but has refresh token, try refresh\n if (storedToken?.refreshToken) {\n try {\n logger.info('Refreshing expired access token', { accountId: effectiveAccountId });\n const refreshedToken = await this.refreshAccessToken(storedToken.refreshToken);\n await setToken(tokenStore, { accountId: effectiveAccountId, service }, refreshedToken);\n return refreshedToken.accessToken;\n } catch (error) {\n logger.info('Token refresh failed, starting new OAuth flow', {\n accountId: effectiveAccountId,\n error: error instanceof Error ? error.message : String(error),\n });\n // Fall through to new OAuth flow\n }\n }\n }\n\n // No valid token or no account - check if we can start OAuth flow\n const { headless } = this.config;\n if (headless) {\n // In headless mode (production), cannot start OAuth flow\n // Throw AuthRequiredError with auth_url descriptor for MCP tool response\n const { clientId, scope } = this.config;\n\n // Incremental OAuth detection: Check if other accounts exist\n const existingAccounts = await this.getExistingAccounts();\n const hasOtherAccounts = effectiveAccountId ? existingAccounts.length > 0 && !existingAccounts.includes(effectiveAccountId) : existingAccounts.length > 0;\n\n // Build informational OAuth URL for headless mode\n // Note: No redirect_uri included - user must use account-add tool which starts proper ephemeral server\n const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', scope);\n authUrl.searchParams.set('access_type', 'offline');\n authUrl.searchParams.set('prompt', 'consent');\n\n let hint: string;\n if (hasOtherAccounts) {\n hint = `Existing ${service} accounts found. Use account-list to view, account-switch to change account, or account-add to add new account`;\n } else if (effectiveAccountId) {\n hint = `Use account-add to authenticate ${effectiveAccountId}`;\n } else {\n hint = 'Use account-add to authenticate interactively';\n }\n\n const baseDescriptor = {\n kind: 'auth_url' as const,\n provider: 'google',\n url: authUrl.toString(),\n hint,\n };\n\n const descriptor: AuthFlowDescriptor & { accountId?: string } = effectiveAccountId ? { ...baseDescriptor, accountId: effectiveAccountId } : baseDescriptor;\n\n throw new AuthRequiredError(descriptor);\n }\n\n // Interactive mode - start ephemeral OAuth flow\n logger.info('Starting ephemeral OAuth flow', { service, headless });\n const { token, email } = await this.performEphemeralOAuthFlow();\n\n // Store token with email as accountId\n await setToken(tokenStore, { accountId: email, service }, token);\n\n // Register account in account management system\n await addAccount(tokenStore, { service, accountId: email });\n\n // Set as active account so subsequent getAccessToken() calls find it\n await setActiveAccount(tokenStore, { service, accountId: email });\n\n // Store account metadata (email, added timestamp)\n await setAccountInfo(\n tokenStore,\n { service, accountId: email },\n {\n email,\n addedAt: new Date().toISOString(),\n }\n );\n\n logger.info('OAuth flow completed', { service, accountId: email });\n\n return token.accessToken;\n }\n\n /**\n * Convert to googleapis-compatible OAuth2Client\n *\n * @param accountId - Account identifier for multi-account support (e.g., 'user@example.com')\n * @returns OAuth2Client configured for the specified account\n */\n toAuth(accountId?: string): OAuth2Client {\n const { clientId, clientSecret } = this.config;\n const client = new OAuth2Client({\n clientId,\n ...(clientSecret && { clientSecret }),\n });\n\n // @ts-expect-error - Override protected method to inject fresh token\n client.getRequestMetadataAsync = async (_url?: string) => {\n // Get token from FileAuthAdapter (not from client to avoid recursion)\n const token = await this.getAccessToken(accountId);\n\n // Update client credentials for googleapis compatibility\n client.credentials = {\n access_token: token,\n token_type: 'Bearer',\n };\n\n // Return headers as Map (required by authclient.js addUserProjectAndAuthHeaders)\n const headers = new Map<string, string>();\n headers.set('authorization', `Bearer ${token}`);\n return { headers };\n };\n\n return client;\n }\n\n /**\n * Authenticate new account with OAuth flow\n * Triggers account selection, stores token, registers account\n *\n * @returns Email address of newly authenticated account\n * @throws Error in headless mode (cannot open browser for OAuth)\n */\n async authenticateNewAccount(): Promise<string> {\n const { logger, headless, service, tokenStore } = this.config;\n\n if (headless) {\n throw new Error('Cannot authenticate new account in headless mode - interactive OAuth required');\n }\n\n logger.info('Starting new account authentication', { service });\n\n // Trigger OAuth with account selection\n const { token, email } = await this.performEphemeralOAuthFlow();\n\n // Store token\n await setToken(tokenStore, { accountId: email, service }, token);\n\n // Register account\n await addAccount(tokenStore, { service, accountId: email });\n\n // Set as active account\n await setActiveAccount(tokenStore, { service, accountId: email });\n\n // Store account metadata\n await setAccountInfo(\n tokenStore,\n { service, accountId: email },\n {\n email,\n addedAt: new Date().toISOString(),\n }\n );\n\n logger.info('New account authenticated', { service, email });\n return email;\n }\n\n /**\n * Get user email from Google's userinfo endpoint (pure query)\n * Used to query email for existing authenticated account\n *\n * @param accountId - Account identifier to get email for\n * @returns User's email address\n */\n async getUserEmail(accountId?: string): Promise<string> {\n // Get token for existing account\n const token = await this.getAccessToken(accountId);\n\n // Fetch email from Google userinfo\n const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to get user info: ${response.status} ${await response.text()}`);\n }\n\n const userInfo = (await response.json()) as { email: string };\n return userInfo.email;\n }\n\n /**\n * Check for existing accounts in token storage (incremental OAuth detection)\n *\n * Uses key-utils helper for forward compatibility with key format changes.\n *\n * @returns Array of account IDs that have tokens for this service\n */\n private async getExistingAccounts(): Promise<string[]> {\n const { service, tokenStore } = this.config;\n return listAccountIds(tokenStore, service);\n }\n\n private isTokenValid(token: CachedToken): boolean {\n if (!token.expiresAt) return true; // No expiry = assume valid\n return Date.now() < token.expiresAt - 60000; // 1 minute buffer\n }\n\n /**\n * Fetch user email from Google OAuth2 userinfo endpoint\n * Called during OAuth flow to get email for accountId\n *\n * @param accessToken - Fresh access token from OAuth exchange\n * @returns User's email address\n */\n private async fetchUserEmailFromToken(accessToken: string): Promise<string> {\n const { logger } = this.config;\n\n const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to fetch user email: HTTP ${response.status} - ${errorText}`);\n }\n\n const userInfo = (await response.json()) as { email: string };\n const email = userInfo.email;\n\n logger.debug('Fetched user email from Google userinfo API', { email });\n return email;\n }\n\n private async performEphemeralOAuthFlow(): Promise<{ token: CachedToken; email: string }> {\n const { clientId, scope, headless, logger, redirectUri: configRedirectUri } = this.config;\n\n // Parse redirectUri if provided to extract host, protocol, port, and path\n let targetHost = 'localhost'; // Default: localhost (match registered redirect URI)\n let targetPort = 0; // Default: OS-assigned ephemeral port\n let targetProtocol = 'http:'; // Default: http\n let callbackPath = '/callback'; // Default callback path\n let useConfiguredUri = false;\n\n if (configRedirectUri) {\n try {\n const parsed = new URL(configRedirectUri);\n\n // Use configured redirect URI as-is for production deployments\n targetHost = parsed.hostname;\n targetProtocol = parsed.protocol;\n\n // Extract port from URL (use default ports if not specified)\n if (parsed.port) {\n targetPort = Number.parseInt(parsed.port, 10);\n } else {\n targetPort = parsed.protocol === 'https:' ? 443 : 80;\n }\n\n // Extract path (default to /callback if URL has no path or just '/')\n if (parsed.pathname && parsed.pathname !== '/') {\n callbackPath = parsed.pathname;\n }\n\n useConfiguredUri = true;\n\n logger.debug('Using configured redirect URI', {\n host: targetHost,\n protocol: targetProtocol,\n port: targetPort,\n path: callbackPath,\n redirectUri: configRedirectUri,\n });\n } catch (error) {\n logger.warn('Failed to parse redirectUri, using ephemeral defaults', {\n redirectUri: configRedirectUri,\n error: error instanceof Error ? error.message : String(error),\n });\n // Continue with defaults (127.0.0.1, port 0, http, /callback)\n }\n }\n\n return new Promise((resolve, reject) => {\n // Generate PKCE challenge\n const { verifier: codeVerifier, challenge: codeChallenge } = generatePKCE();\n\n let server: http.Server | null = null;\n let serverPort: number;\n let finalRedirectUri: string; // Will be set in server.listen callback\n\n // Create ephemeral server with OS-assigned port (RFC 8252)\n server = http.createServer(async (req, res) => {\n if (!req.url) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('Invalid request'));\n server?.close();\n reject(new Error('Invalid request: missing URL'));\n return;\n }\n const url = new URL(req.url, `http://127.0.0.1:${serverPort}`);\n\n if (url.pathname === callbackPath) {\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n\n if (error) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate(error));\n server?.close();\n reject(new Error(`OAuth error: ${error}`));\n return;\n }\n\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('No authorization code received'));\n server?.close();\n reject(new Error('No authorization code received'));\n return;\n }\n\n try {\n // Exchange code for token (must use same redirect_uri as in authorization request)\n const tokenResponse = await this.exchangeCodeForToken(code, codeVerifier, finalRedirectUri);\n\n // Build cached token\n const cachedToken: CachedToken = {\n accessToken: tokenResponse.access_token,\n ...(tokenResponse.refresh_token !== undefined && { refreshToken: tokenResponse.refresh_token }),\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n\n // Fetch user email immediately using the new access token\n const email = await this.fetchUserEmailFromToken(tokenResponse.access_token);\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessTemplate());\n server?.close();\n resolve({ token: cachedToken, email });\n } catch (exchangeError) {\n logger.error('Token exchange failed', { error: exchangeError instanceof Error ? exchangeError.message : String(exchangeError) });\n res.writeHead(500, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('Token exchange failed'));\n server?.close();\n reject(exchangeError);\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n });\n\n // Listen on targetPort (0 for OS assignment, or custom port from redirectUri)\n server.listen(targetPort, targetHost, () => {\n const address = server?.address();\n if (!address || typeof address === 'string') {\n server?.close();\n reject(new Error('Failed to start ephemeral server'));\n return;\n }\n\n serverPort = address.port;\n\n // Construct final redirect URI\n if (useConfiguredUri && configRedirectUri) {\n // Use configured redirect URI as-is for production\n finalRedirectUri = configRedirectUri;\n } else {\n // Construct ephemeral redirect URI with actual server port\n finalRedirectUri = `${targetProtocol}//${targetHost}:${serverPort}${callbackPath}`;\n }\n\n // Build auth URL\n const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', finalRedirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', scope);\n authUrl.searchParams.set('access_type', 'offline');\n authUrl.searchParams.set('prompt', 'consent');\n authUrl.searchParams.set('code_challenge', codeChallenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n\n logger.info('Ephemeral OAuth server started', { port: serverPort, headless });\n\n if (headless) {\n // Headless mode: Print auth URL to stderr (stdout is MCP protocol)\n console.error('\\nš OAuth Authorization Required');\n console.error('š Please visit this URL in your browser:\\n');\n console.error(` ${authUrl.toString()}\\n`);\n console.error('ā³ Waiting for authorization...\\n');\n } else {\n // Interactive mode: Open browser automatically\n logger.info('Opening browser for OAuth authorization');\n open(authUrl.toString()).catch((error: Error) => {\n logger.info('Failed to open browser automatically', { error: error.message });\n console.error('\\nš OAuth Authorization Required');\n console.error(` ${authUrl.toString()}\\n`);\n });\n }\n });\n\n // Timeout after 5 minutes\n setTimeout(\n () => {\n if (server) {\n server.close();\n reject(new Error('OAuth flow timed out after 5 minutes'));\n }\n },\n 5 * 60 * 1000\n );\n });\n }\n\n private async exchangeCodeForToken(code: string, codeVerifier: string, redirectUri: string): Promise<TokenResponse> {\n const { clientId, clientSecret } = this.config;\n\n const tokenUrl = 'https://oauth2.googleapis.com/token';\n const params: Record<string, string> = {\n code,\n client_id: clientId,\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n code_verifier: codeVerifier,\n };\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n const body = new URLSearchParams(params);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorText}`);\n }\n\n return (await response.json()) as TokenResponse;\n }\n\n private async refreshAccessToken(refreshToken: string): Promise<CachedToken> {\n const { clientId, clientSecret } = this.config;\n\n const tokenUrl = 'https://oauth2.googleapis.com/token';\n const params: Record<string, string> = {\n refresh_token: refreshToken,\n client_id: clientId,\n grant_type: 'refresh_token',\n };\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n const body = new URLSearchParams(params);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorText}`);\n }\n\n const tokenResponse = (await response.json()) as TokenResponse;\n\n return {\n accessToken: tokenResponse.access_token,\n refreshToken: refreshToken, // Keep original refresh token\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n }\n\n /**\n * Create authentication middleware for MCP tools, resources, and prompts\n *\n * Returns position-aware middleware wrappers that enrich handlers with authentication context.\n * The middleware handles token retrieval, refresh, and AuthRequiredError automatically.\n *\n * Single-user middleware for desktop/CLI apps where ONE user runs the entire process:\n * - Desktop applications (Claude Desktop)\n * - CLI tools (Gmail CLI)\n * - Personal automation scripts\n *\n * All requests use token lookups based on the active account or account override.\n *\n * @returns Object with withToolAuth, withResourceAuth, withPromptAuth methods\n *\n * @example\n * ```typescript\n * const loopback = new LoopbackOAuthProvider({ service: 'gmail', ... });\n * const authMiddleware = loopback.authMiddleware();\n * const tools = toolFactories.map(f => f()).map(authMiddleware.withToolAuth);\n * const resources = resourceFactories.map(f => f()).map(authMiddleware.withResourceAuth);\n * const prompts = promptFactories.map(f => f()).map(authMiddleware.withPromptAuth);\n * ```\n */\n authMiddleware() {\n const { service, tokenStore, logger } = this.config;\n\n // Shared wrapper logic - extracts extra parameter from specified position\n // Generic T captures the actual module type; handler is cast from unknown to callable\n const wrapAtPosition = <T extends { name: string; handler: unknown; [key: string]: unknown }>(module: T, extraPosition: number): T => {\n const operation = module.name;\n const originalHandler = module.handler as (...args: unknown[]) => Promise<unknown>;\n\n const wrappedHandler = async (...allArgs: unknown[]) => {\n // Extract extra from the correct position\n const extra = allArgs[extraPosition] as EnrichedExtra;\n\n try {\n // Check for backchannel override via _meta.accountId\n let accountId: string | undefined;\n try {\n accountId = (extra as { _meta?: { accountId?: string } })._meta?.accountId ?? (await getActiveAccount(tokenStore, { service }));\n } catch (error) {\n if (error instanceof Error && ((error as { code?: string }).code === 'REQUIRES_AUTHENTICATION' || error.name === 'AccountManagerError')) {\n accountId = undefined;\n } else {\n throw error;\n }\n }\n\n // Eagerly validate token exists or trigger OAuth flow\n await this.getAccessToken(accountId);\n\n // After OAuth flow completes, get the actual accountId (email) that was set\n const effectiveAccountId = accountId ?? (await getActiveAccount(tokenStore, { service }));\n if (!effectiveAccountId) {\n throw new Error(`No account found after OAuth flow for service ${service}`);\n }\n\n const auth = this.toAuth(effectiveAccountId);\n\n // Inject authContext and logger into extra\n (extra as { authContext?: AuthContext }).authContext = {\n auth,\n accountId: effectiveAccountId,\n };\n (extra as { logger?: unknown }).logger = logger;\n\n // Call original handler with all args\n return await originalHandler(...allArgs);\n } catch (error) {\n if (error instanceof AuthRequiredError) {\n logger.info('Authentication required', {\n service,\n tool: operation,\n descriptor: error.descriptor,\n });\n // Return auth_required response wrapped in { result } to match tool outputSchema pattern\n // Tools define outputSchema: z.object({ result: discriminatedUnion(...) }) where auth_required is a branch\n const authRequiredResponse = {\n type: 'auth_required' as const,\n provider: service,\n message: `Authentication required for ${operation}. Please authenticate with ${service}.`,\n url: error.descriptor.kind === 'auth_url' ? error.descriptor.url : undefined,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ result: authRequiredResponse }),\n },\n ],\n structuredContent: { result: authRequiredResponse },\n };\n }\n throw error;\n }\n };\n\n return {\n ...module,\n handler: wrappedHandler,\n } as T;\n };\n\n return {\n withToolAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 1),\n withResourceAuth: <T extends { name: string; template?: unknown; config?: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 2),\n withPromptAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 0),\n };\n }\n}\n\n/**\n * Create a loopback OAuth client for Google services\n * Works for both stdio and HTTP transports\n */\nexport function createGoogleFileAuth(config: LoopbackOAuthConfig): OAuth2TokenStorageProvider {\n return new LoopbackOAuthProvider(config);\n}\n"],"names":["addAccount","generatePKCE","getActiveAccount","getErrorTemplate","getSuccessTemplate","getToken","listAccountIds","setAccountInfo","setActiveAccount","setToken","OAuth2Client","http","open","AuthRequiredError","LoopbackOAuthProvider","getAccessToken","accountId","logger","service","tokenStore","config","effectiveAccountId","debug","storedToken","isTokenValid","accessToken","refreshToken","info","refreshedToken","refreshAccessToken","error","Error","message","String","headless","clientId","scope","existingAccounts","getExistingAccounts","hasOtherAccounts","length","includes","authUrl","URL","searchParams","set","hint","baseDescriptor","kind","provider","url","toString","descriptor","token","email","performEphemeralOAuthFlow","addedAt","Date","toISOString","toAuth","clientSecret","client","getRequestMetadataAsync","_url","credentials","access_token","token_type","headers","Map","authenticateNewAccount","getUserEmail","response","fetch","Authorization","ok","status","text","userInfo","json","expiresAt","now","fetchUserEmailFromToken","errorText","redirectUri","configRedirectUri","targetHost","targetPort","targetProtocol","callbackPath","useConfiguredUri","parsed","hostname","protocol","port","Number","parseInt","pathname","host","path","warn","Promise","resolve","reject","verifier","codeVerifier","challenge","codeChallenge","server","serverPort","finalRedirectUri","createServer","req","res","writeHead","end","close","code","get","tokenResponse","exchangeCodeForToken","cachedToken","refresh_token","undefined","expires_in","exchangeError","listen","address","console","catch","setTimeout","tokenUrl","params","client_id","redirect_uri","grant_type","code_verifier","client_secret","body","URLSearchParams","method","authMiddleware","wrapAtPosition","module","extraPosition","operation","name","originalHandler","handler","wrappedHandler","allArgs","extra","_meta","auth","authContext","tool","authRequiredResponse","type","content","JSON","stringify","result","structuredContent","withToolAuth","withResourceAuth","withPromptAuth","createGoogleFileAuth"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-google/src/providers/loopback-oauth.ts"],"sourcesContent":["/**\n * Loopback OAuth Implementation (RFC 8252)\n *\n * Implements OAuth 2.0 Authorization Code Flow with PKCE using loopback interface redirection.\n * Uses ephemeral local server with OS-assigned port (RFC 8252 Section 8.3).\n */\n\nimport { addAccount, generatePKCE, getActiveAccount, getErrorTemplate, getSuccessTemplate, getToken, type OAuth2TokenStorageProvider, setAccountInfo, setActiveAccount, setToken } from '@mcp-z/oauth';\nimport { randomUUID } from 'crypto';\nimport { OAuth2Client } from 'google-auth-library';\nimport * as http from 'http';\nimport open from 'open';\nimport { type AuthContext, AuthRequiredError, type CachedToken, type EnrichedExtra, type LoopbackOAuthConfig } from '../types.ts';\n\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * Loopback OAuth Client (RFC 8252 Section 7.3)\n *\n * Implements OAuth 2.0 Authorization Code Flow with PKCE for native applications\n * using loopback interface redirection. Manages ephemeral OAuth flows and token persistence\n * with Keyv for key-based token storage using compound keys.\n *\n * Token key format: {accountId}:{service}:token (e.g., \"user@example.com:gmail:token\")\n */\nexport class LoopbackOAuthProvider implements OAuth2TokenStorageProvider {\n private config: LoopbackOAuthConfig;\n\n constructor(config: LoopbackOAuthConfig) {\n this.config = config;\n }\n\n /**\n * Get access token from Keyv using compound key\n *\n * @param accountId - Account identifier (email address). Required for loopback OAuth.\n * @returns Access token for API requests\n */\n async getAccessToken(accountId?: string): Promise<string> {\n const { logger, service, tokenStore } = this.config;\n\n // Use active account if no accountId specified\n const effectiveAccountId = accountId ?? (await getActiveAccount(tokenStore, { service }));\n\n // If we have an accountId, try to use existing token\n if (effectiveAccountId) {\n logger.debug('Getting access token', { service, accountId: effectiveAccountId });\n\n // Check Keyv for token using new key format\n const storedToken = await getToken<CachedToken>(tokenStore, { accountId: effectiveAccountId, service });\n\n if (storedToken && this.isTokenValid(storedToken)) {\n logger.debug('Using stored access token', { accountId: effectiveAccountId });\n return storedToken.accessToken;\n }\n\n // If stored token expired but has refresh token, try refresh\n if (storedToken?.refreshToken) {\n try {\n logger.info('Refreshing expired access token', { accountId: effectiveAccountId });\n const refreshedToken = await this.refreshAccessToken(storedToken.refreshToken);\n await setToken(tokenStore, { accountId: effectiveAccountId, service }, refreshedToken);\n return refreshedToken.accessToken;\n } catch (error) {\n logger.info('Token refresh failed, starting new OAuth flow', {\n accountId: effectiveAccountId,\n error: error instanceof Error ? error.message : String(error),\n });\n // Fall through to new OAuth flow\n }\n }\n }\n\n // No valid token or no account - need OAuth authentication\n const { clientId, scope, redirectUri } = this.config;\n\n if (redirectUri) {\n // Persistent callback mode (cloud deployment with configured redirect_uri)\n const { verifier: codeVerifier, challenge: codeChallenge } = generatePKCE();\n const stateId = randomUUID();\n\n // Store PKCE verifier for callback (5 minute TTL)\n await tokenStore.set(`${service}:pending:${stateId}`, { codeVerifier, createdAt: Date.now() }, 5 * 60 * 1000);\n\n // Build auth URL with configured redirect_uri\n const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', scope);\n authUrl.searchParams.set('access_type', 'offline');\n authUrl.searchParams.set('code_challenge', codeChallenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n authUrl.searchParams.set('state', stateId);\n authUrl.searchParams.set('prompt', 'consent');\n\n logger.info('OAuth required - persistent callback mode', { service, redirectUri });\n throw new AuthRequiredError({\n kind: 'auth_url',\n provider: service,\n url: authUrl.toString(),\n });\n }\n // Ephemeral callback mode (local development)\n logger.info('Starting ephemeral OAuth flow', { service, headless: this.config.headless });\n const { token, email } = await this.performEphemeralOAuthFlow();\n\n await setToken(tokenStore, { accountId: email, service }, token);\n await addAccount(tokenStore, { service, accountId: email });\n await setActiveAccount(tokenStore, { service, accountId: email });\n await setAccountInfo(tokenStore, { service, accountId: email }, { email, addedAt: new Date().toISOString() });\n\n logger.info('OAuth flow completed', { service, accountId: email });\n return token.accessToken;\n }\n\n /**\n * Convert to googleapis-compatible OAuth2Client\n *\n * @param accountId - Account identifier for multi-account support (e.g., 'user@example.com')\n * @returns OAuth2Client configured for the specified account\n */\n toAuth(accountId?: string): OAuth2Client {\n const { clientId, clientSecret } = this.config;\n const client = new OAuth2Client({\n clientId,\n ...(clientSecret && { clientSecret }),\n });\n\n // @ts-expect-error - Override protected method to inject fresh token\n client.getRequestMetadataAsync = async (_url?: string) => {\n // Get token from FileAuthAdapter (not from client to avoid recursion)\n const token = await this.getAccessToken(accountId);\n\n // Update client credentials for googleapis compatibility\n client.credentials = {\n access_token: token,\n token_type: 'Bearer',\n };\n\n // Return headers as Map (required by authclient.js addUserProjectAndAuthHeaders)\n const headers = new Map<string, string>();\n headers.set('authorization', `Bearer ${token}`);\n return { headers };\n };\n\n return client;\n }\n\n /**\n * Get user email from Google's userinfo endpoint (pure query)\n * Used to query email for existing authenticated account\n *\n * @param accountId - Account identifier to get email for\n * @returns User's email address\n */\n async getUserEmail(accountId?: string): Promise<string> {\n // Get token for existing account\n const token = await this.getAccessToken(accountId);\n\n // Fetch email from Google userinfo\n const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to get user info: ${response.status} ${await response.text()}`);\n }\n\n const userInfo = (await response.json()) as { email: string };\n return userInfo.email;\n }\n\n private isTokenValid(token: CachedToken): boolean {\n if (!token.expiresAt) return true; // No expiry = assume valid\n return Date.now() < token.expiresAt - 60000; // 1 minute buffer\n }\n\n /**\n * Fetch user email from Google OAuth2 userinfo endpoint\n * Called during OAuth flow to get email for accountId\n *\n * @param accessToken - Fresh access token from OAuth exchange\n * @returns User's email address\n */\n private async fetchUserEmailFromToken(accessToken: string): Promise<string> {\n const { logger } = this.config;\n\n const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to fetch user email: HTTP ${response.status} - ${errorText}`);\n }\n\n const userInfo = (await response.json()) as { email: string };\n const email = userInfo.email;\n\n logger.debug('Fetched user email from Google userinfo API', { email });\n return email;\n }\n\n private async performEphemeralOAuthFlow(): Promise<{ token: CachedToken; email: string }> {\n const { clientId, scope, headless, logger, redirectUri: configRedirectUri } = this.config;\n\n // Server listen configuration (where ephemeral server binds)\n let listenHost = 'localhost'; // Default: localhost for ephemeral loopback\n let listenPort = 0; // Default: OS-assigned ephemeral port\n\n // Redirect URI configuration (what goes in auth URL and token exchange)\n let callbackPath = '/callback'; // Default callback path\n let useConfiguredUri = false;\n\n if (configRedirectUri) {\n try {\n const parsed = new URL(configRedirectUri);\n const isLoopback = parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\n\n if (isLoopback) {\n // Local development: Listen on specific loopback address/port\n listenHost = parsed.hostname;\n listenPort = parsed.port ? Number.parseInt(parsed.port, 10) : 0;\n } else {\n // Cloud deployment: Listen on 0.0.0.0 with PORT from environment\n // The redirectUri is the PUBLIC URL (e.g., https://example.com/oauth/callback)\n // The server listens on 0.0.0.0:PORT and the load balancer routes to it\n listenHost = '0.0.0.0';\n const envPort = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : undefined;\n listenPort = envPort && Number.isFinite(envPort) ? envPort : 8080;\n }\n\n // Extract callback path from URL\n if (parsed.pathname && parsed.pathname !== '/') {\n callbackPath = parsed.pathname;\n }\n\n useConfiguredUri = true;\n\n logger.debug('Using configured redirect URI', {\n listenHost,\n listenPort,\n callbackPath,\n redirectUri: configRedirectUri,\n isLoopback,\n });\n } catch (error) {\n logger.warn('Failed to parse redirectUri, using ephemeral defaults', {\n redirectUri: configRedirectUri,\n error: error instanceof Error ? error.message : String(error),\n });\n // Continue with defaults (localhost, port 0, http, /callback)\n }\n }\n\n return new Promise((resolve, reject) => {\n // Generate PKCE challenge\n const { verifier: codeVerifier, challenge: codeChallenge } = generatePKCE();\n\n let server: http.Server | null = null;\n let serverPort: number;\n let finalRedirectUri: string; // Will be set in server.listen callback\n\n // Create ephemeral server with OS-assigned port (RFC 8252)\n server = http.createServer(async (req, res) => {\n if (!req.url) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('Invalid request'));\n server?.close();\n reject(new Error('Invalid request: missing URL'));\n return;\n }\n const url = new URL(req.url, `http://127.0.0.1:${serverPort}`);\n\n if (url.pathname === callbackPath) {\n const code = url.searchParams.get('code');\n const error = url.searchParams.get('error');\n\n if (error) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate(error));\n server?.close();\n reject(new Error(`OAuth error: ${error}`));\n return;\n }\n\n if (!code) {\n res.writeHead(400, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('No authorization code received'));\n server?.close();\n reject(new Error('No authorization code received'));\n return;\n }\n\n try {\n // Exchange code for token (must use same redirect_uri as in authorization request)\n const tokenResponse = await this.exchangeCodeForToken(code, codeVerifier, finalRedirectUri);\n\n // Build cached token\n const cachedToken: CachedToken = {\n accessToken: tokenResponse.access_token,\n ...(tokenResponse.refresh_token !== undefined && { refreshToken: tokenResponse.refresh_token }),\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n\n // Fetch user email immediately using the new access token\n const email = await this.fetchUserEmailFromToken(tokenResponse.access_token);\n\n res.writeHead(200, { 'Content-Type': 'text/html' });\n res.end(getSuccessTemplate());\n server?.close();\n resolve({ token: cachedToken, email });\n } catch (exchangeError) {\n logger.error('Token exchange failed', { error: exchangeError instanceof Error ? exchangeError.message : String(exchangeError) });\n res.writeHead(500, { 'Content-Type': 'text/html' });\n res.end(getErrorTemplate('Token exchange failed'));\n server?.close();\n reject(exchangeError);\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n });\n\n // Listen on configured host/port\n server.listen(listenPort, listenHost, () => {\n const address = server?.address();\n if (!address || typeof address === 'string') {\n server?.close();\n reject(new Error('Failed to start ephemeral server'));\n return;\n }\n\n serverPort = address.port;\n\n // Construct final redirect URI\n if (useConfiguredUri && configRedirectUri) {\n // Use configured redirect URI as-is (public URL for cloud, or specific local URL)\n finalRedirectUri = configRedirectUri;\n } else {\n // Construct ephemeral redirect URI with actual server port (default local behavior)\n finalRedirectUri = `http://localhost:${serverPort}${callbackPath}`;\n }\n\n // Build auth URL\n const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n authUrl.searchParams.set('client_id', clientId);\n authUrl.searchParams.set('redirect_uri', finalRedirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', scope);\n authUrl.searchParams.set('access_type', 'offline');\n authUrl.searchParams.set('prompt', 'consent');\n authUrl.searchParams.set('code_challenge', codeChallenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n\n logger.info('Ephemeral OAuth server started', { port: serverPort, headless });\n\n if (headless) {\n // Headless mode: Print auth URL to stderr (stdout is MCP protocol)\n console.error('\\nš OAuth Authorization Required');\n console.error('š Please visit this URL in your browser:\\n');\n console.error(` ${authUrl.toString()}\\n`);\n console.error('ā³ Waiting for authorization...\\n');\n } else {\n // Interactive mode: Open browser automatically\n logger.info('Opening browser for OAuth authorization');\n open(authUrl.toString()).catch((error: Error) => {\n logger.info('Failed to open browser automatically', { error: error.message });\n console.error('\\nš OAuth Authorization Required');\n console.error(` ${authUrl.toString()}\\n`);\n });\n }\n });\n\n // Timeout after 5 minutes\n setTimeout(\n () => {\n if (server) {\n server.close();\n reject(new Error('OAuth flow timed out after 5 minutes'));\n }\n },\n 5 * 60 * 1000\n );\n });\n }\n\n private async exchangeCodeForToken(code: string, codeVerifier: string, redirectUri: string): Promise<TokenResponse> {\n const { clientId, clientSecret } = this.config;\n\n const tokenUrl = 'https://oauth2.googleapis.com/token';\n const params: Record<string, string> = {\n code,\n client_id: clientId,\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n code_verifier: codeVerifier,\n };\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n const body = new URLSearchParams(params);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorText}`);\n }\n\n return (await response.json()) as TokenResponse;\n }\n\n private async refreshAccessToken(refreshToken: string): Promise<CachedToken> {\n const { clientId, clientSecret } = this.config;\n\n const tokenUrl = 'https://oauth2.googleapis.com/token';\n const params: Record<string, string> = {\n refresh_token: refreshToken,\n client_id: clientId,\n grant_type: 'refresh_token',\n };\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n const body = new URLSearchParams(params);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorText}`);\n }\n\n const tokenResponse = (await response.json()) as TokenResponse;\n\n return {\n accessToken: tokenResponse.access_token,\n refreshToken: refreshToken, // Keep original refresh token\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n }\n\n /**\n * Handle OAuth callback from persistent endpoint.\n * Used by HTTP servers with configured redirectUri.\n *\n * @param params - OAuth callback parameters\n * @returns Email and cached token\n */\n async handleOAuthCallback(params: { code: string; state?: string }): Promise<{ email: string; token: CachedToken }> {\n const { code, state } = params;\n const { logger, service, tokenStore, clientId, clientSecret, redirectUri } = this.config;\n\n if (!state) {\n throw new Error('Missing state parameter in OAuth callback');\n }\n\n if (!redirectUri) {\n throw new Error('handleOAuthCallback requires configured redirectUri');\n }\n\n // Load pending auth (includes PKCE verifier)\n const pendingKey = `${service}:pending:${state}`;\n const pendingAuth = await tokenStore.get<{ codeVerifier: string; createdAt: number }>(pendingKey);\n\n if (!pendingAuth) {\n throw new Error('Invalid or expired OAuth state. Please try again.');\n }\n\n // Check TTL (5 minutes)\n if (Date.now() - pendingAuth.createdAt > 5 * 60 * 1000) {\n await tokenStore.delete(pendingKey);\n throw new Error('OAuth state expired. Please try again.');\n }\n\n logger.info('Processing OAuth callback', { service, state });\n\n // Exchange code for token\n const body = new URLSearchParams({\n code,\n client_id: clientId,\n ...(clientSecret && { client_secret: clientSecret }),\n redirect_uri: redirectUri,\n grant_type: 'authorization_code',\n code_verifier: pendingAuth.codeVerifier,\n });\n\n const response = await fetch('https://oauth2.googleapis.com/token', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token exchange failed: ${response.status} ${errorText}`);\n }\n\n const tokenResponse = (await response.json()) as TokenResponse;\n\n // Fetch user email\n const email = await this.fetchUserEmailFromToken(tokenResponse.access_token);\n\n // Create cached token\n const cachedToken: CachedToken = {\n accessToken: tokenResponse.access_token,\n refreshToken: tokenResponse.refresh_token,\n expiresAt: tokenResponse.expires_in ? Date.now() + tokenResponse.expires_in * 1000 : undefined,\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n\n // Store token\n await setToken(tokenStore, { accountId: email, service }, cachedToken);\n\n // Add account and set as active\n await addAccount(tokenStore, { service, accountId: email });\n await setActiveAccount(tokenStore, { service, accountId: email });\n\n // Store account metadata\n await setAccountInfo(\n tokenStore,\n { service, accountId: email },\n {\n email,\n addedAt: new Date().toISOString(),\n }\n );\n\n // Clean up pending auth\n await tokenStore.delete(pendingKey);\n\n logger.info('OAuth callback completed', { service, email });\n\n return { email, token: cachedToken };\n }\n\n /**\n * Create authentication middleware for MCP tools, resources, and prompts\n *\n * Returns position-aware middleware wrappers that enrich handlers with authentication context.\n * The middleware handles token retrieval, refresh, and AuthRequiredError automatically.\n *\n * Single-user middleware for desktop/CLI apps where ONE user runs the entire process:\n * - Desktop applications (Claude Desktop)\n * - CLI tools (Gmail CLI)\n * - Personal automation scripts\n *\n * All requests use token lookups based on the active account or account override.\n *\n * @returns Object with withToolAuth, withResourceAuth, withPromptAuth methods\n *\n * @example\n * ```typescript\n * const loopback = new LoopbackOAuthProvider({ service: 'gmail', ... });\n * const authMiddleware = loopback.authMiddleware();\n * const tools = toolFactories.map(f => f()).map(authMiddleware.withToolAuth);\n * const resources = resourceFactories.map(f => f()).map(authMiddleware.withResourceAuth);\n * const prompts = promptFactories.map(f => f()).map(authMiddleware.withPromptAuth);\n * ```\n */\n authMiddleware() {\n const { service, tokenStore, logger } = this.config;\n\n // Shared wrapper logic - extracts extra parameter from specified position\n // Generic T captures the actual module type; handler is cast from unknown to callable\n const wrapAtPosition = <T extends { name: string; handler: unknown; [key: string]: unknown }>(module: T, extraPosition: number): T => {\n const operation = module.name;\n const originalHandler = module.handler as (...args: unknown[]) => Promise<unknown>;\n\n const wrappedHandler = async (...allArgs: unknown[]) => {\n // Extract extra from the correct position\n const extra = allArgs[extraPosition] as EnrichedExtra;\n\n try {\n // Check for backchannel override via _meta.accountId\n let accountId: string | undefined;\n try {\n accountId = (extra as { _meta?: { accountId?: string } })._meta?.accountId ?? (await getActiveAccount(tokenStore, { service }));\n } catch (error) {\n if (error instanceof Error && ((error as { code?: string }).code === 'REQUIRES_AUTHENTICATION' || error.name === 'AccountManagerError')) {\n accountId = undefined;\n } else {\n throw error;\n }\n }\n\n // Eagerly validate token exists or trigger OAuth flow\n await this.getAccessToken(accountId);\n\n // After OAuth flow completes, get the actual accountId (email) that was set\n const effectiveAccountId = accountId ?? (await getActiveAccount(tokenStore, { service }));\n if (!effectiveAccountId) {\n throw new Error(`No account found after OAuth flow for service ${service}`);\n }\n\n const auth = this.toAuth(effectiveAccountId);\n\n // Inject authContext and logger into extra\n (extra as { authContext?: AuthContext }).authContext = {\n auth,\n accountId: effectiveAccountId,\n };\n (extra as { logger?: unknown }).logger = logger;\n\n // Call original handler with all args\n return await originalHandler(...allArgs);\n } catch (error) {\n if (error instanceof AuthRequiredError) {\n logger.info('Authentication required', {\n service,\n tool: operation,\n descriptor: error.descriptor,\n });\n // Return auth_required response wrapped in { result } to match tool outputSchema pattern\n // Tools define outputSchema: z.object({ result: discriminatedUnion(...) }) where auth_required is a branch\n const authRequiredResponse = {\n type: 'auth_required' as const,\n provider: service,\n message: `Authentication required for ${operation}. Please authenticate with ${service}.`,\n url: error.descriptor.kind === 'auth_url' ? error.descriptor.url : undefined,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify({ result: authRequiredResponse }),\n },\n ],\n structuredContent: { result: authRequiredResponse },\n };\n }\n throw error;\n }\n };\n\n return {\n ...module,\n handler: wrappedHandler,\n } as T;\n };\n\n return {\n withToolAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 1),\n withResourceAuth: <T extends { name: string; template?: unknown; config?: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 2),\n withPromptAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 0),\n };\n }\n}\n\n/**\n * Create a loopback OAuth client for Google services\n * Works for both stdio and HTTP transports\n */\nexport function createGoogleFileAuth(config: LoopbackOAuthConfig): OAuth2TokenStorageProvider {\n return new LoopbackOAuthProvider(config);\n}\n"],"names":["addAccount","generatePKCE","getActiveAccount","getErrorTemplate","getSuccessTemplate","getToken","setAccountInfo","setActiveAccount","setToken","randomUUID","OAuth2Client","http","open","AuthRequiredError","LoopbackOAuthProvider","getAccessToken","accountId","logger","service","tokenStore","config","effectiveAccountId","debug","storedToken","isTokenValid","accessToken","refreshToken","info","refreshedToken","refreshAccessToken","error","Error","message","String","clientId","scope","redirectUri","verifier","codeVerifier","challenge","codeChallenge","stateId","set","createdAt","Date","now","authUrl","URL","searchParams","kind","provider","url","toString","headless","token","email","performEphemeralOAuthFlow","addedAt","toISOString","toAuth","clientSecret","client","getRequestMetadataAsync","_url","credentials","access_token","token_type","headers","Map","getUserEmail","response","fetch","Authorization","ok","status","text","userInfo","json","expiresAt","fetchUserEmailFromToken","errorText","configRedirectUri","listenHost","listenPort","callbackPath","useConfiguredUri","parsed","isLoopback","hostname","port","Number","parseInt","envPort","process","env","PORT","undefined","isFinite","pathname","warn","Promise","resolve","reject","server","serverPort","finalRedirectUri","createServer","req","res","writeHead","end","close","code","get","tokenResponse","exchangeCodeForToken","cachedToken","refresh_token","expires_in","exchangeError","listen","address","console","catch","setTimeout","tokenUrl","params","client_id","redirect_uri","grant_type","code_verifier","client_secret","body","URLSearchParams","method","handleOAuthCallback","state","pendingKey","pendingAuth","delete","authMiddleware","wrapAtPosition","module","extraPosition","operation","name","originalHandler","handler","wrappedHandler","allArgs","extra","_meta","auth","authContext","tool","descriptor","authRequiredResponse","type","content","JSON","stringify","result","structuredContent","withToolAuth","withResourceAuth","withPromptAuth","createGoogleFileAuth"],"mappings":"AAAA;;;;;CAKC,GAED,SAASA,UAAU,EAAEC,YAAY,EAAEC,gBAAgB,EAAEC,gBAAgB,EAAEC,kBAAkB,EAAEC,QAAQ,EAAmCC,cAAc,EAAEC,gBAAgB,EAAEC,QAAQ,QAAQ,eAAe;AACvM,SAASC,UAAU,QAAQ,SAAS;AACpC,SAASC,YAAY,QAAQ,sBAAsB;AACnD,YAAYC,UAAU,OAAO;AAC7B,OAAOC,UAAU,OAAO;AACxB,SAA2BC,iBAAiB,QAAwE,cAAc;AAUlI;;;;;;;;CAQC,GACD,OAAO,MAAMC;IAOX;;;;;GAKC,GACD,MAAMC,eAAeC,SAAkB,EAAmB;QACxD,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAEC,UAAU,EAAE,GAAG,IAAI,CAACC,MAAM;QAEnD,+CAA+C;QAC/C,MAAMC,qBAAqBL,sBAAAA,uBAAAA,YAAc,MAAMd,iBAAiBiB,YAAY;YAAED;QAAQ;QAEtF,qDAAqD;QACrD,IAAIG,oBAAoB;YACtBJ,OAAOK,KAAK,CAAC,wBAAwB;gBAAEJ;gBAASF,WAAWK;YAAmB;YAE9E,4CAA4C;YAC5C,MAAME,cAAc,MAAMlB,SAAsBc,YAAY;gBAAEH,WAAWK;gBAAoBH;YAAQ;YAErG,IAAIK,eAAe,IAAI,CAACC,YAAY,CAACD,cAAc;gBACjDN,OAAOK,KAAK,CAAC,6BAA6B;oBAAEN,WAAWK;gBAAmB;gBAC1E,OAAOE,YAAYE,WAAW;YAChC;YAEA,6DAA6D;YAC7D,IAAIF,wBAAAA,kCAAAA,YAAaG,YAAY,EAAE;gBAC7B,IAAI;oBACFT,OAAOU,IAAI,CAAC,mCAAmC;wBAAEX,WAAWK;oBAAmB;oBAC/E,MAAMO,iBAAiB,MAAM,IAAI,CAACC,kBAAkB,CAACN,YAAYG,YAAY;oBAC7E,MAAMlB,SAASW,YAAY;wBAAEH,WAAWK;wBAAoBH;oBAAQ,GAAGU;oBACvE,OAAOA,eAAeH,WAAW;gBACnC,EAAE,OAAOK,OAAO;oBACdb,OAAOU,IAAI,CAAC,iDAAiD;wBAC3DX,WAAWK;wBACXS,OAAOA,iBAAiBC,QAAQD,MAAME,OAAO,GAAGC,OAAOH;oBACzD;gBACA,iCAAiC;gBACnC;YACF;QACF;QAEA,2DAA2D;QAC3D,MAAM,EAAEI,QAAQ,EAAEC,KAAK,EAAEC,WAAW,EAAE,GAAG,IAAI,CAAChB,MAAM;QAEpD,IAAIgB,aAAa;YACf,2EAA2E;YAC3E,MAAM,EAAEC,UAAUC,YAAY,EAAEC,WAAWC,aAAa,EAAE,GAAGvC;YAC7D,MAAMwC,UAAUhC;YAEhB,kDAAkD;YAClD,MAAMU,WAAWuB,GAAG,CAAC,GAAGxB,QAAQ,SAAS,EAAEuB,SAAS,EAAE;gBAAEH;gBAAcK,WAAWC,KAAKC,GAAG;YAAG,GAAG,IAAI,KAAK;YAExG,8CAA8C;YAC9C,MAAMC,UAAU,IAAIC,IAAI;YACxBD,QAAQE,YAAY,CAACN,GAAG,CAAC,aAAaR;YACtCY,QAAQE,YAAY,CAACN,GAAG,CAAC,gBAAgBN;YACzCU,QAAQE,YAAY,CAACN,GAAG,CAAC,iBAAiB;YAC1CI,QAAQE,YAAY,CAACN,GAAG,CAAC,SAASP;YAClCW,QAAQE,YAAY,CAACN,GAAG,CAAC,eAAe;YACxCI,QAAQE,YAAY,CAACN,GAAG,CAAC,kBAAkBF;YAC3CM,QAAQE,YAAY,CAACN,GAAG,CAAC,yBAAyB;YAClDI,QAAQE,YAAY,CAACN,GAAG,CAAC,SAASD;YAClCK,QAAQE,YAAY,CAACN,GAAG,CAAC,UAAU;YAEnCzB,OAAOU,IAAI,CAAC,6CAA6C;gBAAET;gBAASkB;YAAY;YAChF,MAAM,IAAIvB,kBAAkB;gBAC1BoC,MAAM;gBACNC,UAAUhC;gBACViC,KAAKL,QAAQM,QAAQ;YACvB;QACF;QACA,8CAA8C;QAC9CnC,OAAOU,IAAI,CAAC,iCAAiC;YAAET;YAASmC,UAAU,IAAI,CAACjC,MAAM,CAACiC,QAAQ;QAAC;QACvF,MAAM,EAAEC,KAAK,EAAEC,KAAK,EAAE,GAAG,MAAM,IAAI,CAACC,yBAAyB;QAE7D,MAAMhD,SAASW,YAAY;YAAEH,WAAWuC;YAAOrC;QAAQ,GAAGoC;QAC1D,MAAMtD,WAAWmB,YAAY;YAAED;YAASF,WAAWuC;QAAM;QACzD,MAAMhD,iBAAiBY,YAAY;YAAED;YAASF,WAAWuC;QAAM;QAC/D,MAAMjD,eAAea,YAAY;YAAED;YAASF,WAAWuC;QAAM,GAAG;YAAEA;YAAOE,SAAS,IAAIb,OAAOc,WAAW;QAAG;QAE3GzC,OAAOU,IAAI,CAAC,wBAAwB;YAAET;YAASF,WAAWuC;QAAM;QAChE,OAAOD,MAAM7B,WAAW;IAC1B;IAEA;;;;;GAKC,GACDkC,OAAO3C,SAAkB,EAAgB;QACvC,MAAM,EAAEkB,QAAQ,EAAE0B,YAAY,EAAE,GAAG,IAAI,CAACxC,MAAM;QAC9C,MAAMyC,SAAS,IAAInD,aAAa;YAC9BwB;YACA,GAAI0B,gBAAgB;gBAAEA;YAAa,CAAC;QACtC;QAEA,qEAAqE;QACrEC,OAAOC,uBAAuB,GAAG,OAAOC;YACtC,sEAAsE;YACtE,MAAMT,QAAQ,MAAM,IAAI,CAACvC,cAAc,CAACC;YAExC,yDAAyD;YACzD6C,OAAOG,WAAW,GAAG;gBACnBC,cAAcX;gBACdY,YAAY;YACd;YAEA,iFAAiF;YACjF,MAAMC,UAAU,IAAIC;YACpBD,QAAQzB,GAAG,CAAC,iBAAiB,CAAC,OAAO,EAAEY,OAAO;YAC9C,OAAO;gBAAEa;YAAQ;QACnB;QAEA,OAAON;IACT;IAEA;;;;;;GAMC,GACD,MAAMQ,aAAarD,SAAkB,EAAmB;QACtD,iCAAiC;QACjC,MAAMsC,QAAQ,MAAM,IAAI,CAACvC,cAAc,CAACC;QAExC,mCAAmC;QACnC,MAAMsD,WAAW,MAAMC,MAAM,iDAAiD;YAC5EJ,SAAS;gBACPK,eAAe,CAAC,OAAO,EAAElB,OAAO;YAClC;QACF;QAEA,IAAI,CAACgB,SAASG,EAAE,EAAE;YAChB,MAAM,IAAI1C,MAAM,CAAC,yBAAyB,EAAEuC,SAASI,MAAM,CAAC,CAAC,EAAE,MAAMJ,SAASK,IAAI,IAAI;QACxF;QAEA,MAAMC,WAAY,MAAMN,SAASO,IAAI;QACrC,OAAOD,SAASrB,KAAK;IACvB;IAEQ/B,aAAa8B,KAAkB,EAAW;QAChD,IAAI,CAACA,MAAMwB,SAAS,EAAE,OAAO,MAAM,2BAA2B;QAC9D,OAAOlC,KAAKC,GAAG,KAAKS,MAAMwB,SAAS,GAAG,OAAO,kBAAkB;IACjE;IAEA;;;;;;GAMC,GACD,MAAcC,wBAAwBtD,WAAmB,EAAmB;QAC1E,MAAM,EAAER,MAAM,EAAE,GAAG,IAAI,CAACG,MAAM;QAE9B,MAAMkD,WAAW,MAAMC,MAAM,iDAAiD;YAC5EJ,SAAS;gBACPK,eAAe,CAAC,OAAO,EAAE/C,aAAa;YACxC;QACF;QAEA,IAAI,CAAC6C,SAASG,EAAE,EAAE;YAChB,MAAMO,YAAY,MAAMV,SAASK,IAAI;YACrC,MAAM,IAAI5C,MAAM,CAAC,iCAAiC,EAAEuC,SAASI,MAAM,CAAC,GAAG,EAAEM,WAAW;QACtF;QAEA,MAAMJ,WAAY,MAAMN,SAASO,IAAI;QACrC,MAAMtB,QAAQqB,SAASrB,KAAK;QAE5BtC,OAAOK,KAAK,CAAC,+CAA+C;YAAEiC;QAAM;QACpE,OAAOA;IACT;IAEA,MAAcC,4BAA4E;QACxF,MAAM,EAAEtB,QAAQ,EAAEC,KAAK,EAAEkB,QAAQ,EAAEpC,MAAM,EAAEmB,aAAa6C,iBAAiB,EAAE,GAAG,IAAI,CAAC7D,MAAM;QAEzF,6DAA6D;QAC7D,IAAI8D,aAAa,aAAa,4CAA4C;QAC1E,IAAIC,aAAa,GAAG,sCAAsC;QAE1D,wEAAwE;QACxE,IAAIC,eAAe,aAAa,wBAAwB;QACxD,IAAIC,mBAAmB;QAEvB,IAAIJ,mBAAmB;YACrB,IAAI;gBACF,MAAMK,SAAS,IAAIvC,IAAIkC;gBACvB,MAAMM,aAAaD,OAAOE,QAAQ,KAAK,eAAeF,OAAOE,QAAQ,KAAK;gBAE1E,IAAID,YAAY;oBACd,8DAA8D;oBAC9DL,aAAaI,OAAOE,QAAQ;oBAC5BL,aAAaG,OAAOG,IAAI,GAAGC,OAAOC,QAAQ,CAACL,OAAOG,IAAI,EAAE,MAAM;gBAChE,OAAO;oBACL,iEAAiE;oBACjE,+EAA+E;oBAC/E,wEAAwE;oBACxEP,aAAa;oBACb,MAAMU,UAAUC,QAAQC,GAAG,CAACC,IAAI,GAAGL,OAAOC,QAAQ,CAACE,QAAQC,GAAG,CAACC,IAAI,EAAE,MAAMC;oBAC3Eb,aAAaS,WAAWF,OAAOO,QAAQ,CAACL,WAAWA,UAAU;gBAC/D;gBAEA,iCAAiC;gBACjC,IAAIN,OAAOY,QAAQ,IAAIZ,OAAOY,QAAQ,KAAK,KAAK;oBAC9Cd,eAAeE,OAAOY,QAAQ;gBAChC;gBAEAb,mBAAmB;gBAEnBpE,OAAOK,KAAK,CAAC,iCAAiC;oBAC5C4D;oBACAC;oBACAC;oBACAhD,aAAa6C;oBACbM;gBACF;YACF,EAAE,OAAOzD,OAAO;gBACdb,OAAOkF,IAAI,CAAC,yDAAyD;oBACnE/D,aAAa6C;oBACbnD,OAAOA,iBAAiBC,QAAQD,MAAME,OAAO,GAAGC,OAAOH;gBACzD;YACA,8DAA8D;YAChE;QACF;QAEA,OAAO,IAAIsE,QAAQ,CAACC,SAASC;YAC3B,0BAA0B;YAC1B,MAAM,EAAEjE,UAAUC,YAAY,EAAEC,WAAWC,aAAa,EAAE,GAAGvC;YAE7D,IAAIsG,SAA6B;YACjC,IAAIC;YACJ,IAAIC,kBAA0B,wCAAwC;YAEtE,2DAA2D;YAC3DF,SAAS5F,KAAK+F,YAAY,CAAC,OAAOC,KAAKC;gBACrC,IAAI,CAACD,IAAIxD,GAAG,EAAE;oBACZyD,IAAIC,SAAS,CAAC,KAAK;wBAAE,gBAAgB;oBAAY;oBACjDD,IAAIE,GAAG,CAAC3G,iBAAiB;oBACzBoG,mBAAAA,6BAAAA,OAAQQ,KAAK;oBACbT,OAAO,IAAIvE,MAAM;oBACjB;gBACF;gBACA,MAAMoB,MAAM,IAAIJ,IAAI4D,IAAIxD,GAAG,EAAE,CAAC,iBAAiB,EAAEqD,YAAY;gBAE7D,IAAIrD,IAAI+C,QAAQ,KAAKd,cAAc;oBACjC,MAAM4B,OAAO7D,IAAIH,YAAY,CAACiE,GAAG,CAAC;oBAClC,MAAMnF,QAAQqB,IAAIH,YAAY,CAACiE,GAAG,CAAC;oBAEnC,IAAInF,OAAO;wBACT8E,IAAIC,SAAS,CAAC,KAAK;4BAAE,gBAAgB;wBAAY;wBACjDD,IAAIE,GAAG,CAAC3G,iBAAiB2B;wBACzByE,mBAAAA,6BAAAA,OAAQQ,KAAK;wBACbT,OAAO,IAAIvE,MAAM,CAAC,aAAa,EAAED,OAAO;wBACxC;oBACF;oBAEA,IAAI,CAACkF,MAAM;wBACTJ,IAAIC,SAAS,CAAC,KAAK;4BAAE,gBAAgB;wBAAY;wBACjDD,IAAIE,GAAG,CAAC3G,iBAAiB;wBACzBoG,mBAAAA,6BAAAA,OAAQQ,KAAK;wBACbT,OAAO,IAAIvE,MAAM;wBACjB;oBACF;oBAEA,IAAI;wBACF,mFAAmF;wBACnF,MAAMmF,gBAAgB,MAAM,IAAI,CAACC,oBAAoB,CAACH,MAAM1E,cAAcmE;wBAE1E,qBAAqB;wBACrB,MAAMW,cAA2B;4BAC/B3F,aAAayF,cAAcjD,YAAY;4BACvC,GAAIiD,cAAcG,aAAa,KAAKrB,aAAa;gCAAEtE,cAAcwF,cAAcG,aAAa;4BAAC,CAAC;4BAC9F,GAAIH,cAAcI,UAAU,KAAKtB,aAAa;gCAAElB,WAAWlC,KAAKC,GAAG,KAAKqE,cAAcI,UAAU,GAAG;4BAAK,CAAC;4BACzG,GAAIJ,cAAc/E,KAAK,KAAK6D,aAAa;gCAAE7D,OAAO+E,cAAc/E,KAAK;4BAAC,CAAC;wBACzE;wBAEA,0DAA0D;wBAC1D,MAAMoB,QAAQ,MAAM,IAAI,CAACwB,uBAAuB,CAACmC,cAAcjD,YAAY;wBAE3E2C,IAAIC,SAAS,CAAC,KAAK;4BAAE,gBAAgB;wBAAY;wBACjDD,IAAIE,GAAG,CAAC1G;wBACRmG,mBAAAA,6BAAAA,OAAQQ,KAAK;wBACbV,QAAQ;4BAAE/C,OAAO8D;4BAAa7D;wBAAM;oBACtC,EAAE,OAAOgE,eAAe;wBACtBtG,OAAOa,KAAK,CAAC,yBAAyB;4BAAEA,OAAOyF,yBAAyBxF,QAAQwF,cAAcvF,OAAO,GAAGC,OAAOsF;wBAAe;wBAC9HX,IAAIC,SAAS,CAAC,KAAK;4BAAE,gBAAgB;wBAAY;wBACjDD,IAAIE,GAAG,CAAC3G,iBAAiB;wBACzBoG,mBAAAA,6BAAAA,OAAQQ,KAAK;wBACbT,OAAOiB;oBACT;gBACF,OAAO;oBACLX,IAAIC,SAAS,CAAC,KAAK;wBAAE,gBAAgB;oBAAa;oBAClDD,IAAIE,GAAG,CAAC;gBACV;YACF;YAEA,iCAAiC;YACjCP,OAAOiB,MAAM,CAACrC,YAAYD,YAAY;gBACpC,MAAMuC,UAAUlB,mBAAAA,6BAAAA,OAAQkB,OAAO;gBAC/B,IAAI,CAACA,WAAW,OAAOA,YAAY,UAAU;oBAC3ClB,mBAAAA,6BAAAA,OAAQQ,KAAK;oBACbT,OAAO,IAAIvE,MAAM;oBACjB;gBACF;gBAEAyE,aAAaiB,QAAQhC,IAAI;gBAEzB,+BAA+B;gBAC/B,IAAIJ,oBAAoBJ,mBAAmB;oBACzC,kFAAkF;oBAClFwB,mBAAmBxB;gBACrB,OAAO;oBACL,oFAAoF;oBACpFwB,mBAAmB,CAAC,iBAAiB,EAAED,aAAapB,cAAc;gBACpE;gBAEA,iBAAiB;gBACjB,MAAMtC,UAAU,IAAIC,IAAI;gBACxBD,QAAQE,YAAY,CAACN,GAAG,CAAC,aAAaR;gBACtCY,QAAQE,YAAY,CAACN,GAAG,CAAC,gBAAgB+D;gBACzC3D,QAAQE,YAAY,CAACN,GAAG,CAAC,iBAAiB;gBAC1CI,QAAQE,YAAY,CAACN,GAAG,CAAC,SAASP;gBAClCW,QAAQE,YAAY,CAACN,GAAG,CAAC,eAAe;gBACxCI,QAAQE,YAAY,CAACN,GAAG,CAAC,UAAU;gBACnCI,QAAQE,YAAY,CAACN,GAAG,CAAC,kBAAkBF;gBAC3CM,QAAQE,YAAY,CAACN,GAAG,CAAC,yBAAyB;gBAElDzB,OAAOU,IAAI,CAAC,kCAAkC;oBAAE8D,MAAMe;oBAAYnD;gBAAS;gBAE3E,IAAIA,UAAU;oBACZ,mEAAmE;oBACnEqE,QAAQ5F,KAAK,CAAC;oBACd4F,QAAQ5F,KAAK,CAAC;oBACd4F,QAAQ5F,KAAK,CAAC,CAAC,GAAG,EAAEgB,QAAQM,QAAQ,GAAG,EAAE,CAAC;oBAC1CsE,QAAQ5F,KAAK,CAAC;gBAChB,OAAO;oBACL,+CAA+C;oBAC/Cb,OAAOU,IAAI,CAAC;oBACZf,KAAKkC,QAAQM,QAAQ,IAAIuE,KAAK,CAAC,CAAC7F;wBAC9Bb,OAAOU,IAAI,CAAC,wCAAwC;4BAAEG,OAAOA,MAAME,OAAO;wBAAC;wBAC3E0F,QAAQ5F,KAAK,CAAC;wBACd4F,QAAQ5F,KAAK,CAAC,CAAC,GAAG,EAAEgB,QAAQM,QAAQ,GAAG,EAAE,CAAC;oBAC5C;gBACF;YACF;YAEA,0BAA0B;YAC1BwE,WACE;gBACE,IAAIrB,QAAQ;oBACVA,OAAOQ,KAAK;oBACZT,OAAO,IAAIvE,MAAM;gBACnB;YACF,GACA,IAAI,KAAK;QAEb;IACF;IAEA,MAAcoF,qBAAqBH,IAAY,EAAE1E,YAAoB,EAAEF,WAAmB,EAA0B;QAClH,MAAM,EAAEF,QAAQ,EAAE0B,YAAY,EAAE,GAAG,IAAI,CAACxC,MAAM;QAE9C,MAAMyG,WAAW;QACjB,MAAMC,SAAiC;YACrCd;YACAe,WAAW7F;YACX8F,cAAc5F;YACd6F,YAAY;YACZC,eAAe5F;QACjB;QACA,IAAIsB,cAAc;YAChBkE,OAAOK,aAAa,GAAGvE;QACzB;QACA,MAAMwE,OAAO,IAAIC,gBAAgBP;QAEjC,MAAMxD,WAAW,MAAMC,MAAMsD,UAAU;YACrCS,QAAQ;YACRnE,SAAS;gBACP,gBAAgB;YAClB;YACAiE,MAAMA,KAAKhF,QAAQ;QACrB;QAEA,IAAI,CAACkB,SAASG,EAAE,EAAE;YAChB,MAAMO,YAAY,MAAMV,SAASK,IAAI;YACrC,MAAM,IAAI5C,MAAM,CAAC,uBAAuB,EAAEuC,SAASI,MAAM,CAAC,CAAC,EAAEM,WAAW;QAC1E;QAEA,OAAQ,MAAMV,SAASO,IAAI;IAC7B;IAEA,MAAchD,mBAAmBH,YAAoB,EAAwB;QAC3E,MAAM,EAAEQ,QAAQ,EAAE0B,YAAY,EAAE,GAAG,IAAI,CAACxC,MAAM;QAE9C,MAAMyG,WAAW;QACjB,MAAMC,SAAiC;YACrCT,eAAe3F;YACfqG,WAAW7F;YACX+F,YAAY;QACd;QACA,IAAIrE,cAAc;YAChBkE,OAAOK,aAAa,GAAGvE;QACzB;QACA,MAAMwE,OAAO,IAAIC,gBAAgBP;QAEjC,MAAMxD,WAAW,MAAMC,MAAMsD,UAAU;YACrCS,QAAQ;YACRnE,SAAS;gBACP,gBAAgB;YAClB;YACAiE,MAAMA,KAAKhF,QAAQ;QACrB;QAEA,IAAI,CAACkB,SAASG,EAAE,EAAE;YAChB,MAAMO,YAAY,MAAMV,SAASK,IAAI;YACrC,MAAM,IAAI5C,MAAM,CAAC,sBAAsB,EAAEuC,SAASI,MAAM,CAAC,CAAC,EAAEM,WAAW;QACzE;QAEA,MAAMkC,gBAAiB,MAAM5C,SAASO,IAAI;QAE1C,OAAO;YACLpD,aAAayF,cAAcjD,YAAY;YACvCvC,cAAcA;YACd,GAAIwF,cAAcI,UAAU,KAAKtB,aAAa;gBAAElB,WAAWlC,KAAKC,GAAG,KAAKqE,cAAcI,UAAU,GAAG;YAAK,CAAC;YACzG,GAAIJ,cAAc/E,KAAK,KAAK6D,aAAa;gBAAE7D,OAAO+E,cAAc/E,KAAK;YAAC,CAAC;QACzE;IACF;IAEA;;;;;;GAMC,GACD,MAAMoG,oBAAoBT,MAAwC,EAAkD;QAClH,MAAM,EAAEd,IAAI,EAAEwB,KAAK,EAAE,GAAGV;QACxB,MAAM,EAAE7G,MAAM,EAAEC,OAAO,EAAEC,UAAU,EAAEe,QAAQ,EAAE0B,YAAY,EAAExB,WAAW,EAAE,GAAG,IAAI,CAAChB,MAAM;QAExF,IAAI,CAACoH,OAAO;YACV,MAAM,IAAIzG,MAAM;QAClB;QAEA,IAAI,CAACK,aAAa;YAChB,MAAM,IAAIL,MAAM;QAClB;QAEA,6CAA6C;QAC7C,MAAM0G,aAAa,GAAGvH,QAAQ,SAAS,EAAEsH,OAAO;QAChD,MAAME,cAAc,MAAMvH,WAAW8F,GAAG,CAA8CwB;QAEtF,IAAI,CAACC,aAAa;YAChB,MAAM,IAAI3G,MAAM;QAClB;QAEA,wBAAwB;QACxB,IAAIa,KAAKC,GAAG,KAAK6F,YAAY/F,SAAS,GAAG,IAAI,KAAK,MAAM;YACtD,MAAMxB,WAAWwH,MAAM,CAACF;YACxB,MAAM,IAAI1G,MAAM;QAClB;QAEAd,OAAOU,IAAI,CAAC,6BAA6B;YAAET;YAASsH;QAAM;QAE1D,0BAA0B;QAC1B,MAAMJ,OAAO,IAAIC,gBAAgB;YAC/BrB;YACAe,WAAW7F;YACX,GAAI0B,gBAAgB;gBAAEuE,eAAevE;YAAa,CAAC;YACnDoE,cAAc5F;YACd6F,YAAY;YACZC,eAAeQ,YAAYpG,YAAY;QACzC;QAEA,MAAMgC,WAAW,MAAMC,MAAM,uCAAuC;YAClE+D,QAAQ;YACRnE,SAAS;gBACP,gBAAgB;YAClB;YACAiE,MAAMA,KAAKhF,QAAQ;QACrB;QAEA,IAAI,CAACkB,SAASG,EAAE,EAAE;YAChB,MAAMO,YAAY,MAAMV,SAASK,IAAI;YACrC,MAAM,IAAI5C,MAAM,CAAC,uBAAuB,EAAEuC,SAASI,MAAM,CAAC,CAAC,EAAEM,WAAW;QAC1E;QAEA,MAAMkC,gBAAiB,MAAM5C,SAASO,IAAI;QAE1C,mBAAmB;QACnB,MAAMtB,QAAQ,MAAM,IAAI,CAACwB,uBAAuB,CAACmC,cAAcjD,YAAY;QAE3E,sBAAsB;QACtB,MAAMmD,cAA2B;YAC/B3F,aAAayF,cAAcjD,YAAY;YACvCvC,cAAcwF,cAAcG,aAAa;YACzCvC,WAAWoC,cAAcI,UAAU,GAAG1E,KAAKC,GAAG,KAAKqE,cAAcI,UAAU,GAAG,OAAOtB;YACrF,GAAIkB,cAAc/E,KAAK,KAAK6D,aAAa;gBAAE7D,OAAO+E,cAAc/E,KAAK;YAAC,CAAC;QACzE;QAEA,cAAc;QACd,MAAM3B,SAASW,YAAY;YAAEH,WAAWuC;YAAOrC;QAAQ,GAAGkG;QAE1D,gCAAgC;QAChC,MAAMpH,WAAWmB,YAAY;YAAED;YAASF,WAAWuC;QAAM;QACzD,MAAMhD,iBAAiBY,YAAY;YAAED;YAASF,WAAWuC;QAAM;QAE/D,yBAAyB;QACzB,MAAMjD,eACJa,YACA;YAAED;YAASF,WAAWuC;QAAM,GAC5B;YACEA;YACAE,SAAS,IAAIb,OAAOc,WAAW;QACjC;QAGF,wBAAwB;QACxB,MAAMvC,WAAWwH,MAAM,CAACF;QAExBxH,OAAOU,IAAI,CAAC,4BAA4B;YAAET;YAASqC;QAAM;QAEzD,OAAO;YAAEA;YAAOD,OAAO8D;QAAY;IACrC;IAEA;;;;;;;;;;;;;;;;;;;;;;;GAuBC,GACDwB,iBAAiB;QACf,MAAM,EAAE1H,OAAO,EAAEC,UAAU,EAAEF,MAAM,EAAE,GAAG,IAAI,CAACG,MAAM;QAEnD,0EAA0E;QAC1E,sFAAsF;QACtF,MAAMyH,iBAAiB,CAAuEC,QAAWC;YACvG,MAAMC,YAAYF,OAAOG,IAAI;YAC7B,MAAMC,kBAAkBJ,OAAOK,OAAO;YAEtC,MAAMC,iBAAiB,OAAO,GAAGC;gBAC/B,0CAA0C;gBAC1C,MAAMC,QAAQD,OAAO,CAACN,cAAc;gBAEpC,IAAI;oBACF,qDAAqD;oBACrD,IAAI/H;oBACJ,IAAI;;4BACU;wBAAZA,qBAAY,eAAA,AAACsI,MAA6CC,KAAK,cAAnD,mCAAA,aAAqDvI,SAAS,uCAAK,MAAMd,iBAAiBiB,YAAY;4BAAED;wBAAQ;oBAC9H,EAAE,OAAOY,OAAO;wBACd,IAAIA,iBAAiBC,SAAU,CAAA,AAACD,MAA4BkF,IAAI,KAAK,6BAA6BlF,MAAMmH,IAAI,KAAK,qBAAoB,GAAI;4BACvIjI,YAAYgF;wBACd,OAAO;4BACL,MAAMlE;wBACR;oBACF;oBAEA,sDAAsD;oBACtD,MAAM,IAAI,CAACf,cAAc,CAACC;oBAE1B,4EAA4E;oBAC5E,MAAMK,qBAAqBL,sBAAAA,uBAAAA,YAAc,MAAMd,iBAAiBiB,YAAY;wBAAED;oBAAQ;oBACtF,IAAI,CAACG,oBAAoB;wBACvB,MAAM,IAAIU,MAAM,CAAC,8CAA8C,EAAEb,SAAS;oBAC5E;oBAEA,MAAMsI,OAAO,IAAI,CAAC7F,MAAM,CAACtC;oBAEzB,2CAA2C;oBAC1CiI,MAAwCG,WAAW,GAAG;wBACrDD;wBACAxI,WAAWK;oBACb;oBACCiI,MAA+BrI,MAAM,GAAGA;oBAEzC,sCAAsC;oBACtC,OAAO,MAAMiI,mBAAmBG;gBAClC,EAAE,OAAOvH,OAAO;oBACd,IAAIA,iBAAiBjB,mBAAmB;wBACtCI,OAAOU,IAAI,CAAC,2BAA2B;4BACrCT;4BACAwI,MAAMV;4BACNW,YAAY7H,MAAM6H,UAAU;wBAC9B;wBACA,yFAAyF;wBACzF,2GAA2G;wBAC3G,MAAMC,uBAAuB;4BAC3BC,MAAM;4BACN3G,UAAUhC;4BACVc,SAAS,CAAC,4BAA4B,EAAEgH,UAAU,2BAA2B,EAAE9H,QAAQ,CAAC,CAAC;4BACzFiC,KAAKrB,MAAM6H,UAAU,CAAC1G,IAAI,KAAK,aAAanB,MAAM6H,UAAU,CAACxG,GAAG,GAAG6C;wBACrE;wBAEA,OAAO;4BACL8D,SAAS;gCACP;oCACED,MAAM;oCACNlF,MAAMoF,KAAKC,SAAS,CAAC;wCAAEC,QAAQL;oCAAqB;gCACtD;6BACD;4BACDM,mBAAmB;gCAAED,QAAQL;4BAAqB;wBACpD;oBACF;oBACA,MAAM9H;gBACR;YACF;YAEA,OAAO;gBACL,GAAGgH,MAAM;gBACTK,SAASC;YACX;QACF;QAEA,OAAO;YACLe,cAAc,CAAgErB,SAAcD,eAAeC,QAAQ;YACnHsB,kBAAkB,CAAqFtB,SAAcD,eAAeC,QAAQ;YAC5IuB,gBAAgB,CAAgEvB,SAAcD,eAAeC,QAAQ;QACvH;IACF;IAloBA,YAAY1H,MAA2B,CAAE;QACvC,IAAI,CAACA,MAAM,GAAGA;IAChB;AAioBF;AAEA;;;CAGC,GACD,OAAO,SAASkJ,qBAAqBlJ,MAA2B;IAC9D,OAAO,IAAIN,sBAAsBM;AACnC"}
|