UNPKG

vibetunnel

Version:

Terminal sharing server with web interface - supports macOS, Linux, and headless environments

320 lines 39.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.createRepositoryRoutes = createRepositoryRoutes; const child_process_1 = require("child_process"); const express_1 = require("express"); const fs = __importStar(require("fs/promises")); const os = __importStar(require("os")); const path = __importStar(require("path")); const util_1 = require("util"); const constants_js_1 = require("../../shared/constants.js"); const logger_js_1 = require("../utils/logger.js"); const path_utils_js_1 = require("../utils/path-utils.js"); const logger = (0, logger_js_1.createLogger)('repositories'); const execAsync = (0, util_1.promisify)(child_process_1.exec); /** * Create routes for repository discovery functionality */ function createRepositoryRoutes() { const router = (0, express_1.Router)(); // List branches for a repository router.get('/repositories/branches', async (req, res) => { try { const repoPath = req.query.path; if (!repoPath || typeof repoPath !== 'string') { return res.status(400).json({ error: 'Missing or invalid path parameter', }); } const expandedPath = (0, path_utils_js_1.resolveAbsolutePath)(repoPath); logger.debug(`[GET /repositories/branches] Listing branches for: ${expandedPath}`); // Get all branches (local and remote) const branches = await listBranches(expandedPath); res.json(branches); } catch (error) { logger.error('[GET /repositories/branches] Error listing branches:', error); res.status(500).json({ error: 'Failed to list branches' }); } }); // Discover repositories endpoint router.get('/repositories/discover', async (req, res) => { try { const basePath = req.query.path || constants_js_1.DEFAULT_REPOSITORY_BASE_PATH; const maxDepth = Number.parseInt(req.query.maxDepth) || 3; logger.debug(`[GET /repositories/discover] Discovering repositories in: ${basePath}`); const expandedPath = (0, path_utils_js_1.resolveAbsolutePath)(basePath); logger.debug(`[GET /repositories/discover] Expanded path: ${expandedPath}`); // Check if the path exists try { await fs.access(expandedPath, fs.constants.R_OK); logger.debug(`[GET /repositories/discover] Path exists and is readable: ${expandedPath}`); } catch (error) { logger.error(`[GET /repositories/discover] Cannot access path: ${expandedPath}`, error); } const repositories = await discoverRepositories({ basePath: expandedPath, maxDepth, }); logger.debug(`[GET /repositories/discover] Found ${repositories.length} repositories`); res.json(repositories); } catch (error) { logger.error('[GET /repositories/discover] Error discovering repositories:', error); res.status(500).json({ error: 'Failed to discover repositories' }); } }); return router; } /** * Discover git repositories in the specified base path */ async function discoverRepositories(options) { const { basePath, maxDepth = 3 } = options; const repositories = []; logger.debug(`Starting repository discovery in ${basePath} with maxDepth=${maxDepth}`); async function scanDirectory(dirPath, depth) { if (depth > maxDepth) { return; } try { // Check if directory is accessible await fs.access(dirPath, fs.constants.R_OK); // First check if the current directory itself is a git repository // Only check at depth 0 to match Mac app behavior if (depth === 0) { const currentGitPath = path.join(dirPath, '.git'); try { await fs.access(currentGitPath, fs.constants.F_OK); // Current directory is a git repository const repository = await createDiscoveredRepository(dirPath); repositories.push(repository); logger.debug(`Found git repository at base path: ${dirPath}`); // Don't scan subdirectories of a git repository return; } catch { // Current directory is not a git repository, continue scanning } } const entries = await fs.readdir(dirPath, { withFileTypes: true }); for (const entry of entries) { if (!entry.isDirectory()) continue; // Skip hidden directories except .git if (entry.name.startsWith('.') && entry.name !== '.git') continue; const fullPath = path.join(dirPath, entry.name); // Check if this subdirectory is a git repository const gitPath = path.join(fullPath, '.git'); try { await fs.access(gitPath, fs.constants.F_OK); // If .git exists (either as a file or directory), this is a git repository const repository = await createDiscoveredRepository(fullPath); repositories.push(repository); logger.debug(`Found git repository: ${fullPath}`); // Don't scan subdirectories of a git repository } catch { // .git doesn't exist, scan subdirectories await scanDirectory(fullPath, depth + 1); } } } catch (error) { logger.debug(`Cannot access directory ${dirPath}:`, error); } } await scanDirectory(basePath, 0); // Sort by folder name repositories.sort((a, b) => a.folderName.localeCompare(b.folderName)); return repositories; } /** * List all branches (local and remote) for a repository */ async function listBranches(repoPath) { const branches = []; try { // Get current branch let currentBranch; try { const { stdout } = await execAsync('git branch --show-current', { cwd: repoPath }); currentBranch = stdout.trim(); } catch { logger.debug('Failed to get current branch, repository might be in detached HEAD state'); } // Get all local branches const { stdout: localBranchesOutput } = await execAsync('git branch', { cwd: repoPath }); const localBranches = localBranchesOutput .split('\n') .map((line) => line.trim()) .filter((line) => line.length > 0) .map((line) => { const isCurrent = line.startsWith('*'); const name = line.replace(/^\*?\s+/, ''); return { name, current: isCurrent || name === currentBranch, remote: false, }; }); branches.push(...localBranches); // Get all remote branches try { const { stdout: remoteBranchesOutput } = await execAsync('git branch -r', { cwd: repoPath }); const remoteBranches = remoteBranchesOutput .split('\n') .map((line) => line.trim()) .filter((line) => line.length > 0 && !line.includes('->')) // Skip HEAD pointers .map((line) => { const name = line.replace(/^\s+/, ''); return { name, current: false, remote: true, }; }); branches.push(...remoteBranches); } catch { logger.debug('No remote branches found'); } // Get worktree information try { const { stdout: worktreeOutput } = await execAsync('git worktree list --porcelain', { cwd: repoPath, }); const worktrees = parseWorktreeList(worktreeOutput); // Add worktree information to branches for (const worktree of worktrees) { const branch = branches.find((b) => b.name === worktree.branch || b.name === `refs/heads/${worktree.branch}` || b.name.replace(/^origin\//, '') === worktree.branch); if (branch) { branch.worktree = worktree.path; } } } catch { logger.debug('Failed to get worktree information'); } // Sort branches: current first, then local, then remote branches.sort((a, b) => { if (a.current && !b.current) return -1; if (!a.current && b.current) return 1; if (!a.remote && b.remote) return -1; if (a.remote && !b.remote) return 1; return a.name.localeCompare(b.name); }); return branches; } catch (error) { logger.error('Error listing branches:', error); throw error; } } /** * Parse worktree list output */ function parseWorktreeList(output) { const worktrees = []; const lines = output.trim().split('\n'); let current = {}; for (const line of lines) { if (line === '') { if (current.path && current.branch) { worktrees.push({ path: current.path, branch: current.branch }); } current = {}; continue; } const [key, ...valueParts] = line.split(' '); const value = valueParts.join(' '); if (key === 'worktree') { current.path = value; } else if (key === 'branch') { current.branch = value.replace(/^refs\/heads\//, ''); } } // Handle last worktree if (current.path && current.branch) { worktrees.push({ path: current.path, branch: current.branch }); } return worktrees; } /** * Create a DiscoveredRepository from a path */ async function createDiscoveredRepository(repoPath) { const folderName = path.basename(repoPath); // Get last modified date const stats = await fs.stat(repoPath); const lastModified = stats.mtime.toISOString(); // Get relative path from home directory const homeDir = os.homedir(); const relativePath = repoPath.startsWith(homeDir) ? `~${repoPath.slice(homeDir.length)}` : repoPath; // Get current git branch let gitBranch; try { const { stdout: branch } = await execAsync('git branch --show-current', { cwd: repoPath, }); gitBranch = branch.trim(); } catch { // Failed to get branch - repository might not have any commits yet logger.debug(`Failed to get git branch for ${repoPath}`); } return { id: `${folderName}-${stats.ino}`, path: repoPath, folderName, lastModified, relativePath, gitBranch, }; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVwb3NpdG9yaWVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3NlcnZlci9yb3V0ZXMvcmVwb3NpdG9yaWVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBcUNBLHdEQTREQztBQWpHRCxpREFBcUM7QUFDckMscUNBQWlDO0FBQ2pDLGdEQUFrQztBQUNsQyx1Q0FBeUI7QUFDekIsMkNBQTZCO0FBQzdCLCtCQUFpQztBQUNqQyw0REFBeUU7QUFDekUsa0RBQWtEO0FBQ2xELDBEQUE2RDtBQUU3RCxNQUFNLE1BQU0sR0FBRyxJQUFBLHdCQUFZLEVBQUMsY0FBYyxDQUFDLENBQUM7QUFDNUMsTUFBTSxTQUFTLEdBQUcsSUFBQSxnQkFBUyxFQUFDLG9CQUFJLENBQUMsQ0FBQztBQXVCbEM7O0dBRUc7QUFDSCxTQUFnQixzQkFBc0I7SUFDcEMsTUFBTSxNQUFNLEdBQUcsSUFBQSxnQkFBTSxHQUFFLENBQUM7SUFFeEIsaUNBQWlDO0lBQ2pDLE1BQU0sQ0FBQyxHQUFHLENBQUMsd0JBQXdCLEVBQUUsS0FBSyxFQUFFLEdBQUcsRUFBRSxHQUFHLEVBQUUsRUFBRTtRQUN0RCxJQUFJLENBQUM7WUFDSCxNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLElBQWMsQ0FBQztZQUUxQyxJQUFJLENBQUMsUUFBUSxJQUFJLE9BQU8sUUFBUSxLQUFLLFFBQVEsRUFBRSxDQUFDO2dCQUM5QyxPQUFPLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDO29CQUMxQixLQUFLLEVBQUUsbUNBQW1DO2lCQUMzQyxDQUFDLENBQUM7WUFDTCxDQUFDO1lBRUQsTUFBTSxZQUFZLEdBQUcsSUFBQSxtQ0FBbUIsRUFBQyxRQUFRLENBQUMsQ0FBQztZQUNuRCxNQUFNLENBQUMsS0FBSyxDQUFDLHNEQUFzRCxZQUFZLEVBQUUsQ0FBQyxDQUFDO1lB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QVEsRUFBRSxDQUFDLENBQUM7SUFDM0QsQ0FBQztJQUVELE9BQU87UUFDTCxFQUFFLEVBQUUsR0FBRyxVQUFVLElBQUksS0FBSyxDQUFDLEdBQUcsRUFBRTtRQUNoQyxJQUFJLEVBQUUsUUFBUTtRQUNkLFVBQVU7UUFDVixZQUFZO1FBQ1osWUFBWTtRQUNaLFNBQVM7S0FDVixDQUFDO0FBQ0osQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4ZWMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCB7IFJvdXRlciB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMvcHJvbWlzZXMnO1xuaW1wb3J0ICogYXMgb3MgZnJvbSAnb3MnO1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCB7IHByb21pc2lmeSB9IGZyb20gJ3V0aWwnO1xuaW1wb3J0IHsgREVGQVVMVF9SRVBPU0lUT1JZX0JBU0VfUEFUSCB9IGZyb20gJy4uLy4uL3NoYXJlZC9jb25zdGFudHMuanMnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IHJlc29sdmVBYnNvbHV0ZVBhdGggfSBmcm9tICcuLi91dGlscy9wYXRoLXV0aWxzLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdyZXBvc2l0b3JpZXMnKTtcbmNvbnN0IGV4ZWNBc3luYyA9IHByb21pc2lmeShleGVjKTtcblxuZXhwb3J0IGludGVyZmFjZSBEaXNjb3ZlcmVkUmVwb3NpdG9yeSB7XG4gIGlkOiBzdHJpbmc7XG4gIHBhdGg6IHN0cmluZztcbiAgZm9sZGVyTmFtZTogc3RyaW5nO1xuICBsYXN0TW9kaWZpZWQ6IHN0cmluZztcbiAgcmVsYXRpdmVQYXRoOiBzdHJpbmc7XG4gIGdpdEJyYW5jaD86IHN0cmluZztcbn1cblxuZXhwb3J0IGludGVyZmFjZSBCcmFuY2gge1xuICBuYW1lOiBzdHJpbmc7XG4gIGN1cnJlbnQ6IGJvb2xlYW47XG4gIHJlbW90ZTogYm9vbGVhbjtcbiAgd29ya3RyZWU/OiBzdHJpbmc7XG59XG5cbmludGVyZmFjZSBSZXBvc2l0b3J5U2VhcmNoT3B0aW9ucyB7XG4gIGJhc2VQYXRoOiBzdHJpbmc7XG4gIG1heERlcHRoPzogbnVtYmVyO1xufVxuXG4vKipcbiAqIENyZWF0ZSByb3V0ZXMgZm9yIHJlcG9zaXRvcnkgZGlzY292ZXJ5IGZ1bmN0aW9uYWxpdHlcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVJlcG9zaXRvcnlSb3V0ZXMoKTogUm91dGVyIHtcbiAgY29uc3Qgcm91dGVyID0gUm91dGVyKCk7XG5cbiAgLy8gTGlzdCBicmFuY2hlcyBmb3IgYSByZXBvc2l0b3J5XG4gIHJvdXRlci5nZXQoJy9yZXBvc2l0b3JpZXMvYnJhbmNoZXMnLCBhc3luYyAocmVxLCByZXMpID0+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVwb1BhdGggPSByZXEucXVlcnkucGF0aCBhcyBzdHJpbmc7XG5cbiAgICAgIGlmICghcmVwb1BhdGggfHwgdHlwZW9mIHJlcG9QYXRoICE9PSAnc3RyaW5nJykge1xuICAgICAgICByZXR1cm4gcmVzLnN0YXR1cyg0MDApLmpzb24oe1xuICAgICAgICAgIGVycm9yOiAnTWlzc2luZyBvciBpbnZhbGlkIHBhdGggcGFyYW1ldGVyJyxcbiAgICAgICAgfSk7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGV4cGFuZGVkUGF0aCA9IHJlc29sdmVBYnNvbHV0ZVBhdGgocmVwb1BhdGgpO1xuICAgICAgbG9nZ2VyLmRlYnVnKGBbR0VUIC9yZXBvc2l0b3JpZXMvYnJhbmNoZXNdIExpc3RpbmcgYnJhbmNoZXMgZm9yOiAke2V4cGFuZGVkUGF0aH1gKTtcblxuICAgICAgLy8gR2V0IGFsbCBicmFuY2hlcyAobG9jYWwgYW5kIHJlbW90ZSlcbiAgICAgIGNvbnN0IGJyYW5jaGVzID0gYXdhaXQgbGlzdEJyYW5jaGVzKGV4cGFuZGVkUGF0aCk7XG5cbiAgICAgIHJlcy5qc29uKGJyYW5jaGVzKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdbR0VUIC9yZXBvc2l0b3JpZXMvYnJhbmNoZXNdIEVycm9yIGxpc3RpbmcgYnJhbmNoZXM6JywgZXJyb3IpO1xuICAgICAgcmVzLnN0YXR1cyg1MDApLmpzb24oeyBlcnJvcjogJ0ZhaWxlZCB0byBsaXN0IGJyYW5jaGVzJyB9KTtcbiAgICB9XG4gIH0pO1xuXG4gIC8vIERpc2NvdmVyIHJlcG9zaXRvcmllcyBlbmRwb2ludFxuICByb3V0ZXIuZ2V0KCcvcmVwb3NpdG9yaWVzL2Rpc2NvdmVyJywgYXN5bmMgKHJlcSwgcmVzKSA9PiB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGJhc2VQYXRoID0gKHJlcS5xdWVyeS5wYXRoIGFzIHN0cmluZykgfHwgREVGQVVMVF9SRVBPU0lUT1JZX0JBU0VfUEFUSDtcbiAgICAgIGNvbnN0IG1heERlcHRoID0gTnVtYmVyLnBhcnNlSW50KHJlcS5xdWVyeS5tYXhEZXB0aCBhcyBzdHJpbmcpIHx8IDM7XG5cbiAgICAgIGxvZ2dlci5kZWJ1ZyhgW0dFVCAvcmVwb3NpdG9yaWVzL2Rpc2NvdmVyXSBEaXNjb3ZlcmluZyByZXBvc2l0b3JpZXMgaW46ICR7YmFzZVBhdGh9YCk7XG5cbiAgICAgIGNvbnN0IGV4cGFuZGVkUGF0aCA9IHJlc29sdmVBYnNvbHV0ZVBhdGgoYmFzZVBhdGgpO1xuICAgICAgbG9nZ2VyLmRlYnVnKGBbR0VUIC9yZXBvc2l0b3JpZXMvZGlzY292ZXJdIEV4cGFuZGVkIHBhdGg6ICR7ZXhwYW5kZWRQYXRofWApO1xuXG4gICAgICAvLyBDaGVjayBpZiB0aGUgcGF0aCBleGlzdHNcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IGZzLmFjY2VzcyhleHBhbmRlZFBhdGgsIGZzLmNvbnN0YW50cy5SX09LKTtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKGBbR0VUIC9yZXBvc2l0b3JpZXMvZGlzY292ZXJdIFBhdGggZXhpc3RzIGFuZCBpcyByZWFkYWJsZTogJHtleHBhbmRlZFBhdGh9YCk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dnZXIuZXJyb3IoYFtHRVQgL3JlcG9zaXRvcmllcy9kaXNjb3Zlcl0gQ2Fubm90IGFjY2VzcyBwYXRoOiAke2V4cGFuZGVkUGF0aH1gLCBlcnJvcik7XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHJlcG9zaXRvcmllcyA9IGF3YWl0IGRpc2NvdmVyUmVwb3NpdG9yaWVzKHtcbiAgICAgICAgYmFzZVBhdGg6IGV4cGFuZGVkUGF0aCxcbiAgICAgICAgbWF4RGVwdGgsXG4gICAgICB9KTtcblxuICAgICAgbG9nZ2VyLmRlYnVnKGBbR0VUIC9yZXBvc2l0b3JpZXMvZGlzY292ZXJdIEZvdW5kICR7cmVwb3NpdG9yaWVzLmxlbmd0aH0gcmVwb3NpdG9yaWVzYCk7XG4gICAgICByZXMuanNvbihyZXBvc2l0b3JpZXMpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ1tHRVQgL3JlcG9zaXRvcmllcy9kaXNjb3Zlcl0gRXJyb3IgZGlzY292ZXJpbmcgcmVwb3NpdG9yaWVzOicsIGVycm9yKTtcbiAgICAgIHJlcy5zdGF0dXMoNTAwKS5qc29uKHsgZXJyb3I6ICdGYWlsZWQgdG8gZGlzY292ZXIgcmVwb3NpdG9yaWVzJyB9KTtcbiAgICB9XG4gIH0pO1xuXG4gIHJldHVybiByb3V0ZXI7XG59XG5cbi8qKlxuICogRGlzY292ZXIgZ2l0IHJlcG9zaXRvcmllcyBpbiB0aGUgc3BlY2lmaWVkIGJhc2UgcGF0aFxuICovXG5hc3luYyBmdW5jdGlvbiBkaXNjb3ZlclJlcG9zaXRvcmllcyhcbiAgb3B0aW9uczogUmVwb3NpdG9yeVNlYXJjaE9wdGlvbnNcbik6IFByb21pc2U8RGlzY292ZXJlZFJlcG9zaXRvcnlbXT4ge1xuICBjb25zdCB7IGJhc2VQYXRoLCBtYXhEZXB0aCA9IDMgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IHJlcG9zaXRvcmllczogRGlzY292ZXJlZFJlcG9zaXRvcnlbXSA9IFtdO1xuXG4gIGxvZ2dlci5kZWJ1ZyhgU3RhcnRpbmcgcmVwb3NpdG9yeSBkaXNjb3ZlcnkgaW4gJHtiYXNlUGF0aH0gd2l0aCBtYXhEZXB0aD0ke21heERlcHRofWApO1xuXG4gIGFzeW5jIGZ1bmN0aW9uIHNjYW5EaXJlY3RvcnkoZGlyUGF0aDogc3RyaW5nLCBkZXB0aDogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKGRlcHRoID4gbWF4RGVwdGgpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgLy8gQ2hlY2sgaWYgZGlyZWN0b3J5IGlzIGFjY2Vzc2libGVcbiAgICAgIGF3YWl0IGZzLmFjY2VzcyhkaXJQYXRoLCBmcy5jb25zdGFudHMuUl9PSyk7XG5cbiAgICAgIC8vIEZpcnN0IGNoZWNrIGlmIHRoZSBjdXJyZW50IGRpcmVjdG9yeSBpdHNlbGYgaXMgYSBnaXQgcmVwb3NpdG9yeVxuICAgICAgLy8gT25seSBjaGVjayBhdCBkZXB0aCAwIHRvIG1hdGNoIE1hYyBhcHAgYmVoYXZpb3JcbiAgICAgIGlmIChkZXB0aCA9PT0gMCkge1xuICAgICAgICBjb25zdCBjdXJyZW50R2l0UGF0aCA9IHBhdGguam9pbihkaXJQYXRoLCAnLmdpdCcpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IGZzLmFjY2VzcyhjdXJyZW50R2l0UGF0aCwgZnMuY29uc3RhbnRzLkZfT0spO1xuICAgICAgICAgIC8vIEN1cnJlbnQgZGlyZWN0b3J5IGlzIGEgZ2l0IHJlcG9zaXRvcnlcbiAgICAgICAgICBjb25zdCByZXBvc2l0b3J5ID0gYXdhaXQgY3JlYXRlRGlzY292ZXJlZFJlcG9zaXRvcnkoZGlyUGF0aCk7XG4gICAgICAgICAgcmVwb3NpdG9yaWVzLnB1c2gocmVwb3NpdG9yeSk7XG4gICAgICAgICAgbG9nZ2VyLmRlYnVnKGBGb3VuZCBnaXQgcmVwb3NpdG9yeSBhdCBiYXNlIHBhdGg6ICR7ZGlyUGF0aH1gKTtcbiAgICAgICAgICAvLyBEb24ndCBzY2FuIHN1YmRpcmVjdG9yaWVzIG9mIGEgZ2l0IHJlcG9zaXRvcnlcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgIC8vIEN1cnJlbnQgZGlyZWN0b3J5IGlzIG5vdCBhIGdpdCByZXBvc2l0b3J5LCBjb250aW51ZSBzY2FubmluZ1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGVudHJpZXMgPSBhd2FpdCBmcy5yZWFkZGlyKGRpclBhdGgsIHsgd2l0aEZpbGVUeXBlczogdHJ1ZSB9KTtcblxuICAgICAgZm9yIChjb25zdCBlbnRyeSBvZiBlbnRyaWVzKSB7XG4gICAgICAgIGlmICghZW50cnkuaXNEaXJlY3RvcnkoKSkgY29udGludWU7XG5cbiAgICAgICAgLy8gU2tpcCBoaWRkZW4gZGlyZWN0b3JpZXMgZXhjZXB0IC5naXRcbiAgICAgICAgaWYgKGVudHJ5Lm5hbWUuc3RhcnRzV2l0aCgnLicpICYmIGVudHJ5Lm5hbWUgIT09ICcuZ2l0JykgY29udGludWU7XG5cbiAgICAgICAgY29uc3QgZnVsbFBhdGggPSBwYXRoLmpvaW4oZGlyUGF0aCwgZW50cnkubmFtZSk7XG5cbiAgICAgICAgLy8gQ2hlY2sgaWYgdGhpcyBzdWJkaXJlY3RvcnkgaXMgYSBnaXQgcmVwb3NpdG9yeVxuICAgICAgICBjb25zdCBnaXRQYXRoID0gcGF0aC5qb2luKGZ1bGxQYXRoLCAnLmdpdCcpO1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IGZzLmFjY2VzcyhnaXRQYXRoLCBmcy5jb25zdGFudHMuRl9PSyk7XG4gICAgICAgICAgLy8gSWYgLmdpdCBleGlzdHMgKGVpdGhlciBhcyBhIGZpbGUgb3IgZGlyZWN0b3J5KSwgdGhpcyBpcyBhIGdpdCByZXBvc2l0b3J5XG4gICAgICAgICAgY29uc3QgcmVwb3NpdG9yeSA9IGF3YWl0IGNyZWF0ZURpc2NvdmVyZWRSZXBvc2l0b3J5KGZ1bGxQYXRoKTtcbiAgICAgICAgICByZXBvc2l0b3JpZXMucHVzaChyZXBvc2l0b3J5KTtcbiAgICAgICAgICBsb2dnZXIuZGVidWcoYEZvdW5kIGdpdCByZXBvc2l0b3J5OiAke2Z1bGxQYXRofWApO1xuICAgICAgICAgIC8vIERvbid0IHNjYW4gc3ViZGlyZWN0b3JpZXMgb2YgYSBnaXQgcmVwb3NpdG9yeVxuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvLyAuZ2l0IGRvZXNuJ3QgZXhpc3QsIHNjYW4gc3ViZGlyZWN0b3JpZXNcbiAgICAgICAgICBhd2FpdCBzY2FuRGlyZWN0b3J5KGZ1bGxQYXRoLCBkZXB0aCArIDEpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgQ2Fubm90IGFjY2VzcyBkaXJlY3RvcnkgJHtkaXJQYXRofTpgLCBlcnJvcik7XG4gICAgfVxuICB9XG5cbiAgYXdhaXQgc2NhbkRpcmVjdG9yeShiYXNlUGF0aCwgMCk7XG5cbiAgLy8gU29ydCBieSBmb2xkZXIgbmFtZVxuICByZXBvc2l0b3JpZXMuc29ydCgoYSwgYikgPT4gYS5mb2xkZXJOYW1lLmxvY2FsZUNvbXBhcmUoYi5mb2xkZXJOYW1lKSk7XG5cbiAgcmV0dXJuIHJlcG9zaXRvcmllcztcbn1cblxuLyoqXG4gKiBMaXN0IGFsbCBicmFuY2hlcyAobG9jYWwgYW5kIHJlbW90ZSkgZm9yIGEgcmVwb3NpdG9yeVxuICovXG5hc3luYyBmdW5jdGlvbiBsaXN0QnJhbmNoZXMocmVwb1BhdGg6IHN0cmluZyk6IFByb21pc2U8QnJhbmNoW10+IHtcbiAgY29uc3QgYnJhbmNoZXM6IEJyYW5jaFtdID0gW107XG5cbiAgdHJ5IHtcbiAgICAvLyBHZXQgY3VycmVudCBicmFuY2hcbiAgICBsZXQgY3VycmVudEJyYW5jaDogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCB7IHN0ZG91dCB9ID0gYXdhaXQgZXhlY0FzeW5jKCdnaXQgYnJhbmNoIC0tc2hvdy1jdXJyZW50JywgeyBjd2Q6IHJlcG9QYXRoIH0pO1xuICAgICAgY3VycmVudEJyYW5jaCA9IHN0ZG91dC50cmltKCk7XG4gICAgfSBjYXRjaCB7XG4gICAgICBsb2dnZXIuZGVidWcoJ0ZhaWxlZCB0byBnZXQgY3VycmVudCBicmFuY2gsIHJlcG9zaXRvcnkgbWlnaHQgYmUgaW4gZGV0YWNoZWQgSEVBRCBzdGF0ZScpO1xuICAgIH1cblxuICAgIC8vIEdldCBhbGwgbG9jYWwgYnJhbmNoZXNcbiAgICBjb25zdCB7IHN0ZG91dDogbG9jYWxCcmFuY2hlc091dHB1dCB9ID0gYXdhaXQgZXhlY0FzeW5jKCdnaXQgYnJhbmNoJywgeyBjd2Q6IHJlcG9QYXRoIH0pO1xuICAgIGNvbnN0IGxvY2FsQnJhbmNoZXMgPSBsb2NhbEJyYW5jaGVzT3V0cHV0XG4gICAgICAuc3BsaXQoJ1xcbicpXG4gICAgICAubWFwKChsaW5lKSA9PiBsaW5lLnRyaW0oKSlcbiAgICAgIC5maWx0ZXIoKGxpbmUpID0+IGxpbmUubGVuZ3RoID4gMClcbiAgICAgIC5tYXAoKGxpbmUpID0+IHtcbiAgICAgICAgY29uc3QgaXNDdXJyZW50ID0gbGluZS5zdGFydHNXaXRoKCcqJyk7XG4gICAgICAgIGNvbnN0IG5hbWUgPSBsaW5lLnJlcGxhY2UoL15cXCo/XFxzKy8sICcnKTtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBuYW1lLFxuICAgICAgICAgIGN1cnJlbnQ6IGlzQ3VycmVudCB8fCBuYW1lID09PSBjdXJyZW50QnJhbmNoLFxuICAgICAgICAgIHJlbW90ZTogZmFsc2UsXG4gICAgICAgIH07XG4gICAgICB9KTtcblxuICAgIGJyYW5jaGVzLnB1c2goLi4ubG9jYWxCcmFuY2hlcyk7XG5cbiAgICAvLyBHZXQgYWxsIHJlbW90ZSBicmFuY2hlc1xuICAgIHRyeSB7XG4gICAgICBjb25zdCB7IHN0ZG91dDogcmVtb3RlQnJhbmNoZXNPdXRwdXQgfSA9IGF3YWl0IGV4ZWNBc3luYygnZ2l0IGJyYW5jaCAtcicsIHsgY3dkOiByZXBvUGF0aCB9KTtcbiAgICAgIGNvbnN0IHJlbW90ZUJyYW5jaGVzID0gcmVtb3RlQnJhbmNoZXNPdXRwdXRcbiAgICAgICAgLnNwbGl0KCdcXG4nKVxuICAgICAgICAubWFwKChsaW5lKSA9PiBsaW5lLnRyaW0oKSlcbiAgICAgICAgLmZpbHRlcigobGluZSkgPT4gbGluZS5sZW5ndGggPiAwICYmICFsaW5lLmluY2x1ZGVzKCctPicpKSAvLyBTa2lwIEhFQUQgcG9pbnRlcnNcbiAgICAgICAgLm1hcCgobGluZSkgPT4ge1xuICAgICAgICAgIGNvbnN0IG5hbWUgPSBsaW5lLnJlcGxhY2UoL15cXHMrLywgJycpO1xuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICBuYW1lLFxuICAgICAgICAgICAgY3VycmVudDogZmFsc2UsXG4gICAgICAgICAgICByZW1vdGU6IHRydWUsXG4gICAgICAgICAgfTtcbiAgICAgICAgfSk7XG5cbiAgICAgIGJyYW5jaGVzLnB1c2goLi4ucmVtb3RlQnJhbmNoZXMpO1xuICAgIH0gY2F0Y2gge1xuICAgICAgbG9nZ2VyLmRlYnVnKCdObyByZW1vdGUgYnJhbmNoZXMgZm91bmQnKTtcbiAgICB9XG5cbiAgICAvLyBHZXQgd29ya3RyZWUgaW5mb3JtYXRpb25cbiAgICB0cnkge1xuICAgICAgY29uc3QgeyBzdGRvdXQ6IHdvcmt0cmVlT3V0cHV0IH0gPSBhd2FpdCBleGVjQXN5bmMoJ2dpdCB3b3JrdHJlZSBsaXN0IC0tcG9yY2VsYWluJywge1xuICAgICAgICBjd2Q6IHJlcG9QYXRoLFxuICAgICAgfSk7XG4gICAgICBjb25zdCB3b3JrdHJlZXMgPSBwYXJzZVdvcmt0cmVlTGlzdCh3b3JrdHJlZU91dHB1dCk7XG5cbiAgICAgIC8vIEFkZCB3b3JrdHJlZSBpbmZvcm1hdGlvbiB0byBicmFuY2hlc1xuICAgICAgZm9yIChjb25zdCB3b3JrdHJlZSBvZiB3b3JrdHJlZXMpIHtcbiAgICAgICAgY29uc3QgYnJhbmNoID0gYnJhbmNoZXMuZmluZChcbiAgICAgICAgICAoYikgPT5cbiAgICAgICAgICAgIGIubmFtZSA9PT0gd29ya3RyZWUuYnJhbmNoIHx8XG4gICAgICAgICAgICBiLm5hbWUgPT09IGByZWZzL2hlYWRzLyR7d29ya3RyZWUuYnJhbmNofWAgfHxcbiAgICAgICAgICAgIGIubmFtZS5yZXBsYWNlKC9eb3JpZ2luXFwvLywgJycpID09PSB3b3JrdHJlZS5icmFuY2hcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKGJyYW5jaCkge1xuICAgICAgICAgIGJyYW5jaC53b3JrdHJlZSA9IHdvcmt0cmVlLnBhdGg7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZygnRmFpbGVkIHRvIGdldCB3b3JrdHJlZSBpbmZvcm1hdGlvbicpO1xuICAgIH1cblxuICAgIC8vIFNvcnQgYnJhbmNoZXM6IGN1cnJlbnQgZmlyc3QsIHRoZW4gbG9jYWwsIHRoZW4gcmVtb3RlXG4gICAgYnJhbmNoZXMuc29ydCgoYSwgYikgPT4ge1xuICAgICAgaWYgKGEuY3VycmVudCAmJiAhYi5jdXJyZW50KSByZXR1cm4gLTE7XG4gICAgICBpZiAoIWEuY3VycmVudCAmJiBiLmN1cnJlbnQpIHJldHVybiAxO1xuICAgICAgaWYgKCFhLnJlbW90ZSAmJiBiLnJlbW90ZSkgcmV0dXJuIC0xO1xuICAgICAgaWYgKGEucmVtb3RlICYmICFiLnJlbW90ZSkgcmV0dXJuIDE7XG4gICAgICByZXR1cm4gYS5uYW1lLmxvY2FsZUNvbXBhcmUoYi5uYW1lKTtcbiAgICB9KTtcblxuICAgIHJldHVybiBicmFuY2hlcztcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICBsb2dnZXIuZXJyb3IoJ0Vycm9yIGxpc3RpbmcgYnJhbmNoZXM6JywgZXJyb3IpO1xuICAgIHRocm93IGVycm9yO1xuICB9XG59XG5cbi8qKlxuICogUGFyc2Ugd29ya3RyZWUgbGlzdCBvdXRwdXRcbiAqL1xuZnVuY3Rpb24gcGFyc2VXb3JrdHJlZUxpc3Qob3V0cHV0OiBzdHJpbmcpOiBBcnJheTx7IHBhdGg6IHN0cmluZzsgYnJhbmNoOiBzdHJpbmcgfT4ge1xuICBjb25zdCB3b3JrdHJlZXM6IEFycmF5PHsgcGF0aDogc3RyaW5nOyBicmFuY2g6IHN0cmluZyB9PiA9IFtdO1xuICBjb25zdCBsaW5lcyA9IG91dHB1dC50cmltKCkuc3BsaXQoJ1xcbicpO1xuXG4gIGxldCBjdXJyZW50OiB7IHBhdGg/OiBzdHJpbmc7IGJyYW5jaD86IHN0cmluZyB9ID0ge307XG5cbiAgZm9yIChjb25zdCBsaW5lIG9mIGxpbmVzKSB7XG4gICAgaWYgKGxpbmUgPT09ICcnKSB7XG4gICAgICBpZiAoY3VycmVudC5wYXRoICYmIGN1cnJlbnQuYnJhbmNoKSB7XG4gICAgICAgIHdvcmt0cmVlcy5wdXNoKHsgcGF0aDogY3VycmVudC5wYXRoLCBicmFuY2g6IGN1cnJlbnQuYnJhbmNoIH0pO1xuICAgICAgfVxuICAgICAgY3VycmVudCA9IHt9O1xuICAgICAgY29udGludWU7XG4gICAgfVxuXG4gICAgY29uc3QgW2tleSwgLi4udmFsdWVQYXJ0c10gPSBsaW5lLnNwbGl0KCcgJyk7XG4gICAgY29uc3QgdmFsdWUgPSB2YWx1ZVBhcnRzLmpvaW4oJyAnKTtcblxuICAgIGlmIChrZXkgPT09ICd3b3JrdHJlZScpIHtcbiAgICAgIGN1cnJlbnQucGF0aCA9IHZhbHVlO1xuICAgIH0gZWxzZSBpZiAoa2V5ID09PSAnYnJhbmNoJykge1xuICAgICAgY3VycmVudC5icmFuY2ggPSB2YWx1ZS5yZXBsYWNlKC9ecmVmc1xcL2hlYWRzXFwvLywgJycpO1xuICAgIH1cbiAgfVxuXG4gIC8vIEhhbmRsZSBsYXN0IHdvcmt0cmVlXG4gIGlmIChjdXJyZW50LnBhdGggJiYgY3VycmVudC5icmFuY2gpIHtcbiAgICB3b3JrdHJlZXMucHVzaCh7IHBhdGg6IGN1cnJlbnQucGF0aCwgYnJhbmNoOiBjdXJyZW50LmJyYW5jaCB9KTtcbiAgfVxuXG4gIHJldHVybiB3b3JrdHJlZXM7XG59XG5cbi8qKlxuICogQ3JlYXRlIGEgRGlzY292ZXJlZFJlcG9zaXRvcnkgZnJvbSBhIHBhdGhcbiAqL1xuYXN5bmMgZnVuY3Rpb24gY3JlYXRlRGlzY292ZXJlZFJlcG9zaXRvcnkocmVwb1BhdGg6IHN0cmluZyk6IFByb21pc2U8RGlzY292ZXJlZFJlcG9zaXRvcnk+IHtcbiAgY29uc3QgZm9sZGVyTmFtZSA9IHBhdGguYmFzZW5hbWUocmVwb1BhdGgpO1xuXG4gIC8vIEdldCBsYXN0IG1vZGlmaWVkIGRhdGVcbiAgY29uc3Qgc3RhdHMgPSBhd2FpdCBmcy5zdGF0KHJlcG9QYXRoKTtcbiAgY29uc3QgbGFzdE1vZGlmaWVkID0gc3RhdHMubXRpbWUudG9JU09TdHJpbmcoKTtcblxuICAvLyBHZXQgcmVsYXRpdmUgcGF0aCBmcm9tIGhvbWUgZGlyZWN0b3J5XG4gIGNvbnN0IGhvbWVEaXIgPSBvcy5ob21lZGlyKCk7XG4gIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHJlcG9QYXRoLnN0YXJ0c1dpdGgoaG9tZURpcilcbiAgICA/IGB+JHtyZXBvUGF0aC5zbGljZShob21lRGlyLmxlbmd0aCl9YFxuICAgIDogcmVwb1BhdGg7XG5cbiAgLy8gR2V0IGN1cnJlbnQgZ2l0IGJyYW5jaFxuICBsZXQgZ2l0QnJhbmNoOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gIHRyeSB7XG4gICAgY29uc3QgeyBzdGRvdXQ6IGJyYW5jaCB9ID0gYXdhaXQgZXhlY0FzeW5jKCdnaXQgYnJhbmNoIC0tc2hvdy1jdXJyZW50Jywge1xuICAgICAgY3dkOiByZXBvUGF0aCxcbiAgICB9KTtcbiAgICBnaXRCcmFuY2ggPSBicmFuY2gudHJpbSgpO1xuICB9IGNhdGNoIHtcbiAgICAvLyBGYWlsZWQgdG8gZ2V0IGJyYW5jaCAtIHJlcG9zaXRvcnkgbWlnaHQgbm90IGhhdmUgYW55IGNvbW1pdHMgeWV0XG4gICAgbG9nZ2VyLmRlYnVnKGBGYWlsZWQgdG8gZ2V0IGdpdCBicmFuY2ggZm9yICR7cmVwb1BhdGh9YCk7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGlkOiBgJHtmb2xkZXJOYW1lfS0ke3N0YXRzLmlub31gLFxuICAgIHBhdGg6IHJlcG9QYXRoLFxuICAgIGZvbGRlck5hbWUsXG4gICAgbGFzdE1vZGlmaWVkLFxuICAgIHJlbGF0aXZlUGF0aCxcbiAgICBnaXRCcmFuY2gsXG4gIH07XG59XG4iXX0=