UNPKG

@nlabs/lex

Version:
304 lines (297 loc) 34.2 kB
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} ${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 += ` Project structure: ${files.join("\n")}`; break; case "test": if (file) { const testConfig = getFileContext("jest.config.js"); projectContext += ` Test configuration: ${testConfig}`; } break; case "optimize": const webpackConfig = getFileContext("webpack.config.js"); projectContext += ` Webpack configuration: ${webpackConfig}`; break; default: break; } return projectContext; }; const constructPrompt = (options, projectContext) => { const { task = "help", prompt = "" } = options; const taskInstructions = { generate: "Generate code according to the following request. Make sure it follows best practices and is well documented:", explain: "Explain the following code in detail, including any patterns, potential issues, and improvement suggestions:", test: "Generate comprehensive unit tests for the following code:", optimize: "Analyze the following code/configuration and suggest optimization improvements:", help: "Provide guidance on the following development question:", ask: "Provide guidance on the following development question:", analyze: "Analyze the following code:" }; const taskInstruction = taskInstructions[task] || taskInstructions.help; let fullPrompt = `${taskInstruction} ${prompt}`; if (projectContext) { fullPrompt += ` ===CONTEXT=== ${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) { content = response.choices[0].message.content; } else if (response.content) { content = response.content; } 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 = { generate: "Generate code according to the following request. Make sure it follows best practices and is well documented:", explain: "Explain the following code in detail, including any patterns, potential issues, and improvement suggestions:", test: "Generate comprehensive unit tests for the following code:", optimize: "Analyze the following code/configuration and suggest optimization improvements:", help: "Provide guidance on the following development question:", ask: "Provide guidance on the following development question:", analyze: "Analyze 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 void 0; default: return void 0; } }; 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; }; 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 += ` ===FILE: ${file}=== ${content} `; } } } 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 += ` ===Project structure:=== ${result.stdout} `; } catch (error) { log(`${chalk.yellow("Warning:")} Error reading directory: ${error.message}`, "warning"); } } let formattedPrompt = ""; switch (task) { case "explain": formattedPrompt = `Explain the following code: ${prompt}`; break; case "generate": formattedPrompt = `Generate code according to the following request: ${prompt}`; break; case "test": formattedPrompt = `Generate comprehensive unit tests: ${prompt}`; break; case "analyze": formattedPrompt = `Analyze the following code: ${prompt}`; break; case "ask": formattedPrompt = `Provide guidance on the following development question: ${prompt}`; break; } if (context) { formattedPrompt += ` ===CONTEXT=== ${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(` ${response}`, "success"); return { response }; } catch (error) { log(`${chalk.red("Error:")} ${error.message}`, "error"); return { error: error.message }; } }; 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); }); var ai_default = ai; export { ai, aiFunction, ai_default as default }; //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2NvbW1hbmRzL2FpL2FpLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIENvcHlyaWdodCAoYykgMjAxOC1QcmVzZW50LCBOaXRyb2dlbiBMYWJzLCBJbmMuXG4gKiBDb3B5cmlnaHRzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gU2VlIHRoZSBhY2NvbXBhbnlpbmcgTElDRU5TRSBmaWxlIGZvciB0ZXJtcy5cbiAqL1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJztcbmltcG9ydCB7Q29tbWFuZH0gZnJvbSAnY29tbWFuZGVyJztcbmltcG9ydCB7cmVhZEZpbGVTeW5jfSBmcm9tICdmcyc7XG5pbXBvcnQge3N5bmMgYXMgZ2xvYlN5bmN9IGZyb20gJ2dsb2InO1xuXG5pbXBvcnQge0xleENvbmZpZ30gZnJvbSAnLi4vLi4vTGV4Q29uZmlnLmpzJztcbmltcG9ydCB7Y2FsbEFJU2VydmljZX0gZnJvbSAnLi4vLi4vdXRpbHMvYWlTZXJ2aWNlLmpzJztcbmltcG9ydCB7bG9nfSBmcm9tICcuLi8uLi91dGlscy9sb2cuanMnO1xuXG5pZihwcm9jZXNzLmVudi5DVVJTT1JfRVhURU5TSU9OID09PSAndHJ1ZScgfHxcbiAgcHJvY2Vzcy5lbnYuQ1VSU09SX1RFUk1JTkFMID09PSAndHJ1ZScgfHxcbiAgcHJvY2Vzcy5lbnYuQ1VSU09SX0FQUCA9PT0gJ3RydWUnIHx8XG4gIHByb2Nlc3MuZW52LlBBVEg/LmluY2x1ZGVzKCdjdXJzb3InKSB8fFxuICBwcm9jZXNzLmVudi5DVVJTT1JfU0VTU0lPTl9JRCkge1xuICBwcm9jZXNzLmVudi5DVVJTT1JfSURFID0gJ3RydWUnO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEFJT3B0aW9ucyB7XG4gIHJlYWRvbmx5IGNsaU5hbWU/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGNvbnRleHQ/OiBib29sZWFuO1xuICByZWFkb25seSBmaWxlPzogc3RyaW5nO1xuICByZWFkb25seSBsZXhDb25maWc/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IG1vZGVsPzogc3RyaW5nO1xuICByZWFkb25seSBwcm9tcHQ/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IHF1aWV0PzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgdGFzaz86ICdnZW5lcmF0ZScgfCAnZXhwbGFpbicgfCAndGVzdCcgfCAnb3B0aW1pemUnIHwgJ2hlbHAnIHwgJ2FzaycgfCAnYW5hbHl6ZSc7XG4gIHJlYWRvbmx5IGRlYnVnPzogYm9vbGVhbjtcbiAgcmVhZG9ubHkgcHJvdmlkZXI/OiBzdHJpbmc7XG4gIHJlYWRvbmx5IGRpcj86IHN0cmluZztcbn1cblxuY29uc3QgZ2V0RmlsZUNvbnRleHQgPSAoZmlsZVBhdGg6IHN0cmluZyk6IHN0cmluZyA9PiB7XG4gIHRyeSB7XG4gICAgY29uc3QgY29udGVudCA9IHJlYWRGaWxlU3luYyhmaWxlUGF0aCwgJ3V0Zi04Jyk7XG4gICAgcmV0dXJuIGBGaWxlOiAke2ZpbGVQYXRofVxcblxcbiR7Y29udGVudH1gO1xuICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICByZXR1cm4gYEVycm9yIHJlYWRpbmcgZmlsZTogJHtmaWxlUGF0aH1gO1xuICB9XG59O1xuXG5jb25zdCBnZXRQcm9qZWN0Q29udGV4dCA9IGFzeW5jIChvcHRpb25zOiBBSU9wdGlvbnMpOiBQcm9taXNlPHN0cmluZz4gPT4ge1xuICBjb25zdCB7ZmlsZSwgdGFzaywgY29udGV4dH0gPSBvcHRpb25zO1xuXG4gIGlmKGNvbnRleHQgPT09IGZhbHNlKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG5cbiAgbGV0IHByb2plY3RDb250ZXh0ID0gJyc7XG5cbiAgaWYoZmlsZSkge1xuICAgIHByb2plY3RDb250ZXh0ICs9IGdldEZpbGVDb250ZXh0KGZpbGUpO1xuICB9XG5cbiAgc3dpdGNoKHRhc2spIHtcbiAgICBjYXNlICdnZW5lcmF0ZSc6XG4gICAgICBjb25zdCBmaWxlcyA9IGdsb2JTeW5jKCdzcmMvKiovKi57dHMsdHN4LGpzLGpzeH0nLCB7XG4gICAgICAgIGN3ZDogcHJvY2Vzcy5jd2QoKSxcbiAgICAgICAgaWdub3JlOiBbJyoqL25vZGVfbW9kdWxlcy8qKicsICcqKi9saWIvKionLCAnKiovZGlzdC8qKicsICcqKi8qLnRlc3QuKicsICcqKi8qLnNwZWMuKiddLFxuICAgICAgICBtYXhEZXB0aDogM1xuICAgICAgfSk7XG4gICAgICBwcm9qZWN0Q29udGV4dCArPSBgXFxuXFxuUHJvamVjdCBzdHJ1Y3R1cmU6XFxuJHtmaWxlcy5qb2luKCdcXG4nKX1gO1xuICAgICAgYnJlYWs7XG5cbiAgICBjYXNlICd0ZXN0JzpcbiAgICAgIGlmKGZpbGUpIHtcbiAgICAgICAgY29uc3QgdGVzdENvbmZpZyA9IGdldEZpbGVDb250ZXh0KCdqZXN0LmNvbmZpZy5qcycpO1xuICAgICAgICBwcm9qZWN0Q29udGV4dCArPSBgXFxuXFxuVGVzdCBjb25maWd1cmF0aW9uOlxcbiR7dGVzdENvbmZpZ31gO1xuICAgICAgfVxuICAgICAgYnJlYWs7XG5cbiAgICBjYXNlICdvcHRpbWl6ZSc6XG4gICAgICBjb25zdCB3ZWJwYWNrQ29uZmlnID0gZ2V0RmlsZUNvbnRleHQoJ3dlYnBhY2suY29uZmlnLmpzJyk7XG4gICAgICBwcm9qZWN0Q29udGV4dCArPSBgXFxuXFxuV2VicGFjayBjb25maWd1cmF0aW9uOlxcbiR7d2VicGFja0NvbmZpZ31gO1xuICAgICAgYnJlYWs7XG5cbiAgICBkZWZhdWx0OlxuICAgICAgYnJlYWs7XG4gIH1cblxuICByZXR1cm4gcHJvamVjdENvbnRleHQ7XG59O1xuXG5jb25zdCBjb25zdHJ1Y3RQcm9tcHQgPSAob3B0aW9uczogQUlPcHRpb25zLCBwcm9qZWN0Q29udGV4dDogc3RyaW5nKTogc3RyaW5nID0+IHtcbiAgY29uc3Qge3Rhc2sgPSAnaGVscCcsIHByb21wdCA9ICcnfSA9IG9wdGlvbnM7XG5cbiAgY29uc3QgdGFza0luc3RydWN0aW9uczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICBnZW5lcmF0ZTogJ0dlbmVyYXRlIGNvZGUgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgcmVxdWVzdC4gTWFrZSBzdXJlIGl0IGZvbGxvd3MgYmVzdCBwcmFjdGljZXMgYW5kIGlzIHdlbGwgZG9jdW1lbnRlZDonLFxuICAgIGV4cGxhaW46ICdFeHBsYWluIHRoZSBmb2xsb3dpbmcgY29kZSBpbiBkZXRhaWwsIGluY2x1ZGluZyBhbnkgcGF0dGVybnMsIHBvdGVudGlhbCBpc3N1ZXMsIGFuZCBpbXByb3ZlbWVudCBzdWdnZXN0aW9uczonLFxuICAgIHRlc3Q6ICdHZW5lcmF0ZSBjb21wcmVoZW5zaXZlIHVuaXQgdGVzdHMgZm9yIHRoZSBmb2xsb3dpbmcgY29kZTonLFxuICAgIG9wdGltaXplOiAnQW5hbHl6ZSB0aGUgZm9sbG93aW5nIGNvZGUvY29uZmlndXJhdGlvbiBhbmQgc3VnZ2VzdCBvcHRpbWl6YXRpb24gaW1wcm92ZW1lbnRzOicsXG4gICAgaGVscDogJ1Byb3ZpZGUgZ3VpZGFuY2Ugb24gdGhlIGZvbGxvd2luZyBkZXZlbG9wbWVudCBxdWVzdGlvbjonLFxuICAgIGFzazogJ1Byb3ZpZGUgZ3VpZGFuY2Ugb24gdGhlIGZvbGxvd2luZyBkZXZlbG9wbWVudCBxdWVzdGlvbjonLFxuICAgIGFuYWx5emU6ICdBbmFseXplIHRoZSBmb2xsb3dpbmcgY29kZTonXG4gIH07XG5cbiAgY29uc3QgdGFza0luc3RydWN0aW9uID0gdGFza0luc3RydWN0aW9uc1t0YXNrXSB8fCB0YXNrSW5zdHJ1Y3Rpb25zLmhlbHA7XG5cbiAgbGV0IGZ1bGxQcm9tcHQgPSBgJHt0YXNrSW5zdHJ1Y3Rpb259XFxuXFxuJHtwcm9tcHR9YDtcblxuICBpZihwcm9qZWN0Q29udGV4dCkge1xuICAgIGZ1bGxQcm9tcHQgKz0gYFxcblxcbj09PUNPTlRFWFQ9PT1cXG4ke3Byb2plY3RDb250ZXh0fWA7XG4gIH1cblxuICByZXR1cm4gZnVsbFByb21wdDtcbn07XG5cbmNvbnN0IGRpc3BsYXlSZXNwb25zZSA9IChyZXNwb25zZTogYW55LCBvcHRpb25zOiBBSU9wdGlvbnMpOiB2b2lkID0+IHtcbiAgY29uc3Qge3Rhc2sgPSAnaGVscCcsIHF1aWV0ID0gZmFsc2V9ID0gb3B0aW9ucztcblxuICBsZXQgY29udGVudCA9ICcnO1xuXG4gIGlmKHR5cGVvZiByZXNwb25zZSA9PT0gJ3N0cmluZycpIHtcbiAgICBjb250ZW50ID0gcmVzcG9uc2U7XG4gIH0gZWxzZSBpZihyZXNwb25zZS5jaG9pY2VzPy5bMF0/Lm1lc3NhZ2U/LmNvbnRlbnQpIHtcbiAgICBjb250ZW50ID0gcmVzcG9uc2UuY2hvaWNlc1swXS5tZXNzYWdlLmNvbnRlbnQ7XG4gIH0gZWxzZSBpZihyZXNwb25zZS5jb250ZW50KSB7XG4gICAgY29udGVudCA9IHJlc3BvbnNlLmNvbnRlbnQ7XG4gIH0gZWxzZSB7XG4gICAgY29udGVudCA9ICdObyByZXNwb25zZSByZWNlaXZlZCBmcm9tIEFJIG1vZGVsJztcbiAgfVxuXG4gIGNvbnN0IGNsZWFuZWRDb250ZW50ID0gY2xlYW5SZXNwb25zZShjb250ZW50LCBvcHRpb25zKTtcblxuICBzd2l0Y2godGFzaykge1xuICAgIGNhc2UgJ2dlbmVyYXRlJzpcbiAgICAgIGxvZygnXFxuR2VuZXJhdGVkIENvZGU6XFxuJywgJ3N1Y2Nlc3MnLCBxdWlldCk7XG4gICAgICBsb2coY2xlYW5lZENvbnRlbnQsICdkZWZhdWx0JywgcXVpZXQpO1xuICAgICAgYnJlYWs7XG5cbiAgICBjYXNlICdleHBsYWluJzpcbiAgICAgIGxvZygnXFxuQ29kZSBFeHBsYW5hdGlvbjpcXG4nLCAnc3VjY2VzcycsIHF1aWV0KTtcbiAgICAgIGxvZyhjbGVhbmVkQ29udGVudCwgJ2RlZmF1bHQnLCBxdWlldCk7XG4gICAgICBicmVhaztcblxuICAgIGNhc2UgJ3Rlc3QnOlxuICAgICAgbG9nKCdcXG5HZW5lcmF0ZWQgVGVzdHM6XFxuJywgJ3N1Y2Nlc3MnLCBxdWlldCk7XG4gICAgICBsb2coY2xlYW5lZENvbnRlbnQsICdkZWZhdWx0JywgcXVpZXQpO1xuICAgICAgYnJlYWs7XG5cbiAgICBjYXNlICdvcHRpbWl6ZSc6XG4gICAgICBsb2coJ1xcbk9wdGltaXphdGlvbiBTdWdnZXN0aW9uczpcXG4nLCAnc3VjY2VzcycsIHF1aWV0KTtcbiAgICAgIGxvZyhjbGVhbmVkQ29udGVudCwgJ2RlZmF1bHQnLCBxdWlldCk7XG4gICAgICBicmVhaztcblxuICAgIGRlZmF1bHQ6XG4gICAgICBsb2coJ1xcbkFJIFJlc3BvbnNlOlxcbicsICdzdWNjZXNzJywgcXVpZXQpO1xuICAgICAgbG9nKGNsZWFuZWRDb250ZW50LCAnZGVmYXVsdCcsIHF1aWV0KTtcbiAgICAgIGJyZWFrO1xuICB9XG59O1xuXG5jb25zdCBjbGVhblJlc3BvbnNlID0gKGNvbnRlbnQ6IHN0cmluZywgb3B0aW9uczogQUlPcHRpb25zKTogc3RyaW5nID0+IHtcbiAgY29uc3Qge3Byb21wdCA9ICcnLCB0YXNrID0gJ2hlbHAnfSA9IG9wdGlvbnM7XG5cbiAgaWYoIWNvbnRlbnQpIHtcbiAgICByZXR1cm4gY29udGVudDtcbiAgfVxuXG4gIGxldCBjbGVhbmVkQ29udGVudCA9IGNvbnRlbnQ7XG5cbiAgY29uc3QgdGFza0luc3RydWN0aW9uczogUmVjb3JkPHN0cmluZywgc3RyaW5nPiA9IHtcbiAgICBnZW5lcmF0ZTogJ0dlbmVyYXRlIGNvZGUgYWNjb3JkaW5nIHRvIHRoZSBmb2xsb3dpbmcgcmVxdWVzdC4gTWFrZSBzdXJlIGl0IGZvbGxvd3MgYmVzdCBwcmFjdGljZXMgYW5kIGlzIHdlbGwgZG9jdW1lbnRlZDonLFxuICAgIGV4cGxhaW46ICdFeHBsYWluIHRoZSBmb2xsb3dpbmcgY29kZSBpbiBkZXRhaWwsIGluY2x1ZGluZyBhbnkgcGF0dGVybnMsIHBvdGVudGlhbCBpc3N1ZXMsIGFuZCBpbXByb3ZlbWVudCBzdWdnZXN0aW9uczonLFxuICAgIHRlc3Q6ICdHZW5lcmF0ZSBjb21wcmVoZW5zaXZlIHVuaXQgdGVzdHMgZm9yIHRoZSBmb2xsb3dpbmcgY29kZTonLFxuICAgIG9wdGltaXplOiAnQW5hbHl6ZSB0aGUgZm9sbG93aW5nIGNvZGUvY29uZmlndXJhdGlvbiBhbmQgc3VnZ2VzdCBvcHRpbWl6YXRpb24gaW1wcm92ZW1lbnRzOicsXG4gICAgaGVscDogJ1Byb3ZpZGUgZ3VpZGFuY2Ugb24gdGhlIGZvbGxvd2luZyBkZXZlbG9wbWVudCBxdWVzdGlvbjonLFxuICAgIGFzazogJ1Byb3ZpZGUgZ3VpZGFuY2Ugb24gdGhlIGZvbGxvd2luZyBkZXZlbG9wbWVudCBxdWVzdGlvbjonLFxuICAgIGFuYWx5emU6ICdBbmFseXplIHRoZSBmb2xsb3dpbmcgY29kZTonXG4gIH07XG5cbiAgY29uc3QgaW5zdHJ1Y3Rpb24gPSB0YXNrSW5zdHJ1Y3Rpb25zW3Rhc2tdIHx8ICcnO1xuXG4gIGlmKGluc3RydWN0aW9uICYmIGNsZWFuZWRDb250ZW50LmluY2x1ZGVzKGluc3RydWN0aW9uKSkge1xuICAgIGNsZWFuZWRDb250ZW50ID0gY2xlYW5lZENvbnRlbnQucmVwbGFjZShpbnN0cnVjdGlvbiwgJycpLnRyaW0oKTtcbiAgfVxuXG4gIGlmKHByb21wdCAmJiBjbGVhbmVkQ29udGVudC5pbmNsdWRlcyhwcm9tcHQpKSB7XG4gICAgY2xlYW5lZENvbnRlbnQgPSBjbGVhbmVkQ29udGVudC5yZXBsYWNlKHByb21wdCwgJycpLnRyaW0oKTtcbiAgfVxuXG4gIGlmKGNsZWFuZWRDb250ZW50LmluY2x1ZGVzKCc9PT1DT05URVhUPT09JykpIHtcbiAgICBjbGVhbmVkQ29udGVudCA9IGNsZWFuZWRDb250ZW50LnNwbGl0KCc9PT1DT05URVhUPT09JylbMF0udHJpbSgpO1xuICB9XG5cbiAgaWYoIWNsZWFuZWRDb250ZW50KSB7XG4gICAgcmV0dXJuIGNvbnRlbnQ7XG4gIH1cblxuICByZXR1cm4gY2xlYW5lZENvbnRlbnQ7XG59O1xuXG5jb25zdCBnZXRQcm92aWRlckF1dGggPSAocHJvdmlkZXI6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCA9PiB7XG4gIGlmKHByb2Nlc3MuY3dkKCkuaW5jbHVkZXMoJ3JlYWt0b3InKSkge1xuICAgIHJldHVybiAnY3Vyc29yLWF1dGgnO1xuICB9XG5cbiAgaWYocHJvY2Vzcy5lbnYuQUlfQVBJX0tFWSkge1xuICAgIHJldHVybiBwcm9jZXNzLmVudi5BSV9BUElfS0VZO1xuICB9XG5cbiAgaWYocHJvdmlkZXIgPT09ICdub25lJyAmJiBwcm9jZXNzLmVudi5DVVJTT1JfSURFID09PSAndHJ1ZScpIHtcbiAgICByZXR1cm4gJ2N1cnNvci1hdXRoJztcbiAgfVxuXG4gIHN3aXRjaChwcm92aWRlcikge1xuICAgIGNhc2UgJ29wZW5haSc6XG4gICAgICByZXR1cm4gcHJvY2Vzcy5lbnYuT1BFTkFJX0FQSV9LRVk7XG4gICAgY2FzZSAnYW50aHJvcGljJzpcbiAgICAgIHJldHVybiBwcm9jZXNzLmVudi5BTlRIUk9QSUNfQVBJX0tFWTtcbiAgICBjYXNlICdjdXJzb3InOlxuICAgICAgcmV0dXJuICdjdXJzb3ItYXV0aCc7XG4gICAgY2FzZSAnY29waWxvdCc6XG4gICAgICByZXR1cm4gcHJvY2Vzcy5lbnYuR0lUSFVCX1RPS0VOO1xuICAgIGNhc2UgJ25vbmUnOlxuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxufTtcblxuY29uc3QgZGV0ZWN0Q3Vyc29ySURFID0gKCk6IGJvb2xlYW4gPT4ge1xuICBpZihwcm9jZXNzLmVudi5DVVJTT1JfSURFID09PSAndHJ1ZScpIHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIGNvbnN0IHBvc3NpYmxlQ3Vyc29yU2lnbmFscyA9IFtcbiAgICBwcm9jZXNzLmVudi5DVVJTT1JfRVhURU5TSU9OID09PSAndHJ1ZScsXG4gICAgcHJvY2Vzcy5lbnYuQ1VSU09SX1RFUk1JTkFMID09PSAndHJ1ZScsXG4gICAgcHJvY2Vzcy5lbnYuQ1VSU09SX0FQUCA9PT0gJ3RydWUnLFxuICAgIHByb2Nlc3MuZW52LlBBVEg/LmluY2x1ZGVzKCdjdXJzb3InKSxcbiAgICAhIXByb2Nlc3MuZW52LkNVUlNPUl9TRVNTSU9OX0lEXG4gIF07XG5cbiAgY29uc3QgaXNDdXJzb3JJREUgPSBwb3NzaWJsZUN1cnNvclNpZ25hbHMuc29tZSgoc2lnbmFsKSA9PiBzaWduYWwpO1xuXG4gIGlmKGlzQ3Vyc29ySURFKSB7XG4gICAgcHJvY2Vzcy5lbnYuQ1VSU09SX0lERSA9ICd0cnVlJztcbiAgfVxuXG4gIHJldHVybiBpc0N1cnNvcklERTtcbn07XG5cbmV4cG9ydCBjb25zdCBhaUZ1bmN0aW9uID0gYXN5bmMgKG9wdGlvbnM6IEFJT3B0aW9ucyk6IFByb21pc2U8YW55PiA9PiB7XG4gIHRyeSB7XG4gICAgY29uc3QgY29uZmlnID0gTGV4Q29uZmlnLmNvbmZpZyB8fCB7fTtcbiAgICBjb25zdCBhaUNvbmZpZyA9IGNvbmZpZy5haSB8fCB7fTtcbiAgICBjb25zdCBwcm92aWRlciA9IG9wdGlvbnMucHJvdmlkZXIgfHwgYWlDb25maWcucHJvdmlkZXIgfHwgJ25vbmUnO1xuXG4gICAgaWYocHJvdmlkZXIgPT09ICdub25lJyAmJiAhcHJvY2Vzcy5lbnYuQ1VSU09SX0VYVEVOU0lPTikge1xuICAgICAgbG9nKGAke2NoYWxrLnJlZCgnRXJyb3I6Jyl9IE5vIEFJIHByb3ZpZGVyIGNvbmZpZ3VyZWQuYCwgJ2Vycm9yJyk7XG4gICAgICByZXR1cm4ge2Vycm9yOiAnTm8gQUkgcHJvdmlkZXIgY29uZmlndXJlZCd9O1xuICAgIH1cblxuICAgIGNvbnN0IHRhc2sgPSBvcHRpb25zLnRhc2sgfHwgJ2Fzayc7XG4gICAgY29uc3QgdmFsaWRUYXNrcyA9IFsnZXhwbGFpbicsICdnZW5lcmF0ZScsICd0ZXN0JywgJ2FuYWx5emUnLCAnYXNrJ107XG5cbiAgICBpZighdmFsaWRUYXNrcy5pbmNsdWRlcyh0YXNrKSkge1xuICAgICAgbG9nKGAke2NoYWxrLnJlZCgnRXJyb3I6Jyl9IEludmFsaWQgdGFzayBcIiR7dGFza31cIi4gVmFsaWQgdGFza3MgYXJlOiAke3ZhbGlkVGFza3Muam9pbignLCAnKX1gLCAnZXJyb3InKTtcbiAgICAgIHJldHVybiB7ZXJyb3I6IGBJbnZhbGlkIHRhc2sgXCIke3Rhc2t9XCJgfTtcbiAgICB9XG5cbiAgICBjb25zdCB7cHJvbXB0fSA9IG9wdGlvbnM7XG5cbiAgICBpZighcHJvbXB0KSB7XG4gICAgICBsb2coYCR7Y2hhbGsucmVkKCdFcnJvcjonKX0gTm8gcHJvbXB0IHByb3ZpZGVkLiBVc2UgLS1wcm9tcHQgXCJZb3VyIHByb21wdCBoZXJlXCJgLCAnZXJyb3InKTtcbiAgICAgIHJldHVybiB7ZXJyb3I6ICdObyBwcm9tcHQgcHJvdmlkZWQnfTtcbiAgICB9XG5cbiAgICBsZXQgY29udGV4dCA9ICcnO1xuXG4gICAgaWYob3B0aW9ucy5maWxlKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBmcyA9IGF3YWl0IGltcG9ydCgnZnMvcHJvbWlzZXMnKTtcbiAgICAgICAgY29uc3QgZ2xvYiA9IGF3YWl0IGltcG9ydCgnZ2xvYicpO1xuICAgICAgICBjb25zdCBmaWxlcyA9IGF3YWl0IGdsb2IuZ2xvYihvcHRpb25zLmZpbGUpO1xuXG4gICAgICAgIGlmKGZpbGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIGxvZyhgJHtjaGFsay55ZWxsb3coJ1dhcm5pbmc6Jyl9IE5vIGZpbGVzIGZvdW5kIG1hdGNoaW5nIFwiJHtvcHRpb25zLmZpbGV9XCJgLCAnd2FybmluZycpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIGZvcihjb25zdCBmaWxlIG9mIGZpbGVzKSB7XG4gICAgICAgICAgICBjb25zdCBjb250ZW50ID0gYXdhaXQgZnMucmVhZEZpbGUoZmlsZSwgJ3V0ZjgnKTtcbiAgICAgICAgICAgIGNvbnRleHQgKz0gYFxcbj09PUZJTEU6ICR7ZmlsZX09PT1cXG4ke2NvbnRlbnR9XFxuYDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZyhgJHtjaGFsay55ZWxsb3coJ1dhcm5pbmc6Jyl9IEVycm9yIHJlYWRpbmcgZmlsZTogJHtlcnJvci5tZXNzYWdlfWAsICd3YXJuaW5nJyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYob3B0aW9ucy5kaXIpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGNvbnN0IHtleGVjYVN5bmN9ID0gYXdhaXQgaW1wb3J0KCdleGVjYScpO1xuICAgICAgICBjb25zdCByZXN1bHQgPSBleGVjYVN5bmMoJ2ZpbmQnLCBbb3B0aW9ucy5kaXIsICctdHlwZScsICdmJywgJ3wnLCAnc29ydCddKTtcbiAgICAgICAgY29udGV4dCArPSBgXFxuPT09UHJvamVjdCBzdHJ1Y3R1cmU6PT09XFxuJHtyZXN1bHQuc3Rkb3V0fVxcbmA7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2coYCR7Y2hhbGsueWVsbG93KCdXYXJuaW5nOicpfSBFcnJvciByZWFkaW5nIGRpcmVjdG9yeTogJHtlcnJvci5tZXNzYWdlfWAsICd3YXJuaW5nJyk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbGV0IGZvcm1hdHRlZFByb21wdCA9ICcnO1xuXG4gICAgc3dpdGNoKHRhc2spIHtcbiAgICAgIGNhc2UgJ2V4cGxhaW4nOlxuICAgICAgICBmb3JtYXR0ZWRQcm9tcHQgPSBgRXhwbGFpbiB0aGUgZm9sbG93aW5nIGNvZGU6XFxuJHtwcm9tcHR9YDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdnZW5lcmF0ZSc6XG4gICAgICAgIGZvcm1hdHRlZFByb21wdCA9IGBHZW5lcmF0ZSBjb2RlIGFjY29yZGluZyB0byB0aGUgZm9sbG93aW5nIHJlcXVlc3Q6XFxuJHtwcm9tcHR9YDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICd0ZXN0JzpcbiAgICAgICAgZm9ybWF0dGVkUHJvbXB0ID0gYEdlbmVyYXRlIGNvbXByZWhlbnNpdmUgdW5pdCB0ZXN0czpcXG4ke3Byb21wdH1gO1xuICAgICAgICBicmVhaztcbiAgICAgIGNhc2UgJ2FuYWx5emUnOlxuICAgICAgICBmb3JtYXR0ZWRQcm9tcHQgPSBgQW5hbHl6ZSB0aGUgZm9sbG93aW5nIGNvZGU6XFxuJHtwcm9tcHR9YDtcbiAgICAgICAgYnJlYWs7XG4gICAgICBjYXNlICdhc2snOlxuICAgICAgICBmb3JtYXR0ZWRQcm9tcHQgPSBgUHJvdmlkZSBndWlkYW5jZSBvbiB0aGUgZm9sbG93aW5nIGRldmVsb3BtZW50IHF1ZXN0aW9uOlxcbiR7cHJvbXB0fWA7XG4gICAgICAgIGJyZWFrO1xuICAgIH1cblxuICAgIGlmKGNvbnRleHQpIHtcbiAgICAgIGZvcm1hdHRlZFByb21wdCArPSBgXFxuPT09Q09OVEVYVD09PVxcbiR7Y29udGV4dH1gO1xuICAgIH1cblxuICAgIGlmKChwcm92aWRlciA9PT0gJ2N1cnNvcicgfHwgcHJvY2Vzcy5lbnYuQ1VSU09SX0VYVEVOU0lPTikgJiYgdGFzayA9PT0gJ2dlbmVyYXRlJykge1xuICAgICAgbG9nKCdVc2luZyBDdXJzb3IgSURFIGZvciBjb2RlIGdlbmVyYXRpb24uLi4nLCAnaW5mbycpO1xuICAgICAgbG9nKCdOb3RlOiBGb3IgZnVsbCBjb2RlIGdlbmVyYXRpb24gY2FwYWJpbGl0aWVzLCBwbGVhc2UgdXNlIEN1cnNvciBJREUgZGlyZWN0bHkgd2l0aCBDbWQrTCBvciBDbWQrSy4nLCAnaW5mbycpO1xuICAgICAgbG9nKCdUaGUgQ0xJIGludGVncmF0aW9uIGhhcyBsaW1pdGVkIGNhcGFiaWxpdGllcyBmb3IgY29kZSBnZW5lcmF0aW9uLicsICd3YXJuaW5nJyk7XG4gICAgfSBlbHNlIGlmKHByb3ZpZGVyID09PSAnY3Vyc29yJyB8fCBwcm9jZXNzLmVudi5DVVJTT1JfRVhURU5TSU9OKSB7XG4gICAgICBsb2coJ1VzaW5nIEN1cnNvciBJREUgZm9yIEFJIGFzc2lzdGFuY2UuLi4nLCAnaW5mbycpO1xuICAgICAgbG9nKCdOb3RlOiBUaGlzIGlzIGEgbGltaXRlZCBpbnRlZ3JhdGlvbi4gRm9yIGZ1bGwgQUkgY2FwYWJpbGl0aWVzLCB1c2UgQ3Vyc29yIElERSBkaXJlY3RseS4nLCAnaW5mbycpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2coYFVzaW5nICR7cHJvdmlkZXJ9IGZvciBBSSBhc3Npc3RhbmNlLi4uYCwgJ2luZm8nKTtcbiAgICB9XG5cbiAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGNhbGxBSVNlcnZpY2UoZm9ybWF0dGVkUHJvbXB0LCBvcHRpb25zLnF1aWV0IHx8IGZhbHNlKTtcblxuICAgIGxvZyhgXFxuJHtyZXNwb25zZX1gLCAnc3VjY2VzcycpO1xuXG4gICAgcmV0dXJuIHtyZXNwb25zZX07XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgbG9nKGAke2NoYWxrLnJlZCgnRXJyb3I6Jyl9ICR7ZXJyb3IubWVzc2FnZX1gLCAnZXJyb3InKTtcbiAgICByZXR1cm4ge2Vycm9yOiBlcnJvci5tZXNzYWdlfTtcbiAgfVxufTtcblxuZXhwb3J0IGNvbnN0IGFpID0gbmV3IENvbW1hbmQoJ2FpJylcbiAgLmRlc2NyaXB0aW9uKCdVc2UgQUkgdG8gaGVscCB3aXRoIGRldmVsb3BtZW50IHRhc2tzJylcbiAgLm9wdGlvbignLS1wcm92aWRlciA8cHJvdmlkZXI+JywgJ0FJIHByb3ZpZGVyIHRvIHVzZSAob3BlbmFpLCBhbnRocm9waWMsIGN1cnNvciknKVxuICAub3B0aW9uKCctLXRhc2sgPHRhc2s+JywgJ1Rhc2sgdG8gcGVyZm9ybSAoZXhwbGFpbiwgZ2VuZXJhdGUsIHRlc3QsIGFuYWx5emUsIGFzayknKVxuICAub3B0aW9uKCctLXByb21wdCA8cHJvbXB0PicsICdQcm9tcHQgdG8gc2VuZCB0byBBSScpXG4gIC5vcHRpb24oJy0tZmlsZSA8ZmlsZT4nLCAnRmlsZSB0byBhbmFseXplJylcbiAgLm9wdGlvbignLS1kaXIgPGRpcj4nLCAnRGlyZWN0b3J5IHRvIGFuYWx5emUnKVxuICAuYWN0aW9uKGFzeW5jIChvcHRpb25zOiBBSU9wdGlvbnMpID0+IHtcbiAgICBhd2FpdCBhaUZ1bmN0aW9uKG9wdGlvbnMpO1xuICB9KTtcblxuZXhw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