UNPKG

vibetunnel

Version:

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

346 lines 38.5 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.ConfigService = void 0; const chokidar_1 = require("chokidar"); const fs = __importStar(require("fs")); const os = __importStar(require("os")); const path = __importStar(require("path")); const zod_1 = require("zod"); const config_js_1 = require("../../types/config.js"); const logger_js_1 = require("../utils/logger.js"); const logger = (0, logger_js_1.createLogger)('config-service'); // Zod schema for config validation const ConfigSchema = zod_1.z.object({ version: zod_1.z.number(), quickStartCommands: zod_1.z.array(zod_1.z.object({ name: zod_1.z.string().optional(), command: zod_1.z.string().min(1, 'Command cannot be empty'), })), repositoryBasePath: zod_1.z.string().optional(), // Extended configuration sections - we parse but don't use most of these yet server: zod_1.z .object({ port: zod_1.z.number(), dashboardAccessMode: zod_1.z.string(), cleanupOnStartup: zod_1.z.boolean(), authenticationMode: zod_1.z.string(), }) .optional(), development: zod_1.z .object({ debugMode: zod_1.z.boolean(), useDevServer: zod_1.z.boolean(), devServerPath: zod_1.z.string(), logLevel: zod_1.z.string(), }) .optional(), preferences: zod_1.z .object({ preferredGitApp: zod_1.z.string().optional(), preferredTerminal: zod_1.z.string().optional(), updateChannel: zod_1.z.string(), showInDock: zod_1.z.boolean(), preventSleepWhenRunning: zod_1.z.boolean(), notifications: zod_1.z .object({ enabled: zod_1.z.boolean(), sessionStart: zod_1.z.boolean(), sessionExit: zod_1.z.boolean(), commandCompletion: zod_1.z.boolean(), commandError: zod_1.z.boolean(), bell: zod_1.z.boolean(), claudeTurn: zod_1.z.boolean(), soundEnabled: zod_1.z.boolean(), vibrationEnabled: zod_1.z.boolean(), }) .optional(), }) .optional(), remoteAccess: zod_1.z .object({ ngrokEnabled: zod_1.z.boolean(), ngrokTokenPresent: zod_1.z.boolean(), }) .optional(), sessionDefaults: zod_1.z .object({ command: zod_1.z.string(), workingDirectory: zod_1.z.string(), spawnWindow: zod_1.z.boolean(), titleMode: zod_1.z.string(), }) .optional(), }); /** * Service for managing VibeTunnel configuration with file persistence and live reloading. * * The ConfigService handles loading, saving, and watching the VibeTunnel configuration file * stored in the user's home directory at `~/.vibetunnel/config.json`. It provides validation * using Zod schemas, automatic file watching for live reloading, and event-based notifications * when configuration changes occur. * * Key features: * - Persistent storage in user's home directory * - Automatic validation with Zod schemas * - Live reloading with file watching * - Event-based change notifications * - Graceful fallback to defaults on errors * - Atomic updates with validation * * @example * ```typescript * // Create and start the config service * const configService = new ConfigService(); * configService.startWatching(); * * // Subscribe to configuration changes * const unsubscribe = configService.onConfigChange((newConfig) => { * console.log('Config updated:', newConfig); * }); * * // Update quick start commands * configService.updateQuickStartCommands([ * { name: '🚀 dev', command: 'npm run dev' }, * { command: 'bash' } * ]); * * // Get current configuration * const config = configService.getConfig(); * * // Clean up when done * unsubscribe(); * configService.stopWatching(); * ``` */ class ConfigService { constructor() { this.config = config_js_1.DEFAULT_CONFIG; this.configChangeCallbacks = new Set(); this.configDir = path.join(os.homedir(), '.vibetunnel'); this.configPath = path.join(this.configDir, 'config.json'); this.loadConfig(); } ensureConfigDir() { try { if (!fs.existsSync(this.configDir)) { fs.mkdirSync(this.configDir, { recursive: true }); logger.info(`Created config directory: ${this.configDir}`); } } catch (error) { logger.error('Failed to create config directory:', error); } } validateConfig(data) { try { return ConfigSchema.parse(data); } catch (error) { if (error instanceof zod_1.z.ZodError) { logger.error('Config validation failed:', error.issues); throw new Error(`Invalid config: ${error.issues.map((e) => e.message).join(', ')}`); } throw error; } } loadConfig() { try { this.ensureConfigDir(); if (fs.existsSync(this.configPath)) { const data = fs.readFileSync(this.configPath, 'utf8'); const parsedData = JSON.parse(data); try { // Validate config using Zod schema this.config = this.validateConfig(parsedData); logger.info('Loaded and validated configuration from disk'); } catch (validationError) { logger.warn('Config validation failed, using defaults:', validationError); this.config = config_js_1.DEFAULT_CONFIG; this.saveConfig(); // Save defaults to fix invalid config } } else { logger.info('No config file found, creating with defaults'); this.saveConfig(); // Create config with defaults } } catch (error) { logger.error('Failed to load config:', error); // Keep using defaults } } saveConfig() { try { this.ensureConfigDir(); fs.writeFileSync(this.configPath, JSON.stringify(this.config, null, 2), 'utf8'); logger.info('Saved configuration to disk'); } catch (error) { logger.error('Failed to save config:', error); throw new Error(`Failed to save configuration: ${error instanceof Error ? error.message : String(error)}`); } } startWatching() { if (this.watcher) { return; // Already watching } try { this.watcher = (0, chokidar_1.watch)(this.configPath, { persistent: true, ignoreInitial: true, awaitWriteFinish: { stabilityThreshold: 500, pollInterval: 100, }, }); this.watcher.on('change', () => { logger.info('Configuration file changed, reloading...'); const oldConfig = JSON.stringify(this.config); this.loadConfig(); // Only notify if config actually changed if (JSON.stringify(this.config) !== oldConfig) { this.notifyConfigChange(); } }); this.watcher.on('error', (error) => { logger.error('Config watcher error:', error); }); logger.info('Started watching configuration file'); } catch (error) { logger.error('Failed to start config watcher:', error); } } stopWatching() { if (this.watcher) { this.watcher.close().catch((error) => { logger.error('Error closing config watcher:', error); }); this.watcher = undefined; logger.info('Stopped watching configuration file'); } } notifyConfigChange() { for (const callback of this.configChangeCallbacks) { try { callback(this.config); } catch (error) { logger.error('Error in config change callback:', error); } } } onConfigChange(callback) { this.configChangeCallbacks.add(callback); // Return unsubscribe function return () => { this.configChangeCallbacks.delete(callback); }; } getConfig() { return this.config; } updateConfig(config) { // Validate the config before updating this.config = this.validateConfig(config); this.saveConfig(); this.notifyConfigChange(); } updateQuickStartCommands(commands) { // Validate the entire config with updated commands const updatedConfig = { ...this.config, quickStartCommands: commands }; this.config = this.validateConfig(updatedConfig); this.saveConfig(); this.notifyConfigChange(); } updateRepositoryBasePath(path) { // Validate the entire config with updated repository base path const updatedConfig = { ...this.config, repositoryBasePath: path }; this.config = this.validateConfig(updatedConfig); this.saveConfig(); this.notifyConfigChange(); } getConfigPath() { return this.configPath; } getNotificationPreferences() { return this.config.preferences?.notifications || config_js_1.DEFAULT_NOTIFICATION_PREFERENCES; } updateNotificationPreferences(notifications) { // Validate the notifications object try { const NotificationPreferencesSchema = zod_1.z .object({ enabled: zod_1.z.boolean(), sessionStart: zod_1.z.boolean(), sessionExit: zod_1.z.boolean(), commandCompletion: zod_1.z.boolean(), commandError: zod_1.z.boolean(), bell: zod_1.z.boolean(), claudeTurn: zod_1.z.boolean(), soundEnabled: zod_1.z.boolean(), vibrationEnabled: zod_1.z.boolean(), }) .partial(); const validatedNotifications = NotificationPreferencesSchema.parse(notifications); // Merge with existing notifications or defaults const currentNotifications = this.config.preferences?.notifications || config_js_1.DEFAULT_NOTIFICATION_PREFERENCES; const mergedNotifications = { ...currentNotifications, ...validatedNotifications }; // Ensure preferences object exists if (!this.config.preferences) { this.config.preferences = { updateChannel: 'stable', showInDock: false, preventSleepWhenRunning: true, }; } // Update notifications with merged values this.config.preferences.notifications = mergedNotifications; this.saveConfig(); this.notifyConfigChange(); } catch (error) { if (error instanceof zod_1.z.ZodError) { logger.error('Invalid notification preferences:', error.issues); throw new Error(`Invalid notification preferences: ${error.issues.map((e) => e.message).join(', ')}`); } throw error; } } } exports.ConfigService = ConfigService; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLXNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc2VydmVyL3NlcnZpY2VzL2NvbmZpZy1zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUNBLHVDQUFpQztBQUNqQyx1Q0FBeUI7QUFDekIsdUNBQXlCO0FBQ3pCLDJDQUE2QjtBQUM3Qiw2QkFBd0I7QUFDeEIscURBSytCO0FBQy9CLGtEQUFrRDtBQUVsRCxNQUFNLE1BQU0sR0FBRyxJQUFBLHdCQUFZLEVBQUMsZ0JBQWdCLENBQUMsQ0FBQztBQUU5QyxtQ0FBbUM7QUFDbkMsTUFBTSxZQUFZLEdBQUcsT0FBQyxDQUFDLE1BQU0sQ0FBQztJQUM1QixPQUFPLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRTtJQUNuQixrQkFBa0IsRUFBRSxPQUFDLENBQUMsS0FBSyxDQUN6QixPQUFDLENBQUMsTUFBTSxDQUFDO1FBQ1AsSUFBSSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7UUFDM0IsT0FBTyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLHlCQUF5QixDQUFDO0tBQ3RELENBQUMsQ0FDSDtJQUNELGtCQUFrQixFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDekMsNkVBQTZFO0lBQzdFLE1BQU0sRUFBRSxPQUFDO1NBQ04sTUFBTSxDQUFDO1FBQ04sSUFBSSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUU7UUFDaEIsbUJBQW1CLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRTtRQUMvQixnQkFBZ0IsRUFBRSxPQUFDLENBQUMsT0FBTyxFQUFFO1FBQzdCLGtCQUFrQixFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUU7S0FDL0IsQ0FBQztTQUNELFFBQVEsRUFBRTtJQUNiLFdBQVcsRUFBRSxP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QUNoQyxNQUFNLENBQUMsS0FBSyxDQUFDLG1DQUFtQyxFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDaEUsTUFBTSxJQUFJLEtBQUssQ0FDYixxQ0FBcUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FDckYsQ0FBQztZQUNKLENBQUM7WUFDRCxNQUFNLEtBQUssQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO0NBQ0Y7QUE5TkQsc0NBOE5DIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBGU1dhdGNoZXIgfSBmcm9tICdjaG9raWRhcic7XG5pbXBvcnQgeyB3YXRjaCB9IGZyb20gJ2Nob2tpZGFyJztcbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgeyB6IH0gZnJvbSAnem9kJztcbmltcG9ydCB7XG4gIERFRkFVTFRfQ09ORklHLFxuICBERUZBVUxUX05PVElGSUNBVElPTl9QUkVGRVJFTkNFUyxcbiAgdHlwZSBOb3RpZmljYXRpb25QcmVmZXJlbmNlcyxcbiAgdHlwZSBWaWJlVHVubmVsQ29uZmlnLFxufSBmcm9tICcuLi8uLi90eXBlcy9jb25maWcuanMnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnLi4vdXRpbHMvbG9nZ2VyLmpzJztcblxuY29uc3QgbG9nZ2VyID0gY3JlYXRlTG9nZ2VyKCdjb25maWctc2VydmljZScpO1xuXG4vLyBab2Qgc2NoZW1hIGZvciBjb25maWcgdmFsaWRhdGlvblxuY29uc3QgQ29uZmlnU2NoZW1hID0gei5vYmplY3Qoe1xuICB2ZXJzaW9uOiB6Lm51bWJlcigpLFxuICBxdWlja1N0YXJ0Q29tbWFuZHM6IHouYXJyYXkoXG4gICAgei5vYmplY3Qoe1xuICAgICAgbmFtZTogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICAgICAgY29tbWFuZDogei5zdHJpbmcoKS5taW4oMSwgJ0NvbW1hbmQgY2Fubm90IGJlIGVtcHR5JyksXG4gICAgfSlcbiAgKSxcbiAgcmVwb3NpdG9yeUJhc2VQYXRoOiB6LnN0cmluZygpLm9wdGlvbmFsKCksXG4gIC8vIEV4dGVuZGVkIGNvbmZpZ3VyYXRpb24gc2VjdGlvbnMgLSB3ZSBwYXJzZSBidXQgZG9uJ3QgdXNlIG1vc3Qgb2YgdGhlc2UgeWV0XG4gIHNlcnZlcjogelxuICAgIC5vYmplY3Qoe1xuICAgICAgcG9ydDogei5udW1iZXIoKSxcbiAgICAgIGRhc2hib2FyZEFjY2Vzc01vZGU6IHouc3RyaW5nKCksXG4gICAgICBjbGVhbnVwT25TdGFydHVwOiB6LmJvb2xlYW4oKSxcbiAgICAgIGF1dGhlbnRpY2F0aW9uTW9kZTogei5zdHJpbmcoKSxcbiAgICB9KVxuICAgIC5vcHRpb25hbCgpLFxuICBkZXZlbG9wbWVudDogelxuICAgIC5vYmplY3Qoe1xuICAgICAgZGVidWdNb2RlOiB6LmJvb2xlYW4oKSxcbiAgICAgIHVzZURldlNlcnZlcjogei5ib29sZWFuKCksXG4gICAgICBkZXZTZXJ2ZXJQYXRoOiB6LnN0cmluZygpLFxuICAgICAgbG9nTGV2ZWw6IHouc3RyaW5nKCksXG4gICAgfSlcbiAgICAub3B0aW9uYWwoKSxcbiAgcHJlZmVyZW5jZXM6IHpcbiAgICAub2JqZWN0KHtcbiAgICAgIHByZWZlcnJlZEdpdEFwcDogei5zdHJpbmcoKS5vcHRpb25hbCgpLFxuICAgICAgcHJlZmVycmVkVGVybWluYWw6IHouc3RyaW5nKCkub3B0aW9uYWwoKSxcbiAgICAgIHVwZGF0ZUNoYW5uZWw6IHouc3RyaW5nKCksXG4gICAgICBzaG93SW5Eb2NrOiB6LmJvb2xlYW4oKSxcbiAgICAgIHByZXZlbnRTbGVlcFdoZW5SdW5uaW5nOiB6LmJvb2xlYW4oKSxcbiAgICAgIG5vdGlmaWNhdGlvbnM6IHpcbiAgICAgICAgLm9iamVjdCh7XG4gICAgICAgICAgZW5hYmxlZDogei5ib29sZWFuKCksXG4gICAgICAgICAgc2Vzc2lvblN0YXJ0OiB6LmJvb2xlYW4oKSxcbiAgICAgICAgICBzZXNzaW9uRXhpdDogei5ib29sZWFuKCksXG4gICAgICAgICAgY29tbWFuZENvbXBsZXRpb246IHouYm9vbGVhbigpLFxuICAgICAgICAgIGNvbW1hbmRFcnJvcjogei5ib29sZWFuKCksXG4gICAgICAgICAgYmVsbDogei5ib29sZWFuKCksXG4gICAgICAgICAgY2xhdWRlVHVybjogei5ib29sZWFuKCksXG4gICAgICAgICAgc291bmRFbmFibGVkOiB6LmJvb2xlYW4oKSxcbiAgICAgICAgICB2aWJyYXRpb25FbmFibGVkOiB6LmJvb2xlYW4oKSxcbiAgICAgICAgfSlcbiAgICAgICAgLm9wdGlvbmFsKCksXG4gICAgfSlcbiAgICAub3B0aW9uYWwoKSxcbiAgcmVtb3RlQWNjZXNzOiB6XG4gICAgLm9iamVjdCh7XG4gICAgICBuZ3Jva0VuYWJsZWQ6IHouYm9vbGVhbigpLFxuICAgICAgbmdyb2tUb2tlblByZXNlbnQ6IHouYm9vbGVhbigpLFxuICAgIH0pXG4gICAgLm9wdGlvbmFsKCksXG4gIHNlc3Npb25EZWZhdWx0czogelxuICAgIC5vYmplY3Qoe1xuICAgICAgY29tbWFuZDogei5zdHJpbmcoKSxcbiAgICAgIHdvcmtpbmdEaXJlY3Rvcnk6IHouc3RyaW5nKCksXG4gICAgICBzcGF3bldpbmRvdzogei5ib29sZWFuKCksXG4gICAgICB0aXRsZU1vZGU6IHouc3RyaW5nKCksXG4gICAgfSlcbiAgICAub3B0aW9uYWwoKSxcbn0pO1xuXG4vKipcbiAqIFNlcnZpY2UgZm9yIG1hbmFnaW5nIFZpYmVUdW5uZWwgY29uZmlndXJhdGlvbiB3aXRoIGZpbGUgcGVyc2lzdGVuY2UgYW5kIGxpdmUgcmVsb2FkaW5nLlxuICpcbiAqIFRoZSBDb25maWdTZXJ2aWNlIGhhbmRsZXMgbG9hZGluZywgc2F2aW5nLCBhbmQgd2F0Y2hpbmcgdGhlIFZpYmVUdW5uZWwgY29uZmlndXJhdGlvbiBmaWxlXG4gKiBzdG9yZWQgaW4gdGhlIHVzZXIncyBob21lIGRpcmVjdG9yeSBhdCBgfi8udmliZXR1bm5lbC9jb25maWcuanNvbmAuIEl0IHByb3ZpZGVzIHZhbGlkYXRpb25cbiAqIHVzaW5nIFpvZCBzY2hlbWFzLCBhdXRvbWF0aWMgZmlsZSB3YXRjaGluZyBmb3IgbGl2ZSByZWxvYWRpbmcsIGFuZCBldmVudC1iYXNlZCBub3RpZmljYXRpb25zXG4gKiB3aGVuIGNvbmZpZ3VyYXRpb24gY2hhbmdlcyBvY2N1ci5cbiAqXG4gKiBLZXkgZmVhdHVyZXM6XG4gKiAtIFBlcnNpc3RlbnQgc3RvcmFnZSBpbiB1c2VyJ3MgaG9tZSBkaXJlY3RvcnlcbiAqIC0gQXV0b21hdGljIHZhbGlkYXRpb24gd2l0aCBab2Qgc2NoZW1hc1xuICogLSBMaXZlIHJlbG9hZGluZyB3aXRoIGZpbGUgd2F0Y2hpbmdcbiAqIC0gRXZlbnQtYmFzZWQgY2hhbmdlIG5vdGlmaWNhdGlvbnNcbiAqIC0gR3JhY2VmdWwgZmFsbGJhY2sgdG8gZGVmYXVsdHMgb24gZXJyb3JzXG4gKiAtIEF0b21pYyB1cGRhdGVzIHdpdGggdmFsaWRhdGlvblxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiAvLyBDcmVhdGUgYW5kIHN0YXJ0IHRoZSBjb25maWcgc2VydmljZVxuICogY29uc3QgY29uZmlnU2VydmljZSA9IG5ldyBDb25maWdTZXJ2aWNlKCk7XG4gKiBjb25maWdTZXJ2aWNlLnN0YXJ0V2F0Y2hpbmcoKTtcbiAqXG4gKiAvLyBTdWJzY3JpYmUgdG8gY29uZmlndXJhdGlvbiBjaGFuZ2VzXG4gKiBjb25zdCB1bnN1YnNjcmliZSA9IGNvbmZpZ1NlcnZpY2Uub25Db25maWdDaGFuZ2UoKG5ld0NvbmZpZykgPT4ge1xuICogICBjb25zb2xlLmxvZygnQ29uZmlnIHVwZGF0ZWQ6JywgbmV3Q29uZmlnKTtcbiAqIH0pO1xuICpcbiAqIC8vIFVwZGF0ZSBxdWljayBzdGFydCBjb21tYW5kc1xuICogY29uZmlnU2VydmljZS51cGRhdGVRdWlja1N0YXJ0Q29tbWFuZHMoW1xuICogICB7IG5hbWU6ICfwn5qAIGRldicsIGNvbW1hbmQ6ICducG0gcnVuIGRldicgfSxcbiAqICAgeyBjb21tYW5kOiAnYmFzaCcgfVxuICogXSk7XG4gKlxuICogLy8gR2V0IGN1cnJlbnQgY29uZmlndXJhdGlvblxuICogY29uc3QgY29uZmlnID0gY29uZmlnU2VydmljZS5nZXRDb25maWcoKTtcbiAqXG4gKiAvLyBDbGVhbiB1cCB3aGVuIGRvbmVcbiAqIHVuc3Vic2NyaWJlKCk7XG4gKiBjb25maWdTZXJ2aWNlLnN0b3BXYXRjaGluZygpO1xuICogYGBgXG4gKi9cbmV4cG9ydCBjbGFzcyBDb25maWdTZXJ2aWNlIHtcbiAgcHJpdmF0ZSBjb25maWdEaXI6IHN0cmluZztcbiAgcHJpdmF0ZSBjb25maWdQYXRoOiBzdHJpbmc7XG4gIHByaXZhdGUgY29uZmlnOiBWaWJlVHVubmVsQ29uZmlnID0gREVGQVVMVF9DT05GSUc7XG4gIHByaXZhdGUgd2F0Y2hlcj86IEZTV2F0Y2hlcjtcbiAgcHJpdmF0ZSBjb25maWdDaGFuZ2VDYWxsYmFja3M6IFNldDwoY29uZmlnOiBWaWJlVHVubmVsQ29uZmlnKSA9PiB2b2lkPiA9IG5ldyBTZXQoKTtcblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmNvbmZpZ0RpciA9IHBhdGguam9pbihvcy5ob21lZGlyKCksICcudmliZXR1bm5lbCcpO1xuICAgIHRoaXMuY29uZmlnUGF0aCA9IHBhdGguam9pbih0aGlzLmNvbmZpZ0RpciwgJ2NvbmZpZy5qc29uJyk7XG4gICAgdGhpcy5sb2FkQ29uZmlnKCk7XG4gIH1cblxuICBwcml2YXRlIGVuc3VyZUNvbmZpZ0RpcigpOiB2b2lkIHtcbiAgICB0cnkge1xuICAgICAgaWYgKCFmcy5leGlzdHNTeW5jKHRoaXMuY29uZmlnRGlyKSkge1xuICAgICAgICBmcy5ta2RpclN5bmModGhpcy5jb25maWdEaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuICAgICAgICBsb2dnZXIuaW5mbyhgQ3JlYXRlZCBjb25maWcgZGlyZWN0b3J5OiAke3RoaXMuY29uZmlnRGlyfWApO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBjcmVhdGUgY29uZmlnIGRpcmVjdG9yeTonLCBlcnJvcik7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSB2YWxpZGF0ZUNvbmZpZyhkYXRhOiB1bmtub3duKTogVmliZVR1bm5lbENvbmZpZyB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBDb25maWdTY2hlbWEucGFyc2UoZGF0YSk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIHouWm9kRXJyb3IpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdDb25maWcgdmFsaWRhdGlvbiBmYWlsZWQ6JywgZXJyb3IuaXNzdWVzKTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIGNvbmZpZzogJHtlcnJvci5pc3N1ZXMubWFwKChlKSA9PiBlLm1lc3NhZ2UpLmpvaW4oJywgJyl9YCk7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGxvYWRDb25maWcoKTogdm9pZCB7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMuZW5zdXJlQ29uZmlnRGlyKCk7XG5cbiAgICAgIGlmIChmcy5leGlzdHNTeW5jKHRoaXMuY29uZmlnUGF0aCkpIHtcbiAgICAgICAgY29uc3QgZGF0YSA9IGZzLnJlYWRGaWxlU3luYyh0aGlzLmNvbmZpZ1BhdGgsICd1dGY4Jyk7XG4gICAgICAgIGNvbnN0IHBhcnNlZERhdGEgPSBKU09OLnBhcnNlKGRhdGEpO1xuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgLy8gVmFsaWRhdGUgY29uZmlnIHVzaW5nIFpvZCBzY2hlbWFcbiAgICAgICAgICB0aGlzLmNvbmZpZyA9IHRoaXMudmFsaWRhdGVDb25maWcocGFyc2VkRGF0YSk7XG4gICAgICAgICAgbG9nZ2VyLmluZm8oJ0xvYWRlZCBhbmQgdmFsaWRhdGVkIGNvbmZpZ3VyYXRpb24gZnJvbSBkaXNrJyk7XG4gICAgICAgIH0gY2F0Y2ggKHZhbGlkYXRpb25FcnJvcikge1xuICAgICAgICAgIGxvZ2dlci53YXJuKCdDb25maWcgdmFsaWRhdGlvbiBmYWlsZWQsIHVzaW5nIGRlZmF1bHRzOicsIHZhbGlkYXRpb25FcnJvcik7XG4gICAgICAgICAgdGhpcy5jb25maWcgPSBERUZBVUxUX0NPTkZJRztcbiAgICAgICAgICB0aGlzLnNhdmVDb25maWcoKTsgLy8gU2F2ZSBkZWZhdWx0cyB0byBmaXggaW52YWxpZCBjb25maWdcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbG9nZ2VyLmluZm8oJ05vIGNvbmZpZyBmaWxlIGZvdW5kLCBjcmVhdGluZyB3aXRoIGRlZmF1bHRzJyk7XG4gICAgICAgIHRoaXMuc2F2ZUNvbmZpZygpOyAvLyBDcmVhdGUgY29uZmlnIHdpdGggZGVmYXVsdHNcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gbG9hZCBjb25maWc6JywgZXJyb3IpO1xuICAgICAgLy8gS2VlcCB1c2luZyBkZWZhdWx0c1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc2F2ZUNvbmZpZygpOiB2b2lkIHtcbiAgICB0cnkge1xuICAgICAgdGhpcy5lbnN1cmVDb25maWdEaXIoKTtcbiAgICAgIGZzLndyaXRlRmlsZVN5bmModGhpcy5jb25maWdQYXRoLCBKU09OLnN0cmluZ2lmeSh0aGlzLmNvbmZpZywgbnVsbCwgMiksICd1dGY4Jyk7XG4gICAgICBsb2dnZXIuaW5mbygnU2F2ZWQgY29uZmlndXJhdGlvbiB0byBkaXNrJyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIHNhdmUgY29uZmlnOicsIGVycm9yKTtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYEZhaWxlZCB0byBzYXZlIGNvbmZpZ3VyYXRpb246ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiBTdHJpbmcoZXJyb3IpfWBcbiAgICAgICk7XG4gICAgfVxuICB9XG5cbiAgcHVibGljIHN0YXJ0V2F0Y2hpbmcoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMud2F0Y2hlcikge1xuICAgICAgcmV0dXJuOyAvLyBBbHJlYWR5IHdhdGNoaW5nXG4gICAgfVxuXG4gICAgdHJ5IHtcbiAgICAgIHRoaXMud2F0Y2hlciA9IHdhdGNoKHRoaXMuY29uZmlnUGF0aCwge1xuICAgICAgICBwZXJzaXN0ZW50OiB0cnVlLFxuICAgICAgICBpZ25vcmVJbml0aWFsOiB0cnVlLFxuICAgICAgICBhd2FpdFdyaXRlRmluaXNoOiB7XG4gICAgICAgICAgc3RhYmlsaXR5VGhyZXNob2xkOiA1MDAsXG4gICAgICAgICAgcG9sbEludGVydmFsOiAxMDAsXG4gICAgICAgIH0sXG4gICAgICB9KTtcblxuICAgICAgdGhpcy53YXRjaGVyLm9uKCdjaGFuZ2UnLCAoKSA9PiB7XG4gICAgICAgIGxvZ2dlci5pbmZvKCdDb25maWd1cmF0aW9uIGZpbGUgY2hhbmdlZCwgcmVsb2FkaW5nLi4uJyk7XG4gICAgICAgIGNvbnN0IG9sZENvbmZpZyA9IEpTT04uc3RyaW5naWZ5KHRoaXMuY29uZmlnKTtcbiAgICAgICAgdGhpcy5sb2FkQ29uZmlnKCk7XG5cbiAgICAgICAgLy8gT25seSBub3RpZnkgaWYgY29uZmlnIGFjdHVhbGx5IGNoYW5nZWRcbiAgICAgICAgaWYgKEpTT04uc3RyaW5naWZ5KHRoaXMuY29uZmlnKSAhPT0gb2xkQ29uZmlnKSB7XG4gICAgICAgICAgdGhpcy5ub3RpZnlDb25maWdDaGFuZ2UoKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG5cbiAgICAgIHRoaXMud2F0Y2hlci5vbignZXJyb3InLCAoZXJyb3IpID0+IHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdDb25maWcgd2F0Y2hlciBlcnJvcjonLCBlcnJvcik7XG4gICAgICB9KTtcblxuICAgICAgbG9nZ2VyLmluZm8oJ1N0YXJ0ZWQgd2F0Y2hpbmcgY29uZmlndXJhdGlvbiBmaWxlJyk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIHN0YXJ0IGNvbmZpZyB3YXRjaGVyOicsIGVycm9yKTtcbiAgICB9XG4gIH1cblxuICBwdWJsaWMgc3RvcFdhdGNoaW5nKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLndhdGNoZXIpIHtcbiAgICAgIHRoaXMud2F0Y2hlci5jbG9zZSgpLmNhdGNoKChlcnJvcikgPT4ge1xuICAgICAgICBsb2dnZXIuZXJyb3IoJ0Vycm9yIGNsb3NpbmcgY29uZmlnIHdhdGNoZXI6JywgZXJyb3IpO1xuICAgICAgfSk7XG4gICAgICB0aGlzLndhdGNoZXIgPSB1bmRlZmluZWQ7XG4gICAgICBsb2dnZXIuaW5mbygnU3RvcHBlZCB3YXRjaGluZyBjb25maWd1cmF0aW9uIGZpbGUnKTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIG5vdGlmeUNvbmZpZ0NoYW5nZSgpOiB2b2lkIHtcbiAgICBmb3IgKGNvbnN0IGNhbGxiYWNrIG9mIHRoaXMuY29uZmlnQ2hhbmdlQ2FsbGJhY2tzKSB7XG4gICAgICB0cnkge1xuICAgICAgICBjYWxsYmFjayh0aGlzLmNvbmZpZyk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBsb2dnZXIuZXJyb3IoJ0Vycm9yIGluIGNvbmZpZyBjaGFuZ2UgY2FsbGJhY2s6JywgZXJyb3IpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyBvbkNvbmZpZ0NoYW5nZShjYWxsYmFjazogKGNvbmZpZzogVmliZVR1bm5lbENvbmZpZykgPT4gdm9pZCk6ICgpID0+IHZvaWQge1xuICAgIHRoaXMuY29uZmlnQ2hhbmdlQ2FsbGJhY2tzLmFkZChjYWxsYmFjayk7XG4gICAgLy8gUmV0dXJuIHVuc3Vic2NyaWJlIGZ1bmN0aW9uXG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIHRoaXMuY29uZmlnQ2hhbmdlQ2FsbGJhY2tzLmRlbGV0ZShjYWxsYmFjayk7XG4gICAgfTtcbiAgfVxuXG4gIHB1YmxpYyBnZXRDb25maWcoKTogVmliZVR1bm5lbENvbmZpZyB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnO1xuICB9XG5cbiAgcHVibGljIHVwZGF0ZUNvbmZpZyhjb25maWc6IFZpYmVUdW5uZWxDb25maWcpOiB2b2lkIHtcbiAgICAvLyBWYWxpZGF0ZSB0aGUgY29uZmlnIGJlZm9yZSB1cGRhdGluZ1xuICAgIHRoaXMuY29uZmlnID0gdGhpcy52YWxpZGF0ZUNvbmZpZyhjb25maWcpO1xuICAgIHRoaXMuc2F2ZUNvbmZpZygpO1xuICAgIHRoaXMubm90aWZ5Q29uZmlnQ2hhbmdlKCk7XG4gIH1cblxuICBwdWJsaWMgdXBkYXRlUXVpY2tTdGFydENvbW1hbmRzKGNvbW1hbmRzOiBWaWJlVHVubmVsQ29uZmlnWydxdWlja1N0YXJ0Q29tbWFuZHMnXSk6IHZvaWQge1xuICAgIC8vIFZhbGlkYXRlIHRoZSBlbnRpcmUgY29uZmlnIHdpdGggdXBkYXRlZCBjb21tYW5kc1xuICAgIGNvbnN0IHVwZGF0ZWRDb25maWcgPSB7IC4uLnRoaXMuY29uZmlnLCBxdWlja1N0YXJ0Q29tbWFuZHM6IGNvbW1hbmRzIH07XG4gICAgdGhpcy5jb25maWcgPSB0aGlzLnZhbGlkYXRlQ29uZmlnKHVwZGF0ZWRDb25maWcpO1xuICAgIHRoaXMuc2F2ZUNvbmZpZygpO1xuICAgIHRoaXMubm90aWZ5Q29uZmlnQ2hhbmdlKCk7XG4gIH1cblxuICBwdWJsaWMgdXBkYXRlUmVwb3NpdG9yeUJhc2VQYXRoKHBhdGg6IHN0cmluZyk6IHZvaWQge1xuICAgIC8vIFZhbGlkYXRlIHRoZSBlbnRpcmUgY29uZmlnIHdpdGggdXBkYXRlZCByZXBvc2l0b3J5IGJhc2UgcGF0aFxuICAgIGNvbnN0IHVwZGF0ZWRDb25maWcgPSB7IC4uLnRoaXMuY29uZmlnLCByZXBvc2l0b3J5QmFzZVBhdGg6IHBhdGggfTtcbiAgICB0aGlzLmNvbmZpZyA9IHRoaXMudmFsaWRhdGVDb25maWcodXBkYXRlZENvbmZpZyk7XG4gICAgdGhpcy5zYXZlQ29uZmlnKCk7XG4gICAgdGhpcy5ub3RpZnlDb25maWdDaGFuZ2UoKTtcbiAgfVxuXG4gIHB1YmxpYyBnZXRDb25maWdQYXRoKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnUGF0aDtcbiAgfVxuXG4gIHB1YmxpYyBnZXROb3RpZmljYXRpb25QcmVmZXJlbmNlcygpOiBOb3RpZmljYXRpb25QcmVmZXJlbmNlcyB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLnByZWZlcmVuY2VzPy5ub3RpZmljYXRpb25zIHx8IERFRkFVTFRfTk9USUZJQ0FUSU9OX1BSRUZFUkVOQ0VTO1xuICB9XG5cbiAgcHVibGljIHVwZGF0ZU5vdGlmaWNhdGlvblByZWZlcmVuY2VzKG5vdGlmaWNhdGlvbnM6IFBhcnRpYWw8Tm90aWZpY2F0aW9uUHJlZmVyZW5jZXM+KTogdm9pZCB7XG4gICAgLy8gVmFsaWRhdGUgdGhlIG5vdGlmaWNhdGlvbnMgb2JqZWN0XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IE5vdGlmaWNhdGlvblByZWZlcmVuY2VzU2NoZW1hID0gelxuICAgICAgICAub2JqZWN0KHtcbiAgICAgICAgICBlbmFibGVkOiB6LmJvb2xlYW4oKSxcbiAgICAgICAgICBzZXNzaW9uU3RhcnQ6IHouYm9vbGVhbigpLFxuICAgICAgICAgIHNlc3Npb25FeGl0OiB6LmJvb2xlYW4oKSxcbiAgICAgICAgICBjb21tYW5kQ29tcGxldGlvbjogei5ib29sZWFuKCksXG4gICAgICAgICAgY29tbWFuZEVycm9yOiB6LmJvb2xlYW4oKSxcbiAgICAgICAgICBiZWxsOiB6LmJvb2xlYW4oKSxcbiAgICAgICAgICBjbGF1ZGVUdXJuOiB6LmJvb2xlYW4oKSxcbiAgICAgICAgICBzb3VuZEVuYWJsZWQ6IHouYm9vbGVhbigpLFxuICAgICAgICAgIHZpYnJhdGlvbkVuYWJsZWQ6IHouYm9vbGVhbigpLFxuICAgICAgICB9KVxuICAgICAgICAucGFydGlhbCgpO1xuXG4gICAgICBjb25zdCB2YWxpZGF0ZWROb3RpZmljYXRpb25zID0gTm90aWZpY2F0aW9uUHJlZmVyZW5jZXNTY2hlbWEucGFyc2Uobm90aWZpY2F0aW9ucyk7XG5cbiAgICAgIC8vIE1lcmdlIHdpdGggZXhpc3Rpbmcgbm90aWZpY2F0aW9ucyBvciBkZWZhdWx0c1xuICAgICAgY29uc3QgY3VycmVudE5vdGlmaWNhdGlvbnMgPVxuICAgICAgICB0aGlzLmNvbmZpZy5wcmVmZXJlbmNlcz8ubm90aWZpY2F0aW9ucyB8fCBERUZBVUxUX05PVElGSUNBVElPTl9QUkVGRVJFTkNFUztcbiAgICAgIGNvbnN0IG1lcmdlZE5vdGlmaWNhdGlvbnMgPSB7IC4uLmN1cnJlbnROb3RpZmljYXRpb25zLCAuLi52YWxpZGF0ZWROb3RpZmljYXRpb25zIH07XG5cbiAgICAgIC8vIEVuc3VyZSBwcmVmZXJlbmNlcyBvYmplY3QgZXhpc3RzXG4gICAgICBpZiAoIXRoaXMuY29uZmlnLnByZWZlcmVuY2VzKSB7XG4gICAgICAgIHRoaXMuY29uZmlnLnByZWZlcmVuY2VzID0ge1xuICAgICAgICAgIHVwZGF0ZUNoYW5uZWw6ICdzdGFibGUnLFxuICAgICAgICAgIHNob3dJbkRvY2s6IGZhbHNlLFxuICAgICAgICAgIHByZXZlbnRTbGVlcFdoZW5SdW5uaW5nOiB0cnVlLFxuICAgICAgICB9O1xuICAgICAgfVxuXG4gICAgICAvLyBVcGRhdGUgbm90aWZpY2F0aW9ucyB3aXRoIG1lcmdlZCB2YWx1ZXNcbiAgICAgIHRoaXMuY29uZmlnLnByZWZlcmVuY2VzLm5vdGlmaWNhdGlvbnMgPSBtZXJnZWROb3RpZmljYXRpb25zO1xuICAgICAgdGhpcy5zYXZlQ29uZmlnKCk7XG4gICAgICB0aGlzLm5vdGlmeUNvbmZpZ0NoYW5nZSgpO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoZXJyb3IgaW5zdGFuY2VvZiB6LlpvZEVycm9yKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignSW52YWxpZCBub3RpZmljYXRpb24gcHJlZmVyZW5jZXM6JywgZXJyb3IuaXNzdWVzKTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBJbnZhbGlkIG5vdGlmaWNhdGlvbiBwcmVmZXJlbmNlczogJHtlcnJvci5pc3N1ZXMubWFwKChlKSA9PiBlLm1lc3NhZ2UpLmpvaW4oJywgJyl9YFxuICAgICAgICApO1xuICAgICAgfVxuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICB9XG59XG4iXX0=