UNPKG

@nlabs/lex

Version:
300 lines (288 loc) 32.1 kB
import { existsSync, readFileSync, writeFileSync } from "fs"; import { resolve as pathResolve } from "path"; import readline from "readline"; import { LexConfig } from "../LexConfig.js"; import { createSpinner } from "./app.js"; import { log } from "./log.js"; const callCursorAI = async (prompt, _options) => { try { log("Using Cursor IDE for AI fixes...", "info"); log("AI fix requested via Cursor IDE", "info"); const taskMatch = prompt.match(/^(Generate code according to the following request|Explain the following code|Generate comprehensive unit tests|Analyze the following code|Provide guidance on the following development question):/); const task = taskMatch ? taskMatch[1] : ""; const isGenerateTask = task.startsWith("Generate code"); const questionMatch = prompt.match(/(?:Generate code according to the following request|Explain the following code|Generate comprehensive unit tests|Analyze the following code|Provide guidance on the following development question):\s*([\s\S]+?)(?:===CONTEXT===|$)/); const question = questionMatch ? questionMatch[1].trim() : prompt; if (question.toLowerCase().includes("how many files") && prompt.includes("Project structure:")) { const projectStructure = prompt.split("Project structure:")[1] || ""; const files = projectStructure.trim().split("\n"); return `Based on the project structure provided, there are ${files.length} files in the project.`; } if (isGenerateTask) { return ` # Code Generation Request: "${question}" To generate code using Cursor's AI capabilities: 1. **Open your project in Cursor IDE** (https://cursor.sh) 2. Press **Cmd+L** (or Ctrl+L on Windows/Linux) to open the AI chat 3. Type your request: "${question}" 4. Cursor will generate the code directly in your editor The current CLI integration doesn't have direct access to Cursor's code generation capabilities. **Alternative options:** 1. **Use OpenAI or Anthropic directly:** Configure in your lex.config file: \`\`\`js export default { ai: { provider: 'openai', apiKey: process.env.OPENAI_API_KEY, model: 'gpt-4o' } } \`\`\` 2. **Use Cursor's command line tool:** Install: \`npm install -g @cursor/cli\` Run: \`cursor ai "${question}"\` `; } return ` To use Cursor's AI capabilities for "${question}", you need to: 1. Open your project in Cursor IDE (https://cursor.sh) 2. Use Cursor's built-in AI features by pressing Cmd+K or Cmd+L 3. Or run the 'cursor' command directly from your terminal The current integration is limited and doesn't directly access Cursor's AI capabilities. For the best experience with AI code generation: - Use Cursor IDE directly - Or configure OpenAI or Anthropic as your provider in your lex.config file: \`\`\`js // lex.config.js (or lex.config.mjs, lex.config.cjs, etc.) export default { ai: { provider: 'openai', // or 'anthropic' apiKey: process.env.OPENAI_API_KEY, // or ANTHROPIC_API_KEY model: 'gpt-4o' // or 'claude-3-opus' } } \`\`\` Then set your API key as an environment variable: \`\`\` export OPENAI_API_KEY=your_key_here \`\`\` `; } catch (error) { throw new Error(`Cursor AI error: ${error.message}`); } }; const callOpenAIAI = async (prompt, options) => { try { const apiKey = options.apiKey || process.env.OPENAI_API_KEY; if (!apiKey) { throw new Error("OpenAI API key is required. Set it in your lex.config file or as OPENAI_API_KEY environment variable."); } const response = await fetch("https://api.openai.com/v1/chat/completions", { body: JSON.stringify({ max_tokens: options.maxTokens || 4e3, messages: [ { content: "You are a helpful assistant that fixes ESLint errors in code.", role: "system" }, { content: prompt, role: "user" } ], model: options.model || "gpt-4o", temperature: options.temperature || 0.1 }), headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" }, method: "POST" }); if (!response.ok) { const error = await response.json(); throw new Error(`OpenAI API error: ${error.error?.message || response.statusText}`); } const data = await response.json(); return data.choices[0].message.content; } catch (error) { throw new Error(`OpenAI AI error: ${error.message}`); } }; const callAnthropicAI = async (prompt, options) => { try { const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY; if (!apiKey) { throw new Error("Anthropic API key is required. Set it in your lex.config file or as ANTHROPIC_API_KEY environment variable."); } const response = await fetch("https://api.anthropic.com/v1/messages", { body: JSON.stringify({ max_tokens: options.maxTokens || 4e3, messages: [ { content: prompt, role: "user" } ], model: options.model || "claude-3-sonnet-20240229", temperature: options.temperature || 0.1 }), headers: { "Content-Type": "application/json", "anthropic-version": "2023-06-01", "x-api-key": apiKey }, method: "POST" }); if (!response.ok) { const error = await response.json(); throw new Error(`Anthropic API error: ${error.error?.message || response.statusText}`); } const data = await response.json(); return data.content[0].text; } catch (error) { throw new Error(`Anthropic AI error: ${error.message}`); } }; const callCopilotAI = async (prompt, _options) => { try { log("GitHub Copilot AI fixes not directly supported. Using manual fix mode.", "info"); return prompt; } catch (error) { throw new Error(`GitHub Copilot AI error: ${error.message}`); } }; const promptForAIProvider = async (_quiet = false) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { log("\nNo AI provider configured. Please choose an AI provider:", "info"); log("1. Cursor IDE", "info"); log("2. OpenAI", "info"); log("3. Anthropic", "info"); log("4. GitHub Copilot", "info"); log("5. None (Skip AI features)", "info"); rl.question("Enter your choice (1-5): ", (answer) => { rl.close(); switch (answer) { case "1": resolve("cursor"); break; case "2": resolve("openai"); break; case "3": resolve("anthropic"); break; case "4": resolve("copilot"); break; default: resolve("none"); } }); }); }; const promptForAPIKey = async (provider, _quiet = false) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise((resolve) => { rl.question(`Please enter your ${provider} API key: `, (answer) => { rl.close(); resolve(answer); }); }); }; const getAIService = (provider, _options) => { switch (provider) { case "cursor": return callCursorAI; case "openai": return callOpenAIAI; case "anthropic": return callAnthropicAI; case "copilot": return callCopilotAI; default: return async () => "No AI provider configured"; } }; const callAIService = async (prompt, quiet = false) => { const spinner = createSpinner(quiet); spinner.start("Calling AI service to fix code issues..."); try { const aiConfig = LexConfig.config.ai || { provider: "none" }; const isInCursorIDE = process.env.CURSOR_IDE === "true"; if (isInCursorIDE && (aiConfig.provider === "none" || !aiConfig.provider)) { log("Detected Cursor IDE environment, using Cursor as AI provider", "info", quiet); aiConfig.provider = "cursor"; } if (aiConfig.provider === "none") { const provider = await promptForAIProvider(quiet); if (provider === "none") { spinner.fail("AI features skipped"); return ""; } aiConfig.provider = provider; if (provider !== "cursor" && provider !== "copilot" && !process.env[`${provider.toUpperCase()}_API_KEY`]) { aiConfig.apiKey = await promptForAPIKey(provider, quiet); } LexConfig.config.ai = aiConfig; const configFormats = ["js", "mjs", "cjs", "ts", "json"]; const configBaseName = "lex.config"; let configPath = ""; for (const format of configFormats) { const potentialPath = pathResolve(process.cwd(), `./${configBaseName}.${format}`); if (existsSync(potentialPath)) { configPath = potentialPath; break; } } if (configPath) { try { const configContent = readFileSync(configPath, "utf8"); const updatedConfig = configContent.replace( /ai:.*?[,}]/s, `ai: { provider: '${aiConfig.provider}' },` ); writeFileSync(configPath, updatedConfig); } catch (_error) { } } } let result = ""; switch (aiConfig.provider) { case "cursor": result = await callCursorAI(prompt, aiConfig); log("Cursor IDE AI integration active", "info", quiet); break; case "openai": result = await callOpenAIAI(prompt, aiConfig); break; case "anthropic": result = await callAnthropicAI(prompt, aiConfig); break; case "copilot": result = await callCopilotAI(prompt, aiConfig); break; default: spinner.fail("No AI provider configured"); return ""; } spinner.succeed("AI code fixes generated successfully"); return result; } catch (error) { spinner.fail(`AI service error: ${error.message}`); if (!quiet) { log(error, "error"); } return ""; } }; export { callAIService, callAnthropicAI, callCopilotAI, callCursorAI, callOpenAIAI, getAIService, promptForAIProvider, promptForAPIKey }; //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL3V0aWxzL2FpU2VydmljZS50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIDIwMjItUHJlc2VudCwgTml0cm9nZW4gTGFicywgSW5jLlxuICogQ29weXJpZ2h0cyBsaWNlbnNlZCB1bmRlciB0aGUgTUlUIExpY2Vuc2UuIFNlZSB0aGUgYWNjb21wYW55aW5nIExJQ0VOU0UgZmlsZSBmb3IgdGVybXMuXG4gKi9cbmltcG9ydCB7ZXhpc3RzU3luYywgcmVhZEZpbGVTeW5jLCB3cml0ZUZpbGVTeW5jfSBmcm9tICdmcyc7XG5pbXBvcnQge3Jlc29sdmUgYXMgcGF0aFJlc29sdmV9IGZyb20gJ3BhdGgnO1xuaW1wb3J0IHJlYWRsaW5lIGZyb20gJ3JlYWRsaW5lJztcblxuaW1wb3J0IHtBSUNvbmZpZywgTGV4Q29uZmlnfSBmcm9tICcuLi9MZXhDb25maWcuanMnO1xuaW1wb3J0IHtjcmVhdGVTcGlubmVyfSBmcm9tICcuL2FwcC5qcyc7XG5pbXBvcnQge2xvZ30gZnJvbSAnLi9sb2cuanMnO1xuXG4vLyBDdXJzb3IgSURFIGludGVncmF0aW9uXG5leHBvcnQgY29uc3QgY2FsbEN1cnNvckFJID0gYXN5bmMgKHByb21wdDogc3RyaW5nLCBfb3B0aW9uczogQUlDb25maWcpOiBQcm9taXNlPHN0cmluZz4gPT4ge1xuICB0cnkge1xuICAgIC8vIFdoZW4gcnVubmluZyB3aXRoaW4gQ3Vyc29yIElERSwgd2UgY2FuIHdyaXRlIHRoZSBwcm9tcHQgdG8gYSB0ZW1wb3JhcnkgZmlsZVxuICAgIC8vIHRoYXQgQ3Vyc29yIGNhbiB1c2UgdG8gcHJvdmlkZSBBSSBhc3Npc3RhbmNlXG4gICAgbG9nKCdVc2luZyBDdXJzb3IgSURFIGZvciBBSSBmaXhlcy4uLicsICdpbmZvJyk7XG5cbiAgICAvLyBGb3Igbm93LCBqdXN0IGxvZyB0aGUgcHJvbXB0IGFuZCByZXR1cm4gYSBwbGFjZWhvbGRlclxuICAgIC8vIEluIGEgcmVhbCBpbXBsZW1lbnRhdGlvbiwgQ3Vyc29yIHdvdWxkIGhhbmRsZSB0aGlzIGF1dG9tYXRpY2FsbHlcbiAgICBsb2coJ0FJIGZpeCByZXF1ZXN0ZWQgdmlhIEN1cnNvciBJREUnLCAnaW5mbycpO1xuXG4gICAgY29uc3QgdGFza01hdGNoID0gcHJvbXB0Lm1hdGNoKC9eKEdlbmVyYXRlIGNvZGUgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgcmVxdWVzdHxFeHBsYWluIHRoZSBmb2xsb3dpbmcgY29kZXxHZW5lcmF0ZSBjb21wcmVoZW5zaXZlIHVuaXQgdGVzdHN8QW5hbHl6ZSB0aGUgZm9sbG93aW5nIGNvZGV8UHJvdmlkZSBndWlkYW5jZSBvbiB0aGUgZm9sbG93aW5nIGRldmVsb3BtZW50IHF1ZXN0aW9uKTovKTtcbiAgICBjb25zdCB0YXNrID0gdGFza01hdGNoID8gdGFza01hdGNoWzFdIDogJyc7XG4gICAgY29uc3QgaXNHZW5lcmF0ZVRhc2sgPSB0YXNrLnN0YXJ0c1dpdGgoJ0dlbmVyYXRlIGNvZGUnKTtcblxuICAgIGNvbnN0IHF1ZXN0aW9uTWF0Y2ggPSBwcm9tcHQubWF0Y2goLyg/OkdlbmVyYXRlIGNvZGUgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgcmVxdWVzdHxFeHBsYWluIHRoZSBmb2xsb3dpbmcgY29kZXxHZW5lcmF0ZSBjb21wcmVoZW5zaXZlIHVuaXQgdGVzdHN8QW5hbHl6ZSB0aGUgZm9sbG93aW5nIGNvZGV8UHJvdmlkZSBndWlkYW5jZSBvbiB0aGUgZm9sbG93aW5nIGRldmVsb3BtZW50IHF1ZXN0aW9uKTpcXHMqKFtcXHNcXFNdKz8pKD86PT09Q09OVEVYVD09PXwkKS8pO1xuICAgIGNvbnN0IHF1ZXN0aW9uID0gcXVlc3Rpb25NYXRjaCA/IHF1ZXN0aW9uTWF0Y2hbMV0udHJpbSgpIDogcHJvbXB0O1xuXG4gICAgaWYocXVlc3Rpb24udG9Mb3dlckNhc2UoKS5pbmNsdWRlcygnaG93IG1hbnkgZmlsZXMnKSAmJiBwcm9tcHQuaW5jbHVkZXMoJ1Byb2plY3Qgc3RydWN0dXJlOicpKSB7XG4gICAgICBjb25zdCBwcm9qZWN0U3RydWN0dXJlID0gcHJvbXB0LnNwbGl0KCdQcm9qZWN0IHN0cnVjdHVyZTonKVsxXSB8fCAnJztcbiAgICAgIGNvbnN0IGZpbGVzID0gcHJvamVjdFN0cnVjdHVyZS50cmltKCkuc3BsaXQoJ1xcbicpO1xuICAgICAgcmV0dXJuIGBCYXNlZCBvbiB0aGUgcHJvamVjdCBzdHJ1Y3R1cmUgcHJvdmlkZWQsIHRoZXJlIGFyZSAke2ZpbGVzLmxlbmd0aH0gZmlsZXMgaW4gdGhlIHByb2plY3QuYDtcbiAgICB9XG5cbiAgICBpZihpc0dlbmVyYXRlVGFzaykge1xuICAgICAgcmV0dXJuIGBcbiMgQ29kZSBHZW5lcmF0aW9uIFJlcXVlc3Q6IFwiJHtxdWVzdGlvbn1cIlxuXG5UbyBnZW5lcmF0ZSBjb2RlIHVzaW5nIEN1cnNvcidzIEFJIGNhcGFiaWxpdGllczpcblxuMS4gKipPcGVuIHlvdXIgcHJvamVjdCBpbiBDdXJzb3IgSURFKiogKGh0dHBzOi8vY3Vyc29yLnNoKVxuMi4gUHJlc3MgKipDbWQrTCoqIChvciBDdHJsK0wgb24gV2luZG93cy9MaW51eCkgdG8gb3BlbiB0aGUgQUkgY2hhdFxuMy4gVHlwZSB5b3VyIHJlcXVlc3Q6IFwiJHtxdWVzdGlvbn1cIlxuNC4gQ3Vyc29yIHdpbGwgZ2VuZXJhdGUgdGhlIGNvZGUgZGlyZWN0bHkgaW4geW91ciBlZGl0b3JcblxuVGhlIGN1cnJlbnQgQ0xJIGludGVncmF0aW9uIGRvZXNuJ3QgaGF2ZSBkaXJlY3QgYWNjZXNzIHRvIEN1cnNvcidzIGNvZGUgZ2VuZXJhdGlvbiBjYXBhYmlsaXRpZXMuXG5cbioqQWx0ZXJuYXRpdmUgb3B0aW9uczoqKlxuXG4xLiAqKlVzZSBPcGVuQUkgb3IgQW50aHJvcGljIGRpcmVjdGx5OioqXG4gICBDb25maWd1cmUgaW4geW91ciBsZXguY29uZmlnIGZpbGU6XG4gICBcXGBcXGBcXGBqc1xuICAgZXhwb3J0IGRlZmF1bHQge1xuICAgICBhaToge1xuICAgICAgIHByb3ZpZGVyOiAnb3BlbmFpJyxcbiAgICAgICBhcGlLZXk6IHByb2Nlc3MuZW52Lk9QRU5BSV9BUElfS0VZLFxuICAgICAgIG1vZGVsOiAnZ3B0LTRvJ1xuICAgICB9XG4gICB9XG4gICBcXGBcXGBcXGBcblxuMi4gKipVc2UgQ3Vyc29yJ3MgY29tbWFuZCBsaW5lIHRvb2w6KipcbiAgIEluc3RhbGw6IFxcYG5wbSBpbnN0YWxsIC1nIEBjdXJzb3IvY2xpXFxgXG4gICBSdW46IFxcYGN1cnNvciBhaSBcIiR7cXVlc3Rpb259XCJcXGBcbmA7XG4gICAgfVxuXG4gICAgcmV0dXJuIGBcblRvIHVzZSBDdXJzb3IncyBBSSBjYXBhYmlsaXRpZXMgZm9yIFwiJHtxdWVzdGlvbn1cIiwgeW91IG5lZWQgdG86XG5cbjEuIE9wZW4geW91ciBwcm9qZWN0IGluIEN1cnNvciBJREUgKGh0dHBzOi8vY3Vyc29yLnNoKVxuMi4gVXNlIEN1cnNvcidzIGJ1aWx0LWluIEFJIGZlYXR1cmVzIGJ5IHByZXNzaW5nIENtZCtLIG9yIENtZCtMXG4zLiBPciBydW4gdGhlICdjdXJzb3InIGNvbW1hbmQgZGlyZWN0bHkgZnJvbSB5b3VyIHRlcm1pbmFsXG5cblRoZSBjdXJyZW50IGludGVncmF0aW9uIGlzIGxpbWl0ZWQgYW5kIGRvZXNuJ3QgZGlyZWN0bHkgYWNjZXNzIEN1cnNvcidzIEFJIGNhcGFiaWxpdGllcy5cblxuRm9yIHRoZSBiZXN0IGV4cGVyaWVuY2Ugd2l0aCBBSSBjb2RlIGdlbmVyYXRpb246XG4tIFVzZSBDdXJzb3IgSURFIGRpcmVjdGx5XG4tIE9yIGNvbmZpZ3VyZSBPcGVuQUkgb3IgQW50aHJvcGljIGFzIHlvdXIgcHJvdmlkZXIgaW4geW91ciBsZXguY29uZmlnIGZpbGU6XG5cblxcYFxcYFxcYGpzXG4vLyBsZXguY29uZmlnLmpzIChvciBsZXguY29uZmlnLm1qcywgbGV4LmNvbmZpZy5janMsIGV0Yy4pXG5leHBvcnQgZGVmYXVsdCB7XG4gIGFpOiB7XG4gICAgcHJvdmlkZXI6ICdvcGVuYWknLCAvLyBvciAnYW50aHJvcGljJ1xuICAgIGFwaUtleTogcHJvY2Vzcy5lbnYuT1BFTkFJX0FQSV9LRVksIC8vIG9yIEFOVEhST1BJQ19BUElfS0VZXG4gICAgbW9kZWw6ICdncHQtNG8nIC8vIG9yICdjbGF1ZGUtMy1vcHVzJ1xuICB9XG59XG5cXGBcXGBcXGBcblxuVGhlbiBzZXQgeW91ciBBUEkga2V5IGFzIGFuIGVudmlyb25tZW50IHZhcmlhYmxlOlxuXFxgXFxgXFxgXG5leHBvcnQgT1BFTkFJX0FQSV9LRVk9eW91cl9rZXlfaGVyZVxuXFxgXFxgXFxgXG5gO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHRocm93IG5ldyBFcnJvcihgQ3Vyc29yIEFJIGVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gIH1cbn07XG5cbmV4cG9ydCBjb25zdCBjYWxsT3BlbkFJQUkgPSBhc3luYyAocHJvbXB0OiBzdHJpbmcsIG9wdGlvbnM6IEFJQ29uZmlnKTogUHJvbWlzZTxzdHJpbmc+ID0+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCBhcGlLZXkgPSBvcHRpb25zLmFwaUtleSB8fCBwcm9jZXNzLmVudi5PUEVOQUlfQVBJX0tFWTtcbiAgICBpZighYXBpS2V5KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ09wZW5BSSBBUEkga2V5IGlzIHJlcXVpcmVkLiBTZXQgaXQgaW4geW91ciBsZXguY29uZmlnIGZpbGUgb3IgYXMgT1BFTkFJX0FQSV9LRVkgZW52aXJvbm1lbnQgdmFyaWFibGUuJyk7XG4gICAgfVxuXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaCgnaHR0cHM6Ly9hcGkub3BlbmFpLmNvbS92MS9jaGF0L2NvbXBsZXRpb25zJywge1xuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICBtYXhfdG9rZW5zOiBvcHRpb25zLm1heFRva2VucyB8fCA0MDAwLFxuICAgICAgICBtZXNzYWdlczogW1xuICAgICAgICAgIHtjb250ZW50OiAnWW91IGFyZSBhIGhlbHBmdWwgYXNzaXN0YW50IHRoYXQgZml4ZXMgRVNMaW50IGVycm9ycyBpbiBjb2RlLicsIHJvbGU6ICdzeXN0ZW0nfSxcbiAgICAgICAgICB7Y29udGVudDogcHJvbXB0LCByb2xlOiAndXNlcid9XG4gICAgICAgIF0sXG4gICAgICAgIG1vZGVsOiBvcHRpb25zLm1vZGVsIHx8ICdncHQtNG8nLFxuICAgICAgICB0ZW1wZXJhdHVyZTogb3B0aW9ucy50ZW1wZXJhdHVyZSB8fCAwLjFcbiAgICAgIH0pLFxuICAgICAgaGVhZGVyczoge1xuICAgICAgICBBdXRob3JpemF0aW9uOiBgQmVhcmVyICR7YXBpS2V5fWAsXG4gICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbidcbiAgICAgIH0sXG4gICAgICBtZXRob2Q6ICdQT1NUJ1xuICAgIH0pO1xuXG4gICAgaWYoIXJlc3BvbnNlLm9rKSB7XG4gICAgICBjb25zdCBlcnJvciA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgT3BlbkFJIEFQSSBlcnJvcjogJHtlcnJvci5lcnJvcj8ubWVzc2FnZSB8fCByZXNwb25zZS5zdGF0dXNUZXh0fWApO1xuICAgIH1cblxuICAgIGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XG4gICAgcmV0dXJuIGRhdGEuY2hvaWNlc1swXS5tZXNzYWdlLmNvbnRlbnQ7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBPcGVuQUkgQUkgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgfVxufTtcblxuZXhwb3J0IGNvbnN0IGNhbGxBbnRocm9waWNBSSA9IGFzeW5jIChwcm9tcHQ6IHN0cmluZywgb3B0aW9uczogQUlDb25maWcpOiBQcm9taXNlPHN0cmluZz4gPT4ge1xuICB0cnkge1xuICAgIGNvbnN0IGFwaUtleSA9IG9wdGlvbnMuYXBpS2V5IHx8IHByb2Nlc3MuZW52LkFOVEhST1BJQ19BUElfS0VZO1xuICAgIGlmKCFhcGlLZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignQW50aHJvcGljIEFQSSBrZXkgaXMgcmVxdWlyZWQuIFNldCBpdCBpbiB5b3VyIGxleC5jb25maWcgZmlsZSBvciBhcyBBTlRIUk9QSUNfQVBJX0tFWSBlbnZpcm9ubWVudCB2YXJpYWJsZS4nKTtcbiAgICB9XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKCdodHRwczovL2FwaS5hbnRocm9waWMuY29tL3YxL21lc3NhZ2VzJywge1xuICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICBtYXhfdG9rZW5zOiBvcHRpb25zLm1heFRva2VucyB8fCA0MDAwLFxuICAgICAgICBtZXNzYWdlczogW1xuICAgICAgICAgIHtjb250ZW50OiBwcm9tcHQsIHJvbGU6ICd1c2VyJ31cbiAgICAgICAgXSxcbiAgICAgICAgbW9kZWw6IG9wdGlvbnMubW9kZWwgfHwgJ2NsYXVkZS0zLXNvbm5ldC0yMDI0MDIyOScsXG4gICAgICAgIHRlbXBlcmF0dXJlOiBvcHRpb25zLnRlbXBlcmF0dXJlIHx8IDAuMVxuICAgICAgfSksXG4gICAgICBoZWFkZXJzOiB7XG4gICAgICAgICdDb250ZW50LVR5cGUnOiAnYXBwbGljYXRpb24vanNvbicsXG4gICAgICAgICdhbnRocm9waWMtdmVyc2lvbic6ICcyMDIzLTA2LTAxJyxcbiAgICAgICAgJ3gtYXBpLWtleSc6IGFwaUtleVxuICAgICAgfSxcbiAgICAgIG1ldGhvZDogJ1BPU1QnXG4gICAgfSk7XG5cbiAgICBpZighcmVzcG9uc2Uub2spIHtcbiAgICAgIGNvbnN0IGVycm9yID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBBbnRocm9waWMgQVBJIGVycm9yOiAke2Vycm9yLmVycm9yPy5tZXNzYWdlIHx8IHJlc3BvbnNlLnN0YXR1c1RleHR9YCk7XG4gICAgfVxuXG4gICAgY29uc3QgZGF0YSA9IGF3YWl0IHJlc3BvbnNlLmpzb24oKTtcbiAgICByZXR1cm4gZGF0YS5jb250ZW50WzBdLnRleHQ7XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBBbnRocm9waWMgQUkgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgfVxufTtcblxuZXhwb3J0IGNvbnN0IGNhbGxDb3BpbG90QUkgPSBhc3luYyAocHJvbXB0OiBzdHJpbmcsIF9vcHRpb25zOiBBSUNvbmZpZyk6IFByb21pc2U8c3RyaW5nPiA9PiB7XG4gIHRyeSB7XG4gICAgbG9nKCdHaXRIdWIgQ29waWxvdCBBSSBmaXhlcyBub3QgZGlyZWN0bHkgc3VwcG9ydGVkLiBVc2luZyBtYW51YWwgZml4IG1vZGUuJywgJ2luZm8nKTtcbiAgICByZXR1cm4gcHJvbXB0O1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHRocm93IG5ldyBFcnJvcihgR2l0SHViIENvcGlsb3QgQUkgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgfVxufTtcblxuZXhwb3J0IGNvbnN0IHByb21wdEZvckFJUHJvdmlkZXIgPSBhc3luYyAoX3F1aWV0ID0gZmFsc2UpOiBQcm9taXNlPCdjdXJzb3InIHwgJ2NvcGlsb3QnIHwgJ29wZW5haScgfCAnYW50aHJvcGljJyB8ICdub25lJz4gPT4ge1xuICBjb25zdCBybCA9IHJlYWRsaW5lLmNyZWF0ZUludGVyZmFjZSh7XG4gICAgaW5wdXQ6IHByb2Nlc3Muc3RkaW4sXG4gICAgb3V0cHV0OiBwcm9jZXNzLnN0ZG91dFxuICB9KTtcblxuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICBsb2coJ1xcbk5vIEFJIHByb3ZpZGVyIGNvbmZpZ3VyZWQuIFBsZWFzZSBjaG9vc2UgYW4gQUkgcHJvdmlkZXI6JywgJ2luZm8nKTtcbiAgICBsb2coJzEuIEN1cnNvciBJREUnLCAnaW5mbycpO1xuICAgIGxvZygnMi4gT3BlbkFJJywgJ2luZm8nKTtcbiAgICBsb2coJzMuIEFudGhyb3BpYycsICdpbmZvJyk7XG4gICAgbG9nKCc0LiBHaXRIdWIgQ29waWxvdCcsICdpbmZvJyk7XG4gICAgbG9nKCc1LiBOb25lIChTa2lwIEFJIGZlYXR1cmVzKScsICdpbmZvJyk7XG5cbiAgICBybC5xdWVzdGlvbignRW50ZXIgeW91ciBjaG9pY2UgKDEtNSk6ICcsIChhbnN3ZXIpID0+IHtcbiAgICAgIHJsLmNsb3NlKCk7XG5cbiAgICAgIHN3aXRjaChhbnN3ZXIpIHtcbiAgICAgICAgY2FzZSAnMSc6XG4gICAgICAgICAgcmVzb2x2ZSgnY3Vyc29yJyk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJzInOlxuICAgICAgICAgIHJlc29sdmUoJ29wZW5haScpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICczJzpcbiAgICAgICAgICByZXNvbHZlKCdhbnRocm9waWMnKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSAnNCc6XG4gICAgICAgICAgcmVzb2x2ZSgnY29waWxvdCcpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHJlc29sdmUoJ25vbmUnKTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSk7XG59O1xuXG5leHBvcnQgY29uc3QgcHJvbXB0Rm9yQVBJS2V5ID0gYXN5bmMgKHByb3ZpZGVyOiBzdHJpbmcsIF9xdWlldCA9IGZhbHNlKTogUHJvbWlzZTxzdHJpbmc+ID0+IHtcbiAgY29uc3QgcmwgPSByZWFkbGluZS5jcmVhdGVJbnRlcmZhY2Uoe1xuICAgIGlucHV0OiBwcm9jZXNzLnN0ZGluLFxuICAgIG91dHB1dDogcHJvY2Vzcy5zdGRvdXRcbiAgfSk7XG5cbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgcmwucXVlc3Rpb24oYFBsZWFzZSBlbnRlciB5b3VyICR7cHJvdmlkZXJ9IEFQSSBrZXk6IGAsIChhbnN3ZXIpID0+IHtcbiAgICAgIHJsLmNsb3NlKCk7XG4gICAgICByZXNvbHZlKGFuc3dlcik7XG4gICAgfSk7XG4gIH0pO1xufTtcblxuZXhwb3J0IGNvbnN0IGdldEFJU2VydmljZSA9IChcbiAgcHJvdmlkZXI6IHN0cmluZyxcbiAgX29wdGlvbnM6IEFJQ29uZmlnXG4pOiAocHJvbXB0OiBzdHJpbmcsIG9wdGlvbnM6IEFJQ29uZmlnKSA9PiBQcm9taXNlPHN0cmluZz4gPT4ge1xuICBzd2l0Y2gocHJvdmlkZXIpIHtcbiAgICBjYXNlICdjdXJzb3InOlxuICAgICAgcmV0dXJuIGNhbGxDdXJzb3JBSTtcbiAgICBjYXNlICdvcGVuYWknOlxuICAgICAgcmV0dXJuIGNhbGxPcGVuQUlBSTtcbiAgICBjYXNlICdhbnRocm9waWMnOlxuICAgICAgcmV0dXJuIGNhbGxBbnRocm9waWNBSTtcbiAgICBjYXNlICdjb3BpbG90JzpcbiAgICAgIHJldHVybiBjYWxsQ29waWxvdEFJO1xuICAgIGRlZmF1bHQ6XG4gICAgICByZXR1cm4gYXN5bmMgKCkgPT4gJ05vIEFJIHByb3ZpZGVyIGNvbmZpZ3VyZWQnO1xuICB9XG59O1xuXG5leHBvcnQgY29uc3QgY2FsbEFJU2VydmljZSA9IGFzeW5jIChwcm9tcHQ6IHN0cmluZywgcXVpZXQgPSBmYWxzZSk6IFByb21pc2U8c3RyaW5nPiA9PiB7XG4gIGNvbnN0IHNwaW5uZXIgPSBjcmVhdGVTcGlubmVyKHF1aWV0KTtcbiAgc3Bpbm5lci5zdGFydCgnQ2FsbGluZyBBSSBzZXJ2aWNlIHRvIGZpeCBjb2RlIGlzc3Vlcy4uLicpO1xuXG4gIHRyeSB7XG4gICAgY29uc3QgYWlDb25maWcgPSBMZXhDb25maWcuY29uZmlnLmFpIHx8IHtwcm92aWRlcjogJ25vbmUnfTtcblxuICAgIGNvbnN0IGlzSW5DdXJzb3JJREUgPSBwcm9jZXNzLmVudi5DVVJTT1JfSURFID09PSAndHJ1ZSc7XG4gICAgaWYoaXNJbkN1cnNvcklERSAmJiAoYWlDb25maWcucHJvdmlkZXIgPT09ICdub25lJyB8fCAhYWlDb25maWcucHJvdmlkZXIpKSB7XG4gICAgICBsb2coJ0RldGVjdGVkIEN1cnNvciBJREUgZW52aXJvbm1lbnQsIHVzaW5nIEN1cnNvciBhcyBBSSBwcm92aWRlcicsICdpbmZvJywgcXVpZXQpO1xuICAgICAgYWlDb25maWcucHJvdmlkZXIgPSAnY3Vyc29yJztcbiAgICB9XG5cbiAgICBpZihhaUNvbmZpZy5wcm92aWRlciA9PT0gJ25vbmUnKSB7XG4gICAgICBjb25zdCBwcm92aWRlciA9IGF3YWl0IHByb21wdEZvckFJUHJvdmlkZXIocXVpZXQpO1xuXG4gICAgICBpZihwcm92aWRlciA9PT0gJ25vbmUnKSB7XG4gICAgICAgIHNwaW5uZXIuZmFpbCgnQUkgZmVhdHVyZXMgc2tpcHBlZCcpO1xuICAgICAgICByZXR1cm4gJyc7XG4gICAgICB9XG5cbiAgICAgIGFpQ29uZmlnLnByb3ZpZGVyID0gcHJvdmlkZXI7XG5cbiAgICAgIGlmKHByb3ZpZGVyICE9PSAnY3Vyc29yJyAmJiBwcm92aWRlciAhPT0gJ2NvcGlsb3QnICYmXG4gICAgICAgICFwcm9jZXNzLmVudltgJHtwcm92aWRlci50b1VwcGVyQ2FzZSgpfV9BUElfS0VZYF0pIHtcbiAgICAgICAgYWlDb25maWcuYXBpS2V5ID0gYXdhaXQgcHJvbXB0Rm9yQVBJS2V5KHByb3ZpZGVyLCBxdWlldCk7XG4gICAgICB9XG5cbiAgICAgIExleENvbmZpZy5jb25maWcuYWkgPSBhaUNvbmZpZztcblxuICAgICAgLy8gU2VhcmNoIGZvciBjb25maWcgZmlsZXMgaW4gbXVsdGlwbGUgZm9ybWF0cyBsaWtlIExleENvbmZpZy5wYXJzZUNvbmZpZyBkb2VzXG4gICAgICBjb25zdCBjb25maWdGb3JtYXRzID0gWydqcycsICdtanMnLCAnY2pzJywgJ3RzJywgJ2pzb24nXTtcbiAgICAgIGNvbnN0IGNvbmZpZ0Jhc2VOYW1lID0gJ2xleC5jb25maWcnO1xuICAgICAgbGV0IGNvbmZpZ1BhdGggPSAnJztcblxuICAgICAgZm9yKGNvbnN0IGZvcm1hdCBvZiBjb25maWdGb3JtYXRzKSB7XG4gICAgICAgIGNvbnN0IHBvdGVudGlhbFBhdGggPSBwYXRoUmVzb2x2ZShwcm9jZXNzLmN3ZCgpLCBgLi8ke2NvbmZpZ0Jhc2VOYW1lfS4ke2Zvcm1hdH1gKTtcbiAgICAgICAgaWYoZXhpc3RzU3luYyhwb3RlbnRpYWxQYXRoKSkge1xuICAgICAgICAgIGNvbmZpZ1BhdGggPSBwb3RlbnRpYWxQYXRoO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGlmKGNvbmZpZ1BhdGgpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCBjb25maWdDb250ZW50ID0gcmVhZEZpbGVTeW5jKGNvbmZpZ1BhdGgsICd1dGY4Jyk7XG4gICAgICAgICAgY29uc3QgdXBkYXRlZENvbmZpZyA9IGNvbmZpZ0NvbnRlbnQucmVwbGFjZShcbiAgICAgICAgICAgIC9haTouKj9bLH1dL3MsXG4gICAgICAgICAgICBgYWk6IHsgcHJvdmlkZXI6ICcke2FpQ29uZmlnLnByb3ZpZGVyfScgfSxgXG4gICAgICAgICAgKTtcbiAgICAgICAgICB3cml0ZUZpbGVTeW5jKGNvbmZpZ1BhdGgsIHVwZGF0ZWRDb25maWcpO1xuICAgICAgICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIGxldCByZXN1bHQgPSAnJztcblxuICAgIHN3aXRjaChhaUNvbmZpZy5wcm92aWRlcikge1xuICAgICAgY2FzZSAnY3Vyc29yJzpcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgY2FsbEN1cnNvckFJKHByb21wdCwgYWlDb25maWcpO1xuICAgICAgICBsb2coJ0N1cnNvciBJREUgQUkgaW50ZWdyYXRpb24gYWN0aXZlJywgJ2luZm8nLCBxdWlldCk7XG4gICAgICAgIGJyZWFrO1xuICAgICAgY2FzZSAnb3BlbmFpJzpcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgY2FsbE9wZW5BSUFJKHByb21wdCwgYWlDb25maWcpO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ2FudGhyb3BpYyc6XG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IGNhbGxBbnRocm9waWNBSShwcm9tcHQsIGFpQ29uZmlnKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdjb3BpbG90JzpcbiAgICAgICAgcmVzdWx0ID0gYXdhaXQgY2FsbENvcGlsb3RBSShwcm9tcHQsIGFpQ29uZmlnKTtcbiAgICAgICAgYnJlYWs7XG4gICAgICBkZWZhdWx0OlxuICAgICAgICBzcGlubmVyLmZhaWwoJ05vIEFJIHByb3ZpZGVyIGNvbmZpZ3VyZWQnKTtcbiAgICAgICAgcmV0dXJuICcnO1xuICAgIH1cblxuICAgIHNwaW5uZXIuc3VjY2VlZCgnQUkgY29kZSBmaXhlcyBnZW5lcmF0ZWQgc3VjY2Vzc2Z1bGx5Jyk7XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBzcGlubmVyLmZhaWwoYEFJIHNlcnZpY2UgZXJyb3I6ICR7ZXJyb3IubWVzc2FnZX1gKTtcbiAgICBpZighcXVpZXQpIHtcbiAgICAgIGxvZyhlcnJvciwgJ2Vycm9yJyk7XG4gICAgfVxuICAgIHJldHVybiAnJztcbiAgfVxufTsiXSwKICAibWFwcGluZ3MiOiAiQUFJQSxTQUFRLFlBQVksY0FBYyxxQkFBb0I7QUFDdEQsU0FBUSxXQUFXLG1CQUFrQjtBQUNyQyxPQUFPLGNBQWM7QUFFckIsU0FBa0IsaUJBQWdCO0FBQ2xDLFNBQVEscUJBQW9CO0FBQzVCLFNBQVEsV0FBVTtBQUdYLE1BQU0sZUFBZSxPQUFPLFFBQWdCLGFBQXdDO0FBQ3pGLE1BQUk7QUFHRixRQUFJLG9DQUFvQyxNQUFNO0FBSTlDLFFBQUksbUNBQW1DLE1BQU07QUFFN0MsVUFBTSxZQUFZLE9BQU8sTUFBTSxxTUFBcU07QUFDcE8sVUFBTSxPQUFPLFlBQVksVUFBVSxDQUFDLElBQUk7QUFDeEMsVUFBTSxpQkFBaUIsS0FBSyxXQUFXLGVBQWU7QUFFdEQsVUFBTSxnQkFBZ0IsT0FBTyxNQUFNLHNPQUFzTztBQUN6USxVQUFNLFdBQVcsZ0JBQWdCLGNBQWMsQ0FBQyxFQUFFLEtBQUssSUFB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