UNPKG

vibetunnel

Version:

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

487 lines 63.1 kB
"use strict"; /** * API Socket Server for VibeTunnel control operations * Provides a Unix socket interface for CLI commands (vt) to communicate with the server */ 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.apiSocketServer = exports.ApiSocketServer = void 0; const fs = __importStar(require("fs")); const net = __importStar(require("net")); const os = __importStar(require("os")); const path = __importStar(require("path")); const util_1 = require("util"); const socket_protocol_js_1 = require("./pty/socket-protocol.js"); const git_error_js_1 = require("./utils/git-error.js"); const git_hooks_js_1 = require("./utils/git-hooks.js"); const logger_js_1 = require("./utils/logger.js"); const path_prettify_js_1 = require("./utils/path-prettify.js"); const version_js_1 = require("./version.js"); const control_protocol_js_1 = require("./websocket/control-protocol.js"); const control_unix_handler_js_1 = require("./websocket/control-unix-handler.js"); const logger = (0, logger_js_1.createLogger)('api-socket'); const execFile = (0, util_1.promisify)(require('child_process').execFile); /** * Execute a git command with proper error handling */ async function execGit(args, options = {}) { try { const { stdout, stderr } = await execFile('git', args, { cwd: options.cwd || process.cwd(), timeout: options.timeout || 5000, maxBuffer: 1024 * 1024, // 1MB env: { ...process.env, GIT_TERMINAL_PROMPT: '0' }, // Disable git prompts }); return { stdout: stdout.toString(), stderr: stderr.toString() }; } catch (error) { throw (0, git_error_js_1.createGitError)(error, 'Git command failed'); } } /** * API Socket Server that handles CLI commands via Unix socket */ class ApiSocketServer { constructor() { this.server = null; // Use control directory from environment or default const controlDir = process.env.VIBETUNNEL_CONTROL_DIR || path.join(os.homedir(), '.vibetunnel'); const socketDir = controlDir; // Ensure directory exists if (!fs.existsSync(socketDir)) { fs.mkdirSync(socketDir, { recursive: true }); } // Use a different socket name to avoid conflicts this.socketPath = path.join(socketDir, 'api.sock'); } /** * Set server info for status queries */ setServerInfo(port, url) { this.serverPort = port; this.serverUrl = url; } /** * Start the API socket server */ async start() { // Clean up any existing socket try { fs.unlinkSync(this.socketPath); } catch (_error) { // Ignore } return new Promise((resolve, reject) => { this.server = net.createServer((socket) => { this.handleConnection(socket); }); this.server.on('error', (error) => { logger.error('API socket server error:', error); reject(error); }); this.server.listen(this.socketPath, () => { logger.log(`API socket server listening on ${this.socketPath}`); resolve(); }); }); } /** * Stop the API socket server */ stop() { if (this.server) { this.server.close(); this.server = null; } // Clean up socket file try { fs.unlinkSync(this.socketPath); } catch (_error) { // Ignore } } /** * Handle incoming socket connections */ handleConnection(socket) { const parser = new socket_protocol_js_1.MessageParser(); socket.on('data', (data) => { parser.addData(data); for (const { type, payload } of parser.parseMessages()) { this.handleMessage(socket, type, payload); } }); socket.on('error', (error) => { logger.error('API socket connection error:', error); }); } /** * Handle incoming messages */ async handleMessage(socket, type, payload) { try { const data = (0, socket_protocol_js_1.parsePayload)(type, payload); switch (type) { case socket_protocol_js_1.MessageType.STATUS_REQUEST: await this.handleStatusRequest(socket); break; case socket_protocol_js_1.MessageType.GIT_FOLLOW_REQUEST: await this.handleGitFollowRequest(socket, data); break; case socket_protocol_js_1.MessageType.GIT_EVENT_NOTIFY: await this.handleGitEventNotify(socket, data); break; default: logger.warn(`Unhandled message type: ${type}`); } } catch (error) { logger.error('Failed to handle message:', error); this.sendError(socket, error instanceof Error ? error.message : 'Unknown error'); } } /** * Handle status request */ async handleStatusRequest(socket) { try { // Get current working directory for follow mode check const cwd = process.cwd(); // Check follow mode status let followMode; try { // Check if we're in a git repo const { stdout: repoPathOutput } = await execGit(['rev-parse', '--show-toplevel'], { cwd }); const repoPath = repoPathOutput.trim(); // Check if this is a worktree const { stdout: gitDirOutput } = await execGit(['rev-parse', '--git-dir'], { cwd }); const gitDir = gitDirOutput.trim(); const isWorktree = gitDir.includes('/.git/worktrees/'); // Find main repo path let mainRepoPath = repoPath; if (isWorktree) { mainRepoPath = gitDir.replace(/\/\.git\/worktrees\/.*$/, ''); } // Check for new worktree-based follow mode try { const { stdout } = await execGit(['config', 'vibetunnel.followWorktree'], { cwd: mainRepoPath, }); const followWorktree = stdout.trim(); if (followWorktree) { // Get branch name from worktree for display let branchName = path.basename(followWorktree); try { const { stdout: branchOutput } = await execGit(['branch', '--show-current'], { cwd: followWorktree, }); if (branchOutput.trim()) { branchName = branchOutput.trim(); } } catch (_e) { // Use directory name as fallback } followMode = { enabled: true, branch: branchName, repoPath: (0, path_prettify_js_1.prettifyPath)(followWorktree), }; } } catch (_e) { // No follow mode configured } } catch (_error) { // Not in a git repo } const response = { running: true, port: this.serverPort, url: this.serverUrl, version: version_js_1.VERSION, buildDate: version_js_1.BUILD_DATE, followMode, }; socket.write(socket_protocol_js_1.MessageBuilder.statusResponse(response)); } catch (error) { logger.error('Failed to get status:', error); this.sendError(socket, 'Failed to get server status'); } } /** * Handle Git follow mode request */ async handleGitFollowRequest(socket, request) { try { const { repoPath, branch, enable, worktreePath, mainRepoPath } = request; // Use new fields if available, otherwise fall back to old fields const targetMainRepo = mainRepoPath || repoPath; if (!targetMainRepo) { throw new Error('No repository path provided'); } const absoluteMainRepo = path.resolve(targetMainRepo); const absoluteWorktreePath = worktreePath ? path.resolve(worktreePath) : undefined; logger.debug(`${enable ? 'Enabling' : 'Disabling'} follow mode${absoluteWorktreePath ? ` for worktree: ${absoluteWorktreePath}` : branch ? ` for branch: ${branch}` : ''}`); if (enable) { // Check if Git hooks are already installed const hooksAlreadyInstalled = await (0, git_hooks_js_1.areHooksInstalled)(absoluteMainRepo); if (!hooksAlreadyInstalled) { // Install Git hooks logger.info('Installing Git hooks for follow mode'); const installResult = await (0, git_hooks_js_1.installGitHooks)(absoluteMainRepo); if (!installResult.success) { const response = { success: false, error: 'Failed to install Git hooks', }; socket.write(socket_protocol_js_1.MessageBuilder.gitFollowResponse(response)); return; } } // If we have a worktree path, use that. Otherwise try to find worktree from branch let followPath; let displayName; if (absoluteWorktreePath) { // Direct worktree path provided followPath = absoluteWorktreePath; // Get the branch name from the worktree for display try { const { stdout } = await execGit(['branch', '--show-current'], { cwd: absoluteWorktreePath, }); displayName = stdout.trim() || path.basename(absoluteWorktreePath); } catch { displayName = path.basename(absoluteWorktreePath); } } else if (branch) { // Try to find worktree for the branch try { const { stdout } = await execGit(['worktree', 'list', '--porcelain'], { cwd: absoluteMainRepo, }); const lines = stdout.split('\n'); let foundWorktree; for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith('worktree ')) { const worktreePath = lines[i].substring(9); // Check if next lines contain our branch if (i + 2 < lines.length && lines[i + 2] === `branch refs/heads/${branch}`) { if (worktreePath !== absoluteMainRepo) { foundWorktree = worktreePath; break; } } } } if (!foundWorktree) { throw new Error(`No worktree found for branch '${branch}'`); } followPath = foundWorktree; displayName = branch; } catch (error) { throw new Error(`Failed to find worktree: ${error instanceof Error ? error.message : String(error)}`); } } else { // No branch or worktree specified - try current branch try { const { stdout } = await execGit(['branch', '--show-current'], { cwd: absoluteMainRepo, }); const currentBranch = stdout.trim(); if (!currentBranch) { throw new Error('Not on a branch (detached HEAD)'); } // Recursively call with the current branch return this.handleGitFollowRequest(socket, { ...request, branch: currentBranch, }); } catch (error) { throw new Error(`Failed to get current branch: ${error instanceof Error ? error.message : String(error)}`); } } // Set the follow mode config with worktree path await execGit(['config', '--local', 'vibetunnel.followWorktree', followPath], { cwd: absoluteMainRepo, }); // Install hooks in both locations const mainRepoHooksInstalled = await (0, git_hooks_js_1.areHooksInstalled)(absoluteMainRepo); if (!mainRepoHooksInstalled) { logger.info('Installing Git hooks in main repository'); const installResult = await (0, git_hooks_js_1.installGitHooks)(absoluteMainRepo); if (!installResult.success) { throw new Error('Failed to install Git hooks in main repository'); } } const worktreeHooksInstalled = await (0, git_hooks_js_1.areHooksInstalled)(followPath); if (!worktreeHooksInstalled) { logger.info('Installing Git hooks in worktree'); const installResult = await (0, git_hooks_js_1.installGitHooks)(followPath); if (!installResult.success) { logger.warn('Failed to install Git hooks in worktree, continuing anyway'); } } // Send notification to Mac app if (control_unix_handler_js_1.controlUnixHandler.isMacAppConnected()) { const notification = (0, control_protocol_js_1.createControlEvent)('system', 'notification', { level: 'info', title: 'Follow Mode Enabled', message: `Now following ${displayName} in ${path.basename(absoluteMainRepo)}`, }); control_unix_handler_js_1.controlUnixHandler.sendToMac(notification); } const response = { success: true, currentBranch: displayName, }; socket.write(socket_protocol_js_1.MessageBuilder.gitFollowResponse(response)); } else { // Disable follow mode await execGit(['config', '--local', '--unset', 'vibetunnel.followWorktree'], { cwd: absoluteMainRepo, }); // Get the worktree path that was being followed let followedWorktree; try { const { stdout } = await execGit(['config', 'vibetunnel.followWorktree'], { cwd: absoluteMainRepo, }); followedWorktree = stdout.trim(); } catch { // No worktree was being followed } // Uninstall Git hooks from main repo logger.info('Uninstalling Git hooks from main repository'); const mainUninstallResult = await (0, git_hooks_js_1.uninstallGitHooks)(absoluteMainRepo); // Also uninstall from worktree if we know which one was being followed if (followedWorktree && followedWorktree !== absoluteMainRepo) { logger.info('Uninstalling Git hooks from worktree'); const worktreeUninstallResult = await (0, git_hooks_js_1.uninstallGitHooks)(followedWorktree); if (!worktreeUninstallResult.success) { logger.warn('Failed to uninstall some Git hooks from worktree:', worktreeUninstallResult.errors); } } if (!mainUninstallResult.success) { logger.warn('Failed to uninstall some Git hooks from main repo:', mainUninstallResult.errors); // Continue anyway - follow mode is still disabled } else { logger.info('Git hooks uninstalled successfully from main repository'); } // Send notification to Mac app if (control_unix_handler_js_1.controlUnixHandler.isMacAppConnected()) { const notification = (0, control_protocol_js_1.createControlEvent)('system', 'notification', { level: 'info', title: 'Follow Mode Disabled', message: `Follow mode disabled in ${path.basename(absoluteMainRepo)}`, }); control_unix_handler_js_1.controlUnixHandler.sendToMac(notification); } const response = { success: true, }; socket.write(socket_protocol_js_1.MessageBuilder.gitFollowResponse(response)); } } catch (error) { const response = { success: false, error: error instanceof Error ? error.message : 'Unknown error', }; socket.write(socket_protocol_js_1.MessageBuilder.gitFollowResponse(response)); } } /** * Handle Git event notification */ async handleGitEventNotify(socket, event) { logger.debug(`Git event notification received: ${event.type} for ${event.repoPath}`); try { // Forward the event to the HTTP endpoint which contains the sync logic const port = this.serverPort || 4020; const url = `http://localhost:${port}/api/git/event`; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ repoPath: event.repoPath, event: event.type, // Branch information would need to be extracted from git hooks // For now, we'll let the endpoint handle branch detection }), }); if (!response.ok) { throw new Error(`HTTP endpoint returned ${response.status}: ${response.statusText}`); } const result = await response.json(); logger.debug('Git event processed successfully:', result); const ack = { handled: true, }; socket.write(socket_protocol_js_1.MessageBuilder.gitEventAck(ack)); } catch (error) { logger.error('Failed to forward git event to HTTP endpoint:', error); const ack = { handled: false, }; socket.write(socket_protocol_js_1.MessageBuilder.gitEventAck(ack)); } } /** * Send error response */ sendError(socket, message) { socket.write(socket_protocol_js_1.MessageBuilder.error('API_ERROR', message)); } } exports.ApiSocketServer = ApiSocketServer; // Export singleton instance exports.apiSocketServer = new ApiSocketServer(); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpLXNvY2tldC1zZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmVyL2FwaS1zb2NrZXQtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O0dBR0c7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVILHVDQUF5QjtBQUN6Qix5Q0FBMkI7QUFDM0IsdUNBQXlCO0FBQ3pCLDJDQUE2QjtBQUM3QiwrQkFBaUM7QUFDakMsaUVBVWtDO0FBQ2xDLHVEQUFzRDtBQUN0RCx1REFBNkY7QUFDN0YsaURBQWlEO0FBQ2pELCtEQUF3RDtBQUN4RCw2Q0FBbUQ7QUFDbkQseUVBQXFFO0FBQ3JFLGlGQUF5RTtBQUV6RSxNQUFNLE1BQU0sR0FBRyxJQUFBLHdCQUFZLEVBQUMsWUFBWSxDQUFDLENBQUM7QUFDMUMsTUFBTSxRQUFRLEdBQUcsSUFBQSxnQkFBUyxFQUFDLE9BQU8sQ0FBQyxlQUFlLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQztBQUU5RDs7R0FFRztBQUNILEtBQUssVUFBVSxPQUFPLENBQ3BCLElBQWMsRUFDZCxVQUE4QyxFQUFFO0lBRWhELElBQUksQ0FBQztRQUNILE1BQU0sRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxRQUFRLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRTtZQUNyRCxHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUcsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFO1lBQ2pDLE9BQU8sRUFBRSxPQUFPLENBQUMsT0FBTyxJQUFJLElBQUk7WUFDaEMsU0FBUyxFQUFFLElBQUksR0FBRyxJQUFJLEVBQUUsTUFBTTtZQUM5QixHQUFHLEVBQUUsRUFBRSxHQUFHLE9BQU8sQ0FBQyxHQUFHLEVBQUUsbUJBQW1CLEVBQUUsR0FBRyxFQUFFLEVBQUUsc0JBQXNCO1NBQzFFLENBQUMsQ0FBQztRQUNILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxDQUFDLFFB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O1lBRUgsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLEVBQUUsQ0FBQztnQkFDakIsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsUUFBUSxDQUFDLE1BQU0sS0FBSyxRQUFRLENBQUMsVUFBVSxFQUFFLENBQUMsQ0FBQztZQUN2RixDQUFDO1lBRUQsTUFBTSxNQUFNLEdBQUcsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDckMsTUFBTSxDQUFDLEtBQUssQ0FBQyxtQ0FBbUMsRUFBRSxNQUFNLENBQUMsQ0FBQztZQUUxRCxNQUFNLEdBQUcsR0FBZ0I7Z0JBQ3ZCLE9BQU8sRUFBRSxJQUFJO2FBQ2QsQ0FBQztZQUNGLE1BQU0sQ0FBQyxLQUFLLENBQUMsbUNBQWMsQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUNoRCxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sQ0FBQyxLQUFLLENBQUMsK0NBQStDLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFFckUsTUFBTSxHQUFHLEdBQWdCO2dCQUN2QixPQUFPLEVBQUUsS0FBSzthQUNmLENBQUM7WUFDRixNQUFNLENBQUMsS0FBSyxDQUFDLG1DQUFjLENBQUMsV0FBVyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7UUFDaEQsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNLLFNBQVMsQ0FBQyxNQUFrQixFQUFFLE9BQWU7UUFDbkQsTUFBTSxDQUFDLEtBQUssQ0FBQyxtQ0FBYyxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUMzRCxDQUFDO0NBQ0Y7QUExZEQsMENBMGRDO0FBRUQsNEJBQTRCO0FBQ2YsUUFBQSxlQUFlLEdBQUcsSUFBSSxlQUFlLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQVBJIFNvY2tldCBTZXJ2ZXIgZm9yIFZpYmVUdW5uZWwgY29udHJvbCBvcGVyYXRpb25zXG4gKiBQcm92aWRlcyBhIFVuaXggc29ja2V0IGludGVyZmFjZSBmb3IgQ0xJIGNvbW1hbmRzICh2dCkgdG8gY29tbXVuaWNhdGUgd2l0aCB0aGUgc2VydmVyXG4gKi9cblxuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMnO1xuaW1wb3J0ICogYXMgbmV0IGZyb20gJ25ldCc7XG5pbXBvcnQgKiBhcyBvcyBmcm9tICdvcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgcHJvbWlzaWZ5IH0gZnJvbSAndXRpbCc7XG5pbXBvcnQge1xuICB0eXBlIEdpdEV2ZW50QWNrLFxuICB0eXBlIEdpdEV2ZW50Tm90aWZ5LFxuICB0eXBlIEdpdEZvbGxvd1JlcXVlc3QsXG4gIHR5cGUgR2l0Rm9sbG93UmVzcG9uc2UsXG4gIE1lc3NhZ2VCdWlsZGVyLFxuICBNZXNzYWdlUGFyc2VyLFxuICBNZXNzYWdlVHlwZSxcbiAgcGFyc2VQYXlsb2FkLFxuICB0eXBlIFN0YXR1c1Jlc3BvbnNlLFxufSBmcm9tICcuL3B0eS9zb2NrZXQtcHJvdG9jb2wuanMnO1xuaW1wb3J0IHsgY3JlYXRlR2l0RXJyb3IgfSBmcm9tICcuL3V0aWxzL2dpdC1lcnJvci5qcyc7XG5pbXBvcnQgeyBhcmVIb29rc0luc3RhbGxlZCwgaW5zdGFsbEdpdEhvb2tzLCB1bmluc3RhbGxHaXRIb29rcyB9IGZyb20gJy4vdXRpbHMvZ2l0LWhvb2tzLmpzJztcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJy4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB7IHByZXR0aWZ5UGF0aCB9IGZyb20gJy4vdXRpbHMvcGF0aC1wcmV0dGlmeS5qcyc7XG5pbXBvcnQgeyBCVUlMRF9EQVRFLCBWRVJTSU9OIH0gZnJvbSAnLi92ZXJzaW9uLmpzJztcbmltcG9ydCB7IGNyZWF0ZUNvbnRyb2xFdmVudCB9IGZyb20gJy4vd2Vic29ja2V0L2NvbnRyb2wtcHJvdG9jb2wuanMnO1xuaW1wb3J0IHsgY29udHJvbFVuaXhIYW5kbGVyIH0gZnJvbSAnLi93ZWJzb2NrZXQvY29udHJvbC11bml4LWhhbmRsZXIuanMnO1xuXG5jb25zdCBsb2dnZXIgPSBjcmVhdGVMb2dnZXIoJ2FwaS1zb2NrZXQnKTtcbmNvbnN0IGV4ZWNGaWxlID0gcHJvbWlzaWZ5KHJlcXVpcmUoJ2NoaWxkX3Byb2Nlc3MnKS5leGVjRmlsZSk7XG5cbi8qKlxuICogRXhlY3V0ZSBhIGdpdCBjb21tYW5kIHdpdGggcHJvcGVyIGVycm9yIGhhbmRsaW5nXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGV4ZWNHaXQoXG4gIGFyZ3M6IHN0cmluZ1tdLFxuICBvcHRpb25zOiB7IGN3ZD86IHN0cmluZzsgdGltZW91dD86IG51bWJlciB9ID0ge31cbik6IFByb21pc2U8eyBzdGRvdXQ6IHN0cmluZzsgc3RkZXJyOiBzdHJpbmcgfT4ge1xuICB0cnkge1xuICAgIGNvbnN0IHsgc3Rkb3V0LCBzdGRlcnIgfSA9IGF3YWl0IGV4ZWNGaWxlKCdnaXQnLCBhcmdzLCB7XG4gICAgICBjd2Q6IG9wdGlvbnMuY3dkIHx8IHByb2Nlc3MuY3dkKCksXG4gICAgICB0aW1lb3V0OiBvcHRpb25zLnRpbWVvdXQgfHwgNTAwMCxcbiAgICAgIG1heEJ1ZmZlcjogMTAyNCAqIDEwMjQsIC8vIDFNQlxuICAgICAgZW52OiB7IC4uLnByb2Nlc3MuZW52LCBHSVRfVEVSTUlOQUxfUFJPTVBUOiAnMCcgfSwgLy8gRGlzYWJsZSBnaXQgcHJvbXB0c1xuICAgIH0pO1xuICAgIHJldHVybiB7IHN0ZG91dDogc3Rkb3V0LnRvU3RyaW5nKCksIHN0ZGVycjogc3RkZXJyLnRvU3RyaW5nKCkgfTtcbiAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICB0aHJvdyBjcmVhdGVHaXRFcnJvcihlcnJvciwgJ0dpdCBjb21tYW5kIGZhaWxlZCcpO1xuICB9XG59XG5cbi8qKlxuICogQVBJIFNvY2tldCBTZXJ2ZXIgdGhhdCBoYW5kbGVzIENMSSBjb21tYW5kcyB2aWEgVW5peCBzb2NrZXRcbiAqL1xuZXhwb3J0IGNsYXNzIEFwaVNvY2tldFNlcnZlciB7XG4gIHByaXZhdGUgc2VydmVyOiBuZXQuU2VydmVyIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgcmVhZG9ubHkgc29ja2V0UGF0aDogc3RyaW5nO1xuICBwcml2YXRlIHNlcnZlclBvcnQ/OiBudW1iZXI7XG4gIHByaXZhdGUgc2VydmVyVXJsPzogc3RyaW5nO1xuXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIC8vIFVzZSBjb250cm9sIGRpcmVjdG9yeSBmcm9tIGVudmlyb25tZW50IG9yIGRlZmF1bHRcbiAgICBjb25zdCBjb250cm9sRGlyID0gcHJvY2Vzcy5lbnYuVklCRVRVTk5FTF9DT05UUk9MX0RJUiB8fCBwYXRoLmpvaW4ob3MuaG9tZWRpcigpLCAnLnZpYmV0dW5uZWwnKTtcbiAgICBjb25zdCBzb2NrZXREaXIgPSBjb250cm9sRGlyO1xuXG4gICAgLy8gRW5zdXJlIGRpcmVjdG9yeSBleGlzdHNcbiAgICBpZiAoIWZzLmV4aXN0c1N5bmMoc29ja2V0RGlyKSkge1xuICAgICAgZnMubWtkaXJTeW5jKHNvY2tldERpciwgeyByZWN1cnNpdmU6IHRydWUgfSk7XG4gICAgfVxuXG4gICAgLy8gVXNlIGEgZGlmZmVyZW50IHNvY2tldCBuYW1lIHRvIGF2b2lkIGNvbmZsaWN0c1xuICAgIHRoaXMuc29ja2V0UGF0aCA9IHBhdGguam9pbihzb2NrZXREaXIsICdhcGkuc29jaycpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNldCBzZXJ2ZXIgaW5mbyBmb3Igc3RhdHVzIHF1ZXJpZXNcbiAgICovXG4gIHNldFNlcnZlckluZm8ocG9ydDogbnVtYmVyLCB1cmw6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuc2VydmVyUG9ydCA9IHBvcnQ7XG4gICAgdGhpcy5zZXJ2ZXJVcmwgPSB1cmw7XG4gIH1cblxuICAvKipcbiAgICogU3RhcnQgdGhlIEFQSSBzb2NrZXQgc2VydmVyXG4gICAqL1xuICBhc3luYyBzdGFydCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBDbGVhbiB1cCBhbnkgZXhpc3Rpbmcgc29ja2V0XG4gICAgdHJ5IHtcbiAgICAgIGZzLnVubGlua1N5bmModGhpcy5zb2NrZXRQYXRoKTtcbiAgICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICAgIC8vIElnbm9yZVxuICAgIH1cblxuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICB0aGlzLnNlcnZlciA9IG5ldC5jcmVhdGVTZXJ2ZXIoKHNvY2tldCkgPT4ge1xuICAgICAgICB0aGlzLmhhbmRsZUNvbm5lY3Rpb24oc29ja2V0KTtcbiAgICAgIH0pO1xuXG4gICAgICB0aGlzLnNlcnZlci5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdBUEkgc29ja2V0IHNlcnZlciBlcnJvcjonLCBlcnJvcik7XG4gICAgICAgIHJlamVjdChlcnJvcik7XG4gICAgICB9KTtcblxuICAgICAgdGhpcy5zZXJ2ZXIubGlzdGVuKHRoaXMuc29ja2V0UGF0aCwgKCkgPT4ge1xuICAgICAgICBsb2dnZXIubG9nKGBBUEkgc29ja2V0IHNlcnZlciBsaXN0ZW5pbmcgb24gJHt0aGlzLnNvY2tldFBhdGh9YCk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0b3AgdGhlIEFQSSBzb2NrZXQgc2VydmVyXG4gICAqL1xuICBzdG9wKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLnNlcnZlcikge1xuICAgICAgdGhpcy5zZXJ2ZXIuY2xvc2UoKTtcbiAgICAgIHRoaXMuc2VydmVyID0gbnVsbDtcbiAgICB9XG5cbiAgICAvLyBDbGVhbiB1cCBzb2NrZXQgZmlsZVxuICAgIHRyeSB7XG4gICAgICBmcy51bmxpbmtTeW5jKHRoaXMuc29ja2V0UGF0aCk7XG4gICAgfSBjYXRjaCAoX2Vycm9yKSB7XG4gICAgICAvLyBJZ25vcmVcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlIGluY29taW5nIHNvY2tldCBjb25uZWN0aW9uc1xuICAgKi9cbiAgcHJpdmF0ZSBoYW5kbGVDb25uZWN0aW9uKHNvY2tldDogbmV0LlNvY2tldCk6IHZvaWQge1xuICAgIGNvbnN0IHBhcnNlciA9IG5ldyBNZXNzYWdlUGFyc2VyKCk7XG5cbiAgICBzb2NrZXQub24oJ2RhdGEnLCAoZGF0YSkgPT4ge1xuICAgICAgcGFyc2VyLmFkZERhdGEoZGF0YSk7XG5cbiAgICAgIGZvciAoY29uc3QgeyB0eXBlLCBwYXlsb2FkIH0gb2YgcGFyc2VyLnBhcnNlTWVzc2FnZXMoKSkge1xuICAgICAgICB0aGlzLmhhbmRsZU1lc3NhZ2Uoc29ja2V0LCB0eXBlLCBwYXlsb2FkKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHNvY2tldC5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcbiAgICAgIGxvZ2dlci5lcnJvcignQVBJIHNvY2tldCBjb25uZWN0aW9uIGVycm9yOicsIGVycm9yKTtcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBIYW5kbGUgaW5jb21pbmcgbWVzc2FnZXNcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlTWVzc2FnZShcbiAgICBzb2NrZXQ6IG5ldC5Tb2NrZXQsXG4gICAgdHlwZTogTWVzc2FnZVR5cGUsXG4gICAgcGF5bG9hZDogQnVmZmVyXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBkYXRhID0gcGFyc2VQYXlsb2FkKHR5cGUsIHBheWxvYWQpO1xuXG4gICAgICBzd2l0Y2ggKHR5cGUpIHtcbiAgICAgICAgY2FzZSBNZXNzYWdlVHlwZS5TVEFUVVNfUkVRVUVTVDpcbiAgICAgICAgICBhd2FpdCB0aGlzLmhhbmRsZVN0YXR1c1JlcXVlc3Qoc29ja2V0KTtcbiAgICAgICAgICBicmVhaztcblxuICAgICAgICBjYXNlIE1lc3NhZ2VUeXBlLkdJVF9GT0xMT1dfUkVRVUVTVDpcbiAgICAgICAgICBhd2FpdCB0aGlzLmhhbmRsZUdpdEZvbGxvd1JlcXVlc3Qoc29ja2V0LCBkYXRhIGFzIEdpdEZvbGxvd1JlcXVlc3QpO1xuICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgIGNhc2UgTWVzc2FnZVR5cGUuR0lUX0VWRU5UX05PVElGWTpcbiAgICAgICAgICBhd2FpdCB0aGlzLmhhbmRsZUdpdEV2ZW50Tm90aWZ5KHNvY2tldCwgZGF0YSBhcyBHaXRFdmVudE5vdGlmeSk7XG4gICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICBsb2dnZXIud2FybihgVW5oYW5kbGVkIG1lc3NhZ2UgdHlwZTogJHt0eXBlfWApO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBoYW5kbGUgbWVzc2FnZTonLCBlcnJvcik7XG4gICAgICB0aGlzLnNlbmRFcnJvcihzb2NrZXQsIGVycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogJ1Vua25vd24gZXJyb3InKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlIHN0YXR1cyByZXF1ZXN0XG4gICAqL1xuICBwcml2YXRlIGFzeW5jIGhhbmRsZVN0YXR1c1JlcXVlc3Qoc29ja2V0OiBuZXQuU29ja2V0KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIC8vIEdldCBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IGZvciBmb2xsb3cgbW9kZSBjaGVja1xuICAgICAgY29uc3QgY3dkID0gcHJvY2Vzcy5jd2QoKTtcblxuICAgICAgLy8gQ2hlY2sgZm9sbG93IG1vZGUgc3RhdHVzXG4gICAgICBsZXQgZm9sbG93TW9kZTogU3RhdHVzUmVzcG9uc2VbJ2ZvbGxvd01vZGUnXTtcbiAgICAgIHRyeSB7XG4gICAgICAgIC8vIENoZWNrIGlmIHdlJ3JlIGluIGEgZ2l0IHJlcG9cbiAgICAgICAgY29uc3QgeyBzdGRvdXQ6IHJlcG9QYXRoT3V0cHV0IH0gPSBhd2FpdCBleGVjR2l0KFsncmV2LXBhcnNlJywgJy0tc2hvdy10b3BsZXZlbCddLCB7IGN3ZCB9KTtcbiAgICAgICAgY29uc3QgcmVwb1BhdGggPSByZXBvUGF0aE91dHB1dC50cmltKCk7XG5cbiAgICAgICAgLy8gQ2hlY2sgaWYgdGhpcyBpcyBhIHdvcmt0cmVlXG4gICAgICAgIGNvbnN0IHsgc3Rkb3V0OiBnaXREaXJPdXRwdXQgfSA9IGF3YWl0IGV4ZWNHaXQoWydyZXYtcGFyc2UnLCAnLS1naXQtZGlyJ10sIHsgY3dkIH0pO1xuICAgICAgICBjb25zdCBnaXREaXIgPSBnaXREaXJPdXRwdXQudHJpbSgpO1xuICAgICAgICBjb25zdCBpc1dvcmt0cmVlID0gZ2l0RGlyLmluY2x1ZGVzKCcvLmdpdC93b3JrdHJlZXMvJyk7XG5cbiAgICAgICAgLy8gRmluZCBtYWluIHJlcG8gcGF0aFxuICAgICAgICBsZXQgbWFpblJlcG9QYXRoID0gcmVwb1BhdGg7XG4gICAgICAgIGlmIChpc1dvcmt0cmVlKSB7XG4gICAgICAgICAgbWFpblJlcG9QYXRoID0gZ2l0RGlyLnJlcGxhY2UoL1xcL1xcLmdpdFxcL3dvcmt0cmVlc1xcLy4qJC8sICcnKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIENoZWNrIGZvciBuZXcgd29ya3RyZWUtYmFzZWQgZm9sbG93IG1vZGVcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjb25zdCB7IHN0ZG91dCB9ID0gYXdhaXQgZXhlY0dpdChbJ2NvbmZpZycsICd2aWJldHVubmVsLmZvbGxvd1dvcmt0cmVlJ10sIHtcbiAgICAgICAgICAgIGN3ZDogbWFpblJlcG9QYXRoLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGNvbnN0IGZvbGxvd1dvcmt0cmVlID0gc3Rkb3V0LnRyaW0oKTtcbiAgICAgICAgICBpZiAoZm9sbG93V29ya3RyZWUpIHtcbiAgICAgICAgICAgIC8vIEdldCBicmFuY2ggbmFtZSBmcm9tIHdvcmt0cmVlIGZvciBkaXNwbGF5XG4gICAgICAgICAgICBsZXQgYnJhbmNoTmFtZSA9IHBhdGguYmFzZW5hbWUoZm9sbG93V29ya3RyZWUpO1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgY29uc3QgeyBzdGRvdXQ6IGJyYW5jaE91dHB1dCB9ID0gYXdhaXQgZXhlY0dpdChbJ2JyYW5jaCcsICctLXNob3ctY3VycmVudCddLCB7XG4gICAgICAgICAgICAgICAgY3dkOiBmb2xsb3dXb3JrdHJlZSxcbiAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgIGlmIChicmFuY2hPdXRwdXQudHJpbSgpKSB7XG4gICAgICAgICAgICAgICAgYnJhbmNoTmFtZSA9IGJyYW5jaE91dHB1dC50cmltKCk7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gY2F0Y2ggKF9lKSB7XG4gICAgICAgICAgICAgIC8vIFVzZSBkaXJlY3RvcnkgbmFtZSBhcyBmYWxsYmFja1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBmb2xsb3dNb2RlID0ge1xuICAgICAgICAgICAgICBlbmFibGVkOiB0cnVlLFxuICAgICAgICAgICAgICBicmFuY2g6IGJyYW5jaE5hbWUsXG4gICAgICAgICAgICAgIHJlcG9QYXRoOiBwcmV0dGlmeVBhdGgoZm9sbG93V29ya3RyZWUpLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICB9XG4gICAgICAgIH0gY2F0Y2ggKF9lKSB7XG4gICAgICAgICAgLy8gTm8gZm9sbG93IG1vZGUgY29uZmlndXJlZFxuICAgICAgICB9XG4gICAgICB9IGNhdGNoIChfZXJyb3IpIHtcbiAgICAgICAgLy8gTm90IGluIGEgZ2l0IHJlcG9cbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVzcG9uc2U6IFN0YXR1c1Jlc3BvbnNlID0ge1xuICAgICAgICBydW5uaW5nOiB0cnVlLFxuICAgICAgICBwb3J0OiB0aGlzLnNlcnZlclBvcnQsXG4gICAgICAgIHVybDogdGhpcy5zZXJ2ZXJVcmwsXG4gICAgICAgIHZlcnNpb246IFZFUlNJT04sXG4gICAgICAgIGJ1aWxkRGF0ZTogQlVJTERfREFURSxcbiAgICAgICAgZm9sbG93TW9kZSxcbiAgICAgIH07XG5cbiAgICAgIHNvY2tldC53cml0ZShNZXNzYWdlQnVpbGRlci5zdGF0dXNSZXNwb25zZShyZXNwb25zZSkpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBnZXQgc3RhdHVzOicsIGVycm9yKTtcbiAgICAgIHRoaXMuc2VuZEVycm9yKHNvY2tldCwgJ0ZhaWxlZCB0byBnZXQgc2VydmVyIHN0YXR1cycpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBIYW5kbGUgR2l0IGZvbGxvdyBtb2RlIHJlcXVlc3RcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgaGFuZGxlR2l0Rm9sbG93UmVxdWVzdChcbiAgICBzb2NrZXQ6IG5ldC5Tb2NrZXQsXG4gICAgcmVxdWVzdDogR2l0Rm9sbG93UmVxdWVzdFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgeyByZXBvUGF0aCwgYnJhbmNoLCBlbmFibGUsIHdvcmt0cmVlUGF0aCwgbWFpblJlcG9QYXRoIH0gPSByZXF1ZXN0O1xuXG4gICAgICAvLyBVc2UgbmV3IGZpZWxkcyBpZiBhdmFpbGFibGUsIG90aGVyd2lzZSBmYWxsIGJhY2sgdG8gb2xkIGZpZWxkc1xuICAgICAgY29uc3QgdGFyZ2V0TWFpblJlcG8gPSBt