@nlabs/lex
Version:
302 lines (301 loc) • 38.3 kB
JavaScript
/**
* Copyright (c) 2018-Present, Nitrogen Labs, Inc.
* Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
*/ import chalk from 'chalk';
import { Command } from 'commander';
import { readFileSync } from 'fs';
import { sync as globSync } from 'glob';
import { LexConfig } from '../../LexConfig.js';
import { callAIService } from '../../utils/aiService.js';
import { log } from '../../utils/log.js';
if (process.env.CURSOR_EXTENSION === 'true' || process.env.CURSOR_TERMINAL === 'true' || process.env.CURSOR_APP === 'true' || process.env.PATH?.includes('cursor') || process.env.CURSOR_SESSION_ID) {
process.env.CURSOR_IDE = 'true';
}
const getFileContext = (filePath)=>{
try {
const content = readFileSync(filePath, 'utf-8');
return `File: ${filePath}\n\n${content}`;
} catch (_error) {
return `Error reading file: ${filePath}`;
}
};
const getProjectContext = async (options)=>{
const { file, task, context } = options;
if (context === false) {
return '';
}
let projectContext = '';
if (file) {
projectContext += getFileContext(file);
}
switch(task){
case 'generate':
const files = globSync('src/**/*.{ts,tsx,js,jsx}', {
cwd: process.cwd(),
ignore: [
'**/node_modules/**',
'**/lib/**',
'**/dist/**',
'**/*.test.*',
'**/*.spec.*'
],
maxDepth: 3
});
projectContext += `\n\nProject structure:\n${files.join('\n')}`;
break;
case 'test':
if (file) {
const testConfig = getFileContext('jest.config.js');
projectContext += `\n\nTest configuration:\n${testConfig}`;
}
break;
case 'optimize':
const webpackConfig = getFileContext('webpack.config.js');
projectContext += `\n\nWebpack configuration:\n${webpackConfig}`;
break;
default:
break;
}
return projectContext;
};
const constructPrompt = (options, projectContext)=>{
const { task = 'help', prompt = '' } = options;
const taskInstructions = {
analyze: 'Analyze the following code:',
ask: 'Provide guidance on the following development question:',
explain: 'Explain the following code in detail, including any patterns, potential issues, and improvement suggestions:',
generate: 'Generate code according to the following request. Make sure it follows best practices and is well documented:',
help: 'Provide guidance on the following development question:',
optimize: 'Analyze the following code/configuration and suggest optimization improvements:',
test: 'Generate comprehensive unit tests for the following code:'
};
const taskInstruction = taskInstructions[task] || taskInstructions.help;
let fullPrompt = `${taskInstruction}\n\n${prompt}`;
if (projectContext) {
fullPrompt += `\n\n===CONTEXT===\n${projectContext}`;
}
return fullPrompt;
};
const displayResponse = (response, options)=>{
const { task = 'help', quiet = false } = options;
let content = '';
if (typeof response === 'string') {
content = response;
} else if (response.choices?.[0]?.message?.content) {
const { content: messageContent } = response.choices[0].message;
content = messageContent;
} else if (response.content) {
const { content: responseContent } = response;
content = responseContent;
} else {
content = 'No response received from AI model';
}
const cleanedContent = cleanResponse(content, options);
switch(task){
case 'generate':
log('\nGenerated Code:\n', 'success', quiet);
log(cleanedContent, 'default', quiet);
break;
case 'explain':
log('\nCode Explanation:\n', 'success', quiet);
log(cleanedContent, 'default', quiet);
break;
case 'test':
log('\nGenerated Tests:\n', 'success', quiet);
log(cleanedContent, 'default', quiet);
break;
case 'optimize':
log('\nOptimization Suggestions:\n', 'success', quiet);
log(cleanedContent, 'default', quiet);
break;
default:
log('\nAI Response:\n', 'success', quiet);
log(cleanedContent, 'default', quiet);
break;
}
};
const cleanResponse = (content, options)=>{
const { prompt = '', task = 'help' } = options;
if (!content) {
return content;
}
let cleanedContent = content;
const taskInstructions = {
analyze: 'Analyze the following code:',
ask: 'Provide guidance on the following development question:',
explain: 'Explain the following code in detail, including any patterns, potential issues, and improvement suggestions:',
generate: 'Generate code according to the following request. Make sure it follows best practices and is well documented:',
help: 'Provide guidance on the following development question:',
optimize: 'Analyze the following code/configuration and suggest optimization improvements:',
test: 'Generate comprehensive unit tests for the following code:'
};
const instruction = taskInstructions[task] || '';
if (instruction && cleanedContent.includes(instruction)) {
cleanedContent = cleanedContent.replace(instruction, '').trim();
}
if (prompt && cleanedContent.includes(prompt)) {
cleanedContent = cleanedContent.replace(prompt, '').trim();
}
if (cleanedContent.includes('===CONTEXT===')) {
cleanedContent = cleanedContent.split('===CONTEXT===')[0].trim();
}
if (!cleanedContent) {
return content;
}
return cleanedContent;
};
const getProviderAuth = (provider)=>{
if (process.cwd().includes('reaktor')) {
return 'cursor-auth';
}
if (process.env.AI_API_KEY) {
return process.env.AI_API_KEY;
}
if (provider === 'none' && process.env.CURSOR_IDE === 'true') {
return 'cursor-auth';
}
switch(provider){
case 'openai':
return process.env.OPENAI_API_KEY;
case 'anthropic':
return process.env.ANTHROPIC_API_KEY;
case 'cursor':
return 'cursor-auth';
case 'copilot':
return process.env.GITHUB_TOKEN;
case 'none':
return undefined;
default:
return undefined;
}
};
const detectCursorIDE = ()=>{
if (process.env.CURSOR_IDE === 'true') {
return true;
}
const possibleCursorSignals = [
process.env.CURSOR_EXTENSION === 'true',
process.env.CURSOR_TERMINAL === 'true',
process.env.CURSOR_APP === 'true',
process.env.PATH?.includes('cursor'),
!!process.env.CURSOR_SESSION_ID
];
const isCursorIDE = possibleCursorSignals.some((signal)=>signal);
if (isCursorIDE) {
process.env.CURSOR_IDE = 'true';
}
return isCursorIDE;
};
export const aiFunction = async (options)=>{
try {
const config = LexConfig.config || {};
const aiConfig = config.ai || {};
const provider = options.provider || aiConfig.provider || 'none';
if (provider === 'none' && !process.env.CURSOR_EXTENSION) {
log(`${chalk.red('Error:')} No AI provider configured.`, 'error');
return {
error: 'No AI provider configured'
};
}
const task = options.task || 'ask';
const validTasks = [
'explain',
'generate',
'test',
'analyze',
'ask'
];
if (!validTasks.includes(task)) {
log(`${chalk.red('Error:')} Invalid task "${task}". Valid tasks are: ${validTasks.join(', ')}`, 'error');
return {
error: `Invalid task "${task}"`
};
}
const { prompt } = options;
if (!prompt) {
log(`${chalk.red('Error:')} No prompt provided. Use --prompt "Your prompt here"`, 'error');
return {
error: 'No prompt provided'
};
}
let context = '';
if (options.file) {
try {
const fs = await import('fs/promises');
const glob = await import('glob');
const files = await glob.glob(options.file);
if (files.length === 0) {
log(`${chalk.yellow('Warning:')} No files found matching "${options.file}"`, 'warning');
} else {
for (const file of files){
const content = await fs.readFile(file, 'utf8');
context += `\n===FILE: ${file}===\n${content}\n`;
}
}
} catch (error) {
log(`${chalk.yellow('Warning:')} Error reading file: ${error.message}`, 'warning');
}
}
if (options.dir) {
try {
const { execaSync } = await import('execa');
const result = execaSync('find', [
options.dir,
'-type',
'f',
'|',
'sort'
]);
context += `\n===Project structure:===\n${result.stdout}\n`;
} catch (error) {
log(`${chalk.yellow('Warning:')} Error reading directory: ${error.message}`, 'warning');
}
}
let formattedPrompt = '';
switch(task){
case 'explain':
formattedPrompt = `Explain the following code:\n${prompt}`;
break;
case 'generate':
formattedPrompt = `Generate code according to the following request:\n${prompt}`;
break;
case 'test':
formattedPrompt = `Generate comprehensive unit tests:\n${prompt}`;
break;
case 'analyze':
formattedPrompt = `Analyze the following code:\n${prompt}`;
break;
case 'ask':
formattedPrompt = `Provide guidance on the following development question:\n${prompt}`;
break;
}
if (context) {
formattedPrompt += `\n===CONTEXT===\n${context}`;
}
if ((provider === 'cursor' || process.env.CURSOR_EXTENSION) && task === 'generate') {
log('Using Cursor IDE for code generation...', 'info');
log('Note: For full code generation capabilities, please use Cursor IDE directly with Cmd+L or Cmd+K.', 'info');
log('The CLI integration has limited capabilities for code generation.', 'warning');
} else if (provider === 'cursor' || process.env.CURSOR_EXTENSION) {
log('Using Cursor IDE for AI assistance...', 'info');
log('Note: This is a limited integration. For full AI capabilities, use Cursor IDE directly.', 'info');
} else {
log(`Using ${provider} for AI assistance...`, 'info');
}
const response = await callAIService(formattedPrompt, options.quiet || false);
log(`\n${response}`, 'success');
return {
response
};
} catch (error) {
log(`${chalk.red('Error:')} ${error.message}`, 'error');
return {
error: error.message
};
}
};
export const ai = new Command('ai').description('Use AI to help with development tasks').option('--provider <provider>', 'AI provider to use (openai, anthropic, cursor)').option('--task <task>', 'Task to perform (explain, generate, test, analyze, ask)').option('--prompt <prompt>', 'Prompt to send to AI').option('--file <file>', 'File to analyze').option('--dir <dir>', 'Directory to analyze').action(async (options)=>{
await aiFunction(options);
});
export default ai;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21tYW5kcy9haS9haS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIENvcHlyaWdodCAoYykgMjAxOC1QcmVzZW50LCBOaXRyb2dlbiBMYWJzLCBJbmMuXG4gKiBDb3B5cmlnaHRzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIHRoZSBhY2NvbXBhbnlpbmcgTElDRU5TRSBmaWxlIGZvciB0ZXJtcy5cbiAqL1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCB7Q29tbWFuZH0gZnJvbSAnY29tbWFuZGVyJztcbmltcG9ydCB7cmVhZEZpbGVTeW5jfSBmcm9tICdmcyc7XG5pbXBvcnQge3N5bmMgYXMgZ2xvYlN5bmN9IGZyb20gJ2dsb2InO1xuXG5pbXBvcnQge0xleENvbmZpZ30gZnJvbSAnLi4vLi4vTGV4Q29uZmlnLmpzJztcbmltcG9ydCB7Y2FsbEFJU2VydmljZX0gZnJvbSAnLi4vLi4vdXRpbHMvYWlTZXJ2aWNlLmpzJztcbmltcG9ydCB7bG9nfSBmcm9tICcuLi8uLi91dGlscy9sb2cuanMnO1xuXG5pZihwcm9jZXNzLmVudi5DVVJTT1JfRVhURU5TSU9OID09PSAndHJ1ZScgfHxcbiAgcHJvY2Vzcy5lbnYuQ1VSU09SX1RFUk1JTkFMID09PSAndHJ1ZScgfHxcbiAgcHJvY2Vzcy5lbnYuQ1VSU09SX0FQUCA9PT0gJ3RydWUnIHx8XG4gIHByb2Nlc3MuZW52LlBBVEg/LmluY2x1ZGVzKCdjdXJzb3InKSB8fFxuICBwcm9jZXNzLmVudi5DVVJTT1JfU0VTU0lPTl9JRCkge1xuICBwcm9jZXNzLmVudi5DVVJTT1JfSURFID0gJ3RydWUnO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEFJT3B0aW9ucyB7XG4gIHJlYWRvbmx5IGNsaU5hbWU/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGNvbnRleHQ/OiBib29sZWFuO1xuICByZWFkb25seSBmaWxlPzogc3RyaW5nO1xuICByZWFkb25seSBsZXhDb25maWc/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IG1vZGVsPzogc3RyaW5nO1xuICByZWFkb25seSBwcm9tcHQ/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHF1aWV0PzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgdGFzaz86ICdnZW5lcmF0ZScgfCAnZXhwbGFpbicgfCAndGVzdCcgfCAnb3B0aW1pemUnIHwgJ2hlbHAnIHwgJ2FzaycgfCAnYW5hbHl6ZSc7XG4gIHJlYWRvbmx5IGRlYnVnPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgcHJvdmlkZXI/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGRpcj86IHN0cmluZztcbn1cblxuY29uc3QgZ2V0RmlsZUNvbnRleHQgPSAoZmlsZVBhdGg6IHN0cmluZyk6IHN0cmluZyA9PiB7XG4gIHRyeSB7XG4gICAgY29uc3QgY29udGVudCA9IHJlYWRGaWxlU3luYyhmaWxlUGF0aCwgJ3V0Zi04Jyk7XG4gICAgcmV0dXJuIGBGaWxlOiAke2ZpbGVQYXRofVxcblxcbiR7Y29udGVudH1gO1xuICB9IGNhdGNoKF9lcnJvcikge1xuICAgIHJldHVybiBgRXJyb3IgcmVhZGluZyBmaWxlOiAke2ZpbGVQYXRofWA7XG4gIH1cbn07XG5cbmNvbnN0IGdldFByb2plY3RDb250ZXh0ID0gYXN5bmMgKG9wdGlvbnM6IEFJT3B0aW9ucyk6IFByb21pc2U8c3RyaW5nPiA9PiB7XG4gIGNvbnN0IHtmaWxlLCB0YXNrLCBjb250ZXh0fSA9IG9wdGlvbnM7XG5cbiAgaWYoY29udGV4dCA9PT0gZmFsc2UpIHtcbiAgICByZXR1cm4gJyc7XG4gIH1cblxuICBsZXQgcHJvamVjdENvbnRleHQgPSAnJztcblxuICBpZihmaWxlKSB7XG4gICAgcHJvamVjdENvbnRleHQgKz0gZ2V0RmlsZUNvbnRleHQoZmlsZSk7XG4gIH1cblxuICBzd2l0Y2godGFzaykge1xuICAgIGNhc2UgJ2dlbmVyYXRlJzpcbiAgICAgIGNvbnN0IGZpbGVzID0gZ2xvYlN5bmMoJ3NyYy8qKi8qLnt0cyx0c3gsanMsanN4fScsIHtcbiAgICAgICAgY3dkOiBwcm9jZXNzLmN3ZCgpLFxuICAgICAgICBpZ25vcmU6IFsnKiovbm9kZV9tb2R1bGVzLyoqJywgJyoqL2xpYi8qKicsICcqKi9kaXN0LyoqJywgJyoqLyoudGVzdC4qJywgJyoqLyouc3BlYy4qJ10sXG4gICAgICAgIG1heERlcHRoOiAzXG4gICAgICB9KTtcbiAgICAgIHByb2plY3RDb250ZXh0ICs9IGBcXG5cXG5Qcm9qZWN0IHN0cnVjdHVyZTpcXG4ke2ZpbGVzLmpvaW4oJ1xcbicpfWA7XG4gICAgICBicmVhaztcblxuICAgIGNhc2UgJ3Rlc3QnOlxuICAgICAgaWYoZmlsZSkge1xuICAgICAgICBjb25zdCB0ZXN0Q29uZmlnID0gZ2V0RmlsZUNvbnRleHQoJ2plc3QuY29uZmlnLmpzJyk7XG4gICAgICAgIHByb2plY3RDb250ZXh0ICs9IGBcXG5cXG5UZXN0IGNvbmZpZ3VyYXRpb246XFxuJHt0ZXN0Q29uZmlnfWA7XG4gICAgICB9XG4gICAgICBicmVhaztcblxuICAgIGNhc2UgJ29wdGltaXplJzpcbiAgICAgIGNvbnN0IHdlYnBhY2tDb25maWcgPSBnZXRGaWxlQ29udGV4dCgnd2VicGFjay5jb25maWcuanMnKTtcbiAgICAgIHByb2plY3RDb250ZXh0ICs9IGBcXG5cXG5XZWJwYWNrIGNvbmZpZ3VyYXRpb246XFxuJHt3ZWJwYWNrQ29uZmlnfWA7XG4gICAgICBicmVhaztcblxuICAgIGRlZmF1bHQ6XG4gICAgICBicmVhaztcbiAgfVxuXG4gIHJldHVybiBwcm9qZWN0Q29udGV4dDtcbn07XG5cbmNvbnN0IGNvbnN0cnVjdFByb21wdCA9IChvcHRpb25zOiBBSU9wdGlvbnMsIHByb2plY3RDb250ZXh0OiBzdHJpbmcpOiBzdHJpbmcgPT4ge1xuICBjb25zdCB7dGFzayA9ICdoZWxwJywgcHJvbXB0ID0gJyd9ID0gb3B0aW9ucztcblxuICBjb25zdCB0YXNrSW5zdHJ1Y3Rpb25zOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgIGFuYWx5emU6ICdBbmFseXplIHRoZSBmb2xsb3dpbmcgY29kZTonLFxuICAgIGFzazogJ1Byb3ZpZGUgZ3VpZGFuY2Ugb24gdGhlIGZvbGxvd2luZyBkZXZlbG9wbWVudCBxdWVzdGlvbjonLFxuICAgIGV4cGxhaW46ICdFeHBsYWluIHRoZSBmb2xsb3dpbmcgY29kZSBpbiBkZXRhaWwsIGluY2x1ZGluZyBhbnkgcGF0dGVybnMsIHBvdGVudGlhbCBpc3N1ZXMsIGFuZCBpbXByb3ZlbWVudCBzdWdnZXN0aW9uczonLFxuICAgIGdlbmVyYXRlOiAnR2VuZXJhdGUgY29kZSBhY2NvcmRpbmcgdG8gdGhlIGZvbGxvd2luZyByZXF1ZXN0LiBNYWtlIHN1cmUgaXQgZm9sbG93cyBiZXN0IHByYWN0aWNlcyBhbmQgaXMgd2VsbCBkb2N1bWVudGVkOicsXG4gICAgaGVscDogJ1Byb3ZpZGUgZ3VpZGFuY2Ugb24gdGhlIGZvbGxvd2luZyBkZXZlbG9wbWVudCBxdWVzdGlvbjonLFxuICAgIG9wdGltaXplOiAnQW5hbHl6ZSB0aGUgZm9sbG93aW5nIGNvZGUvY29uZmlndXJhdGlvbiBhbmQgc3VnZ2VzdCBvcHRpbWl6YXRpb24gaW1wcm92ZW1lbnRzOicsXG4gICAgdGVzdDogJ0dlbmVyYXRlIGNvbXByZWhlbnNpdmUgdW5pdCB0ZXN0cyBmb3IgdGhlIGZvbGxvd2luZyBjb2RlOidcbiAgfTtcblxuICBjb25zdCB0YXNrSW5zdHJ1Y3Rpb24gPSB0YXNrSW5zdHJ1Y3Rpb25zW3Rhc2tdIHx8IHRhc2tJbnN0cnVjdGlvbnMuaGVscDtcblxuICBsZXQgZnVsbFByb21wdCA9IGAke3Rhc2tJbnN0cnVjdGlvbn1cXG5cXG4ke3Byb21wdH1gO1xuXG4gIGlmKHByb2plY3RDb250ZXh0KSB7XG4gICAgZnVsbFByb21wdCArPSBgXFxuXFxuPT09Q09OVEVYVD09PVxcbiR7cHJvamVjdENvbnRleHR9YDtcbiAgfVxuXG4gIHJldHVybiBmdWxsUHJvbXB0O1xufTtcblxuY29uc3QgZGlzcGxheVJlc3BvbnNlID0gKHJlc3BvbnNlOiBhbnksIG9wdGlvbnM6IEFJT3B0aW9ucyk6IHZvaWQgPT4ge1xuICBjb25zdCB7dGFzayA9ICdoZWxwJywgcXVpZXQgPSBmYWxzZX0gPSBvcHRpb25zO1xuXG4gIGxldCBjb250ZW50ID0gJyc7XG5cbiAgaWYodHlwZW9mIHJlc3BvbnNlID09PSAnc3RyaW5nJykge1xuICAgIGNvbnRlbnQgPSByZXNwb25zZTtcbiAgfSBlbHNlIGlmKHJlc3BvbnNlLmNob2ljZXM/LlswXT8ubWVzc2FnZT8uY29udGVudCkge1xuICAgIGNvbnN0IHtjb250ZW50OiBtZXNzYWdlQ29udGVudH0gPSByZXNwb25zZS5jaG9pY2VzWzBdLm1lc3NhZ2U7XG4gICAgY29udGVudCA9IG1lc3NhZ2VDb250ZW50O1xuICB9IGVsc2UgaWYocmVzcG9uc2UuY29udGVudCkge1xuICAgIGNvbnN0IHtjb250ZW50OiByZXNwb25zZUNvbnRlbnR9ID0gcmVzcG9uc2U7XG4gICAgY29udGVudCA9IHJlc3BvbnNlQ29udGVudDtcbiAgfSBlbHNlIHtcbiAgICBjb250ZW50ID0gJ05vIHJlc3BvbnNlIHJlY2VpdmVkIGZyb20gQUkgbW9kZWwnO1xuICB9XG5cbiAgY29uc3QgY2xlYW5lZENvbnRlbnQgPSBjbGVhblJlc3BvbnNlKGNvbnRlbnQsIG9wdGlvbnMpO1xuXG4gIHN3aXRjaCh0YXNrKSB7XG4gICAgY2FzZSAnZ2VuZXJhdGUnOlxuICAgICAgbG9nKCdcXG5HZW5lcmF0ZWQgQ29kZTpcXG4nLCAnc3VjY2VzcycsIHF1aWV0KTtcbiAgICAgIGxvZyhjbGVhbmVkQ29udGVudCwgJ2RlZmF1bHQnLCBxdWlldCk7XG4gICAgICBicmVhaztcblxuICAgIGNhc2UgJ2V4cGxhaW4nOlxuICAgICAgbG9nKCdcXG5Db2RlIEV4cGxhbmF0aW9uOlxcbicsICdzdWNjZXNzJywgcXVpZXQpO1xuICAgICAgbG9nKGNsZWFuZWRDb250ZW50LCAnZGVmYXVsdCcsIHF1aWV0KTtcbiAgICAgIGJyZWFrO1xuXG4gICAgY2FzZSAndGVzdCc6XG4gICAgICBsb2coJ1xcbkdlbmVyYXRlZCBUZXN0czpcXG4nLCAnc3VjY2VzcycsIHF1aWV0KTtcbiAgICAgIGxvZyhjbGVhbmVkQ29udGVudCwgJ2RlZmF1bHQnLCBxdWlldCk7XG4gICAgICBicmVhaztcblxuICAgIGNhc2UgJ29wdGltaXplJzpcbiAgICAgIGxvZygnXFxuT3B0aW1pemF0aW9uIFN1Z2dlc3Rpb25zOlxcbicsICdzdWNjZXNzJywgcXVpZXQpO1xuICAgICAgbG9nKGNsZWFuZWRDb250ZW50LCAnZGVmYXVsdCcsIHF1aWV0KTtcbiAgICAgIGJyZWFrO1xuXG4gICAgZGVmYXVsdDpcbiAgICAgIGxvZygnXFxuQUkgUmVzcG9uc2U6XFxuJywgJ3N1Y2Nlc3MnLCBxdWlldCk7XG4gICAgICBsb2coY2xlYW5lZENvbnRlbnQsICdkZWZhdWx0JywgcXVpZXQpO1xuICAgICAgYnJlYWs7XG4gIH1cbn07XG5cbmNvbnN0IGNsZWFuUmVzcG9uc2UgPSAoY29udGVudDogc3RyaW5nLCBvcHRpb25zOiBBSU9wdGlvbnMpOiBzdHJpbmcgPT4ge1xuICBjb25zdCB7cHJvbXB0ID0gJycsIHRhc2sgPSAnaGVscCd9ID0gb3B0aW9ucztcblxuICBpZighY29udGVudCkge1xuICAgIHJldHVybiBjb250ZW50O1xuICB9XG5cbiAgbGV0IGNsZWFuZWRDb250ZW50ID0gY29udGVudDtcblxuICBjb25zdCB0YXNrSW5zdHJ1Y3Rpb25zOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+ID0ge1xuICAgIGFuYWx5emU6ICdBbmFseXplIHRoZSBmb2xsb3dpbmcgY29kZTonLFxuICAgIGFzazogJ1Byb3ZpZGUgZ3VpZGFuY2Ugb24gdGhlIGZvbGxvd2luZyBkZXZlbG9wbWVudCBxdWVzdGlvbjonLFxuICAgIGV4cGxhaW46ICdFeHBsYWluIHRoZSBmb2xsb3dpbmcgY29kZSBpbiBkZXRhaWwsIGluY2x1ZGluZyBhbnkgcGF0dGVybnMsIHBvdGVudGlhbCBpc3N1ZXMsIGFuZCBpbXByb3ZlbWVudCBzdWdnZXN0aW9uczonLFxuICAgIGdlbmVyYXRlOiAnR2VuZXJhdGUgY29kZSBhY2NvcmRpbmcgdG8gdGhlIGZvbGxvd2luZyByZXF1ZXN0LiBNYWtlIHN1cmUgaXQgZm9sbG93cyBiZXN0IHByYWN0aWNlcyBhbmQgaXMgd2VsbCBkb2N1bWVudGVkOicsXG4gICAgaGVscDogJ1Byb3ZpZGUgZ3VpZGFuY2Ugb24gdGhlIGZvbGxvd2luZyBkZXZlbG9wbWVudCBxdWVzdGlvbjonLFxuICAgIG9wdGltaXplOiAnQW5hbHl6ZSB0aGUgZm9sbG93aW5nIGNvZGUvY29uZmlndXJhdGlvbiBhbmQgc3VnZ2VzdCBvcHRpbWl6YXRpb24gaW1wcm92ZW1lbnRzOicsXG4gICAgdGVzdDogJ0dlbmVyYXRlIGNvbXByZWhlbnNpdmUgdW5pdCB0ZXN0cyBmb3IgdGhlIGZvbGxvd2luZyBjb2RlOidcbiAgfTtcblxuICBjb25zdCBpbnN0cnVjdGlvbiA9IHRhc2tJbnN0cnVjdGlvbnNbdGFza10gfHwgJyc7XG5cbiAgaWYoaW5zdHJ1Y3Rpb24gJiYgY2xlYW5lZENvbnRlbnQuaW5jbHVkZXMoaW5zdHJ1Y3Rpb24pKSB7XG4gICAgY2xlYW5lZENvbnRlbnQgPSBjbGVhbmVkQ29udGVudC5yZXBsYWNlKGluc3RydWN0aW9uLCAnJykudHJpbSgpO1xuICB9XG5cbiAgaWYocHJvbXB0ICYmIGNsZWFuZWRDb250ZW50LmluY2x1ZGVzKHByb21wdCkpIHtcbiAgICBjbGVhbmVkQ29udGVudCA9IGNsZWFuZWRDb250ZW50LnJlcGxhY2UocHJvbXB0LCAnJykudHJpbSgpO1xuICB9XG5cbiAgaWYoY2xlYW5lZENvbnRlbnQuaW5jbHVkZXMoJz09PUNPTlRFWFQ9PT0nKSkge1xuICAgIGNsZWFuZWRDb250ZW50ID0gY2xlYW5lZENvbnRlbnQuc3BsaXQoJz09PUNPTlRFWFQ9PT0nKVswXS50cmltKCk7XG4gIH1cblxuICBpZighY2xlYW5lZENvbnRlbnQpIHtcbiAgICByZXR1cm4gY29udGVudDtcbiAgfVxuXG4gIHJldHVybiBjbGVhbmVkQ29udGVudDtcbn07XG5cbmNvbnN0IGdldFByb3ZpZGVyQXV0aCA9IChwcm92aWRlcjogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkID0+IHtcbiAgaWYocHJvY2Vzcy5jd2QoKS5pbmNsdWRlcygncmVha3RvcicpKSB7XG4gICAgcmV0dXJuICdjdXJzb3ItYXV0aCc7XG4gIH1cblxuICBpZihwcm9jZXNzLmVudi5BSV9BUElfS0VZKSB7XG4gICAgcmV0dXJuIHByb2Nlc3MuZW52LkFJX0FQSV9LRVk7XG4gIH1cblxuICBpZihwcm92aWRlciA9PT0gJ25vbmUnICYmIHByb2Nlc3MuZW52LkNVUlNPUl9JREUgPT09ICd0cnVlJykge1xuICAgIHJldHVybiAnY3Vyc29yLWF1dGgnO1xuICB9XG5cbiAgc3dpdGNoKHByb3ZpZGVyKSB7XG4gICAgY2FzZSAnb3BlbmFpJzpcbiAgICAgIHJldHVybiBwcm9jZXNzLmVudi5PUEVOQUlfQVBJX0tFWTtcbiAgICBjYXNlICdhbnRocm9waWMnOlxuICAgICAgcmV0dXJuIHByb2Nlc3MuZW52LkFOVEhST1BJQ19BUElfS0VZO1xuICAgIGNhc2UgJ2N1cnNvcic6XG4gICAgICByZXR1cm4gJ2N1cnNvci1hdXRoJztcbiAgICBjYXNlICdjb3BpbG90JzpcbiAgICAgIHJldHVybiBwcm9jZXNzLmVudi5HSVRIVUJfVE9LRU47XG4gICAgY2FzZSAnbm9uZSc6XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59O1xuXG5jb25zdCBkZXRlY3RDdXJzb3JJREUgPSAoKTogYm9vbGVhbiA9PiB7XG4gIGlmKHByb2Nlc3MuZW52LkNVUlNPUl9JREUgPT09ICd0cnVlJykge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgY29uc3QgcG9zc2libGVDdXJzb3JTaWduYWxzID0gW1xuICAgIHByb2Nlc3MuZW52LkNVUlNPUl9FWFRFTlNJT04gPT09ICd0cnVlJyxcbiAgICBwcm9jZXNzLmVudi5DVVJTT1JfVEVSTUlOQUwgPT09ICd0cnVlJyxcbiAgICBwcm9jZXNzLmVudi5DVVJTT1JfQVBQID09PSAndHJ1ZScsXG4gICAgcHJvY2Vzcy5lbnYuUEFUSD8uaW5jbHVkZXMoJ2N1cnNvcicpLFxuICAgICEhcHJvY2Vzcy5lbnYuQ1VSU09SX1NFU1NJT05fSURcbiAgXTtcblxuICBjb25zdCBpc0N1cnNvcklERSA9IHBvc3NpYmxlQ3Vyc29yU2lnbmFscy5zb21lKChzaWduYWwpID0+IHNpZ25hbCk7XG5cbiAgaWYoaXNDdXJzb3JJREUpIHtcbiAgICBwcm9jZXNzLmVudi5DVVJTT1JfSURFID0gJ3RydWUnO1xuICB9XG5cbiAgcmV0dXJuIGlzQ3Vyc29ySURFO1xufTtcblxuZXhwb3J0IGNvbnN0IGFpRnVuY3Rpb24gPSBhc3luYyAob3B0aW9uczogQUlPcHRpb25zKTogUHJvbWlzZTxhbnk+ID0+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBjb25maWcgPSBMZXhDb25maWcuY29uZmlnIHx8IHt9O1xuICAgIGNvbnN0IGFpQ29uZmlnID0gY29uZmlnLmFpIHx8IHt9O1xuICAgIGNvbnN0IHByb3ZpZGVyID0gb3B0aW9ucy5wcm92aWRlciB8fCBhaUNvbmZpZy5wcm92aWRlciB8fCAnbm9uZSc7XG5cbiAgICBpZihwcm92aWRlciA9PT0gJ25vbmUnICYmICFwcm9jZXNzLmVudi5DVVJTT1JfRVhURU5TSU9OKSB7XG4gICAgICBsb2coYCR7Y2hhbGsucmVkKCdFcnJvcjonKX0gTm8gQUkgcHJvdmlkZXIgY29uZmlndXJlZC5gLCAnZXJyb3InKTtcbiAgICAgIHJldHVybiB7ZXJyb3I6ICdObyBBSSBwcm92aWRlciBjb25maWd1cmVkJ307XG4gICAgfVxuXG4gICAgY29uc3QgdGFzayA9IG9wdGlvbnMudGFzayB8fCAnYXNrJztcbiAgICBjb25zdCB2YWxpZFRhc2tzID0gWydleHBsYWluJywgJ2dlbmVyYXRlJywgJ3Rlc3QnLCAnYW5hbHl6ZScsICdhc2snXTtcblxuICAgIGlmKCF2YWxpZFRhc2tzLmluY2x1ZGVzKHRhc2spKSB7XG4gICAgICBsb2coYCR7Y2hhbGsucmVkKCdFcnJvcjonKX0gSW52YWxpZCB0YXNrIFwiJHt0YXNrfVwiLiBWYWxpZCB0YXNrcyBhcmU6ICR7dmFsaWRUYXNrcy5qb2luKCcsICcpfWAsICdlcnJvcicpO1xuICAgICAgcmV0dXJuIHtlcnJvcjogYEludmFsaWQgdGFzayBcIiR7dGFza31cImB9O1xuICAgIH1cblxuICAgIGNvbnN0IHtwcm9tcHR9ID0gb3B0aW9ucztcblxuICAgIGlmKCFwcm9tcHQpIHtcbiAgICAgIGxvZyhgJHtjaGFsay5yZWQoJ0Vycm9yOicpfSBObyBwcm9tcHQgcHJvdmlkZWQuIFVzZSAtLXByb21wdCBcIllvdXIgcHJvbXB0IGhlcmVcImAsICdlcnJvcicpO1xuICAgICAgcmV0dXJuIHtlcnJvcjogJ05vIHByb21wdCBwcm92aWRlZCd9O1xuICAgIH1cblxuICAgIGxldCBjb250ZXh0ID0gJyc7XG5cbiAgICBpZihvcHRpb25zLmZpbGUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IGZzID0gYXdhaXQgaW1wb3J0KCdmcy9wcm9taXNlcycpO1xuICAgICAgICBjb25zdCBnbG9iID0gYXdhaXQgaW1wb3J0KCdnbG9iJyk7XG4gICAgICAgIGNvbnN0IGZpbGVzID0gYXdhaXQgZ2xvYi5nbG9iKG9wdGlvbnMuZmlsZSk7XG5cbiAgICAgICAgaWYoZmlsZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgICAgbG9nKGAke2NoYWxrLnllbGxvdygnV2FybmluZzonKX0gTm8gZmlsZXMgZm91bmQgbWF0Y2hpbmcgXCIke29wdGlvbnMuZmlsZX1cImAsICd3YXJuaW5nJyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgZm9yKGNvbnN0IGZpbGUgb2YgZmlsZXMpIHtcbiAgICAgICAgICAgIGNvbnN0IGNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZShmaWxlLCAndXRmOCcpO1xuICAgICAgICAgICAgY29udGV4dCArPSBgXFxuPT09RklMRTogJHtmaWxlfT09PVxcbiR7Y29udGVudH1cXG5gO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfSBjYXRjaChlcnJvcikge1xuICAgICAgICBsb2coYCR7Y2hhbGsueWVsbG93KCdXYXJuaW5nOicpfSBFcnJvciByZWFkaW5nIGZpbGU6ICR7ZXJyb3IubWVzc2FnZX1gLCAnd2FybmluZycpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGlmKG9wdGlvbnMuZGlyKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCB7ZXhlY2FTeW5jfSA9IGF3YWl0IGltcG9ydCgnZXhlY2EnKTtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gZXhlY2FTeW5jKCdmaW5kJywgW29wdGlvbnMuZGlyLCAnLXR5cGUnLCAnZicsICd8JywgJ3NvcnQnXSk7XG4gICAgICAgIGNvbnRleHQgKz0gYFxcbj09PVByb2plY3Qgc3RydWN0dXJlOj09PVxcbiR7cmVzdWx0LnN0ZG91dH1cXG5gO1xuICAgICAgfSBjYXRjaChlcnJvcikge1xuICAgICAgICBsb2coYCR7Y2hhbGsueWVsbG93KCdXYXJuaW5nOicpfSBFcnJvciByZWFkaW5nIGRpcmVjdG9yeTogJHtlcnJvci5tZXNzYWdlfWAsICd3YXJuaW5nJyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbGV0IGZvcm1hdHRlZFByb21wdCA9ICcnO1xuXG4gICAgc3dpdGNoKHRhc2spIHtcbiAgICAgIGNhc2UgJ2V4cGxhaW4nOlxuICAgICAgICBmb3JtYXR0ZWRQcm9tcHQgPSBgRXhwbGFpbiB0aGUgZm9sbG93aW5nIGNvZGU6XFxuJHtwcm9tcHR9YDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdnZW5lcmF0ZSc6XG4gICAgICAgIGZvcm1hdHRlZFByb21wdCA9IGBHZW5lcmF0ZSBjb2RlIGFjY29yZGluZyB0byB0aGUgZm9sbG93aW5nIHJlcXVlc3Q6XFxuJHtwcm9tcHR9YDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd0ZXN0JzpcbiAgICAgICAgZm9ybWF0dGVkUHJvbXB0ID0gYEdlbmVyYXRlIGNvbXByZWhlbnNpdmUgdW5pdCB0ZXN0czpcXG4ke3Byb21wdH1gO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ2FuYWx5emUnOlxuICAgICAgICBmb3JtYXR0ZWRQcm9tcHQgPSBgQW5hbHl6ZSB0aGUgZm9sbG93aW5nIGNvZGU6XFxuJHtwcm9tcHR9YDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdhc2snOlxuICAgICAgICBmb3JtYXR0ZWRQcm9tcHQgPSBgUHJvdmlkZSBndWlkYW5jZSBvbiB0aGUgZm9sbG93aW5nIGRldmVsb3BtZW50IHF1ZXN0aW9uOlxcbiR7cHJvbXB0fWA7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIGlmKGNvbnRleHQpIHtcbiAgICAgIGZvcm1hdHRlZFByb21wdCArPSBgXFxuPT09Q09OVEVYVD09PVxcbiR7Y29udGV4dH1gO1xuICAgIH1cblxuICAgIGlmKChwcm92aWRlciA9PT0gJ2N1cnNvcicgfHwgcHJvY2Vzcy5lbnYuQ1VSU09SX0VYVEVOU0lPTikgJiYgdGFzayA9PT0gJ2dlbmVyYXRlJykge1xuICAgICAgbG9nKCdVc2luZyBDdXJzb3IgSURFIGZvciBjb2RlIGdlbmVyYXRpb24uLi4nLCAnaW5mbycpO1xuICAgICAgbG9nKCdOb3RlOiBGb3IgZnVsbCBjb2RlIGdlbmVyYXRpb24gY2FwYWJpbGl0aWVzLCBwbGVhc2UgdXNlIEN1cnNvciBJREUgZGlyZWN0bHkgd2l0aCBDbWQrTCBvciBDbWQrSy4nLCAnaW5mbycpO1xuICAgICAgbG9nKCdUaGUgQ0xJIGludGVncmF0aW9uIGhhcyBsaW1pdGVkIGNhcGFiaWxpdGllcyBmb3IgY29kZSBnZW5lcmF0aW9uLicsICd3YXJuaW5nJyk7XG4gICAgfSBlbHNlIGlmKHByb3ZpZGVyID09PSAnY3Vyc29yJyB8fCBwcm9jZXNzLmVudi5DVVJTT1JfRVhURU5TSU9OKSB7XG4gICAgICBsb2coJ1VzaW5nIEN1cnNvciBJREUgZm9yIEFJIGFzc2lzdGFuY2UuLi4nLCAnaW5mbycpO1xuICAgICAgbG9nKCdOb3RlOiBUaGlzIGlzIGEgbGltaXRlZCBpbnRlZ3JhdGlvbi4gRm9yIGZ1bGwgQUkgY2FwYWJpbGl0aWVzLCB1c2UgQ3Vyc29yIElERSBkaXJlY3RseS4nLCAnaW5mbycpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2coYFVzaW5nICR7cHJvdmlkZXJ9IGZvciBBSSBhc3Npc3RhbmNlLi4uYCwgJ2luZm8nKTtcbiAgICB9XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNhbGxBSVNlcnZpY2UoZm9ybWF0dGVkUHJvbXB0LCBvcHRpb25zLnF1aWV0IHx8IGZhbHNlKTtcblxuICAgIGxvZyhgXFxuJHtyZXNwb25zZX1gLCAnc3VjY2VzcycpO1xuXG4gICAgcmV0dXJuIHtyZXNwb25zZX07XG4gIH0gY2F0Y2goZXJyb3IpIHtcbiAgICBsb2coYCR7Y2hhbGsucmVkKCdFcnJvcjonKX0gJHtlcnJvci5tZXNzYWdlfWAsICdlcnJvcicpO1xuICAgIHJldHVybiB7ZXJyb3I6IGVycm9yLm1lc3NhZ2V9O1xuICB9XG59O1xuXG5leHBvcnQgY29uc3QgYWkgPSBuZXcgQ29tbWFuZCgnYWknKVxuICAuZGVzY3JpcHRpb24oJ1VzZSBBSSB0byBoZWxwIHdpdGggZGV2ZWxvcG1lbnQgdGFza3MnKVxuICAub3B0aW9uKCctLXByb3ZpZGVyIDxwcm92aWRlcj4nLCAnQUkgcHJvdmlkZXIgdG8gdXNlIChvcGVuYWksIGFudGhyb3BpYywgY3Vyc29yKScpXG4gIC5vcHRpb24oJy0tdGFzayA8dGFzaz4nLCAnVGFzayB0byBwZXJmb3JtIChleHBsYWluLCBnZW5lcmF0ZSwgdGVzdCwgYW5hbHl6ZSwgYXNrKScpXG4gIC5vcHRpb24oJy0tcHJvbXB0IDxwcm9tcHQ+JywgJ1Byb21wdCB0byBzZW5kIHRvIEFJJylcbiAgLm9wdGlvbignLS1maWxlIDxmaWxlPicsICdGaWxlIHRvIGFuYWx5emUnKVxuICAub3B0aW9uKCctLWRpciA8ZGlyPicsICdEaXJlY3RvcnkgdG8gYW5hbHl6ZScpXG4gIC5hY3Rpb24oYXN5bmMgKG9wdGlvbnM6IEFJT3B0aW9ucykgPT4ge1xuICAgIGF3YWl0IGFpRnVuY3Rpb24ob3B0aW9ucyk7XG4gIH0pO1xuXG5leHBvcnQgZGVmYXVsdCBhaTsiXSwibmFtZXMiOlsiY2hhbGsiLCJDb21tYW5kIiwicmVhZEZpbGVTeW5jIiwic3luYyIsImdsb2JTeW5jIiwiTGV4Q29uZmlnIiwiY2FsbEFJU2VydmljZSIsImxvZyIsInByb2Nlc3MiLCJlbnYiLCJDVVJTT1JfRVhURU5TSU9OIiwiQ1VSU09SX1RFUk1JTkFMIiwiQ1VSU09SX0FQUCIsIlBBVEgiLCJpbmNsdWRlcyIsIkNVUlNPUl9TRVNTSU9OX0lEIiwiQ1VSU09SX0lERSIsImdldEZpbGVDb250ZXh0IiwiZmlsZVBhdGgiLCJjb250ZW50IiwiX2Vycm9yIiwiZ2V0UHJvamVjdENvbnRleHQiLCJvcHRpb25zIiwiZmlsZSIsInRhc2siLCJjb250ZXh0IiwicHJvamVjdENvbnRleHQiLCJmaWxlcyIsImN3ZCIsImlnbm9yZSIsIm1heERlcHRoIiwiam9pbiIsInRlc3RDb25maWciLCJ3ZWJwYWNrQ29uZmlnIiwiY29uc3RydWN0UHJvbXB0IiwicHJvbXB0IiwidGFza0luc3RydWN0aW9ucyIsImFuYWx5emUiLCJhc2siLCJleHBsYWluIiwiZ2VuZXJhdGUiLCJoZWxwIiwib3B0aW1pemUiLCJ0ZXN0IiwidGFza0luc3RydWN0aW9uIiwiZnVsbFByb21wdCIsImRpc3BsYXlSZXNwb25zZSIsInJlc3BvbnNlIiwicXVpZXQiLCJjaG9pY2VzIiwibWVzc2FnZSIsIm1lc3NhZ2VDb250ZW50IiwicmVzcG9uc2VDb250ZW50IiwiY2xlYW5lZENvbnRlbnQiLCJjbGVhblJlc3BvbnNlIiwiaW5zdHJ1Y3Rpb24iLCJyZXBsYWNlIiwidHJpbSIsInNwbGl0IiwiZ2V0UHJvdmlkZXJBdXRoIiwicHJvdmlkZXIiLCJBSV9BUElfS0VZIiwiT1BFTkFJX0FQSV9LRVkiLCJBTlRIUk9QSUNfQVBJX0tFWSIsIkdJVEhVQl9UT0tFTiIsInVuZGVmaW5lZCIsImRldGVjdEN1cnNvcklERSIsInBvc3NpYmxlQ3Vyc29yU2lnbmFscyIsImlzQ3Vyc29ySURFIiwic29tZSIsInNpZ25hbCIsImFpRnVuY3Rpb24iLCJjb25maWciLCJhaUNvbmZpZyIsImFpIiwicmVkIiwiZXJyb3IiLCJ2YWxpZFRhc2tzIiwiZnMiLCJnbG9iIiwibGVuZ3RoIiwieWVsbG93IiwicmVhZEZpbGUiLCJkaXIiLCJleGVjYVN5bmMiLCJyZXN1bHQiLCJzdGRvdXQiLCJmb3JtYXR0ZWRQcm9tcHQiLCJkZXNjcmlwdGlvbiIsIm9wdGlvbiIsImFjdGlvbiJdLCJtYXBwaW5ncyI6IkFBQUE7OztDQUdDLEdBQ0QsT0FBT0EsV0FBVyxRQUFRO0FBQzFCLFNBQVFDLE9BQU8sUUFBTyxZQUFZO0FBQ2xDLFNBQVFDLFlBQVksUUFBTyxLQUFLO0FBQ2hDLFNBQVFDLFFBQVFDLFFBQVEsUUFBTyxPQUFPO0FBRXRDLFNBQVFDLFNBQVMsUUFBTyxxQkFBcUI7QUFDN0MsU0FBUUMsYUFBYSxRQUFPLDJCQUEyQjtBQUN2RCxTQUFRQyxHQUFHLFFBQU8scUJBQXFCO0FBRXZDLElBQUdDLFFBQVFDLEdBQUcsQ0FBQ0MsZ0JBQWdCLEtBQUssVUFDbENGLFFBQVFDLEdBQUcsQ0FBQ0UsZUFBZSxLQUFLLFVBQ2hDSCxRQUFRQyxHQUFHLENBQUNHLFVBQVUsS0FBSyxVQUMzQkosUUFBUUMsR0FBRyxDQUFDSSxJQUFJLEVBQUVDLFNBQVMsYUFDM0JOLFFBQVFDLEdBQUcsQ0FBQ00saUJBQWlCLEVBQUU7SUFDL0JQLFFBQVFDLEdBQUcsQ0FBQ08sVUFBVSxHQUFHO0FBQzNCO0FBZ0JBLE1BQU1DLGlCQUFpQixDQUFDQztJQUN0QixJQUFJO1FBQ0YsTUFBTUMsVUFBVWpCLGFBQWFnQixVQUFVO1FBQ3ZDLE9BQU8sQ0FBQyxNQUFNLEVBQUVBLFNBQVMsSUFBSSxFQUFFQyxTQUFTO0lBQzFDLEVBQUUsT0FBTUMsUUFBUTtRQUNkLE9BQU8sQ0FBQyxvQkFBb0IsRUFBRUYsVUFBVTtJQUMxQztBQUNGO0FBRUEs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