UNPKG

@dollhousemcp/mcp-server

Version:

DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.

183 lines 20.1 kB
/** * File locking utility for preventing race conditions * * Uses advisory locks with timeout and retry logic */ import * as fs from 'fs/promises'; import { logger } from './logger.js'; export class FileLock { lockPath; acquired = false; lockId; pendingTimeouts = new Set(); disposed = false; constructor(filePath) { this.lockPath = `${filePath}.lock`; this.lockId = `${process.pid}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } /** * Dispose the file lock and clear any pending timers. */ dispose() { this.disposed = true; for (const timeoutId of this.pendingTimeouts) { clearTimeout(timeoutId); } this.pendingTimeouts.clear(); } /** * Acquire a lock on the file */ async acquire(options = {}) { const { timeout = 5000, retryInterval = 100, stale = 30000 // 30 seconds } = options; const startTime = Date.now(); while (Date.now() - startTime < timeout) { try { // Try to create lock file exclusively const handle = await fs.open(this.lockPath, 'wx'); // Write lock info await handle.write(JSON.stringify({ pid: process.pid, id: this.lockId, timestamp: Date.now(), hostname: process.env.HOSTNAME || 'unknown' })); await handle.close(); this.acquired = true; return true; } catch (error) { if (error.code === 'EEXIST') { // Lock file exists, check if it's stale try { const lockData = await fs.readFile(this.lockPath, 'utf-8'); const lock = JSON.parse(lockData); if (Date.now() - lock.timestamp > stale) { // Lock is stale, remove it logger.warn('Removing stale lock', { lock: this.lockPath, age: Date.now() - lock.timestamp }); await this.forceRelease(); // Continue to try acquiring } else { // Lock is held by another process await this.sleep(retryInterval); } } catch (readError) { // Can't read lock file - could be permissions issue or concurrent deletion logger.debug('Failed to read lock file, retrying', { lock: this.lockPath, error: readError instanceof Error ? readError.message : String(readError) }); await this.sleep(retryInterval); } } else { // Some other error logger.error('Failed to acquire lock', { error }); return false; } } } logger.warn('Lock acquisition timeout', { lock: this.lockPath, timeout }); return false; } /** * Release the lock */ async release() { if (!this.acquired) { return; } try { // Verify we still own the lock const lockData = await fs.readFile(this.lockPath, 'utf-8'); const lock = JSON.parse(lockData); if (lock.id === this.lockId) { await fs.unlink(this.lockPath); this.acquired = false; } else { logger.warn('Lock owned by different process', { lock: this.lockPath, ourId: this.lockId, theirId: lock.id }); } } catch (error) { if (error.code !== 'ENOENT') { logger.error('Failed to release lock', { error }); } this.acquired = false; } } /** * Force release (use with caution) */ async forceRelease() { try { await fs.unlink(this.lockPath); this.acquired = false; logger.warn('Lock forcibly released', { lock: this.lockPath }); } catch (error) { if (error.code !== 'ENOENT') { logger.error('Failed to force release lock', { error }); } } } /** * Check if lock is held */ async isLocked() { try { await fs.access(this.lockPath); return true; } catch (error) { // Lock file doesn't exist or not accessible logger.debug('Lock file not accessible', { lock: this.lockPath, code: error.code }); return false; } } /** * Execute a function with lock protection */ async withLock(fn, options = {}) { const acquired = await this.acquire(options); if (!acquired) { logger.error('Failed to acquire lock for operation'); return null; } try { return await fn(); } finally { await this.release(); } } sleep(ms) { if (this.disposed) { return Promise.resolve(); } return new Promise(resolve => { const timeoutId = setTimeout(() => { this.pendingTimeouts.delete(timeoutId); resolve(); }, ms); this.pendingTimeouts.add(timeoutId); }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmlsZUxvY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdXRpbHMvRmlsZUxvY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7R0FJRztBQUVILE9BQU8sS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2xDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFRckMsTUFBTSxPQUFPLFFBQVE7SUFDWCxRQUFRLENBQVM7SUFDakIsUUFBUSxHQUFZLEtBQUssQ0FBQztJQUMxQixNQUFNLENBQVM7SUFDZixlQUFlLEdBQUcsSUFBSSxHQUFHLEVBQWtCLENBQUM7SUFDNUMsUUFBUSxHQUFHLEtBQUssQ0FBQztJQUV6QixZQUFZLFFBQWdCO1FBQzFCLElBQUksQ0FBQyxRQUFRLEdBQUcsR0FBRyxRQUFRLE9BQU8sQ0FBQztRQUNuQyxJQUFJLENBQUMsTUFBTSxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsR0FBRyxFQUFFLElBQUksSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDMUYsQ0FBQztJQUVEOztPQUVHO0lBQ0ksT0FBTztRQUNaLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxDQUFDO1FBQ3JCLEtBQUssTUFBTSxTQUFTLElBQUksSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQzdDLFlBQVksQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUMxQixDQUFDO1FBQ0QsSUFBSSxDQUFDLGVBQWUsQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUMvQixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsT0FBTyxDQUFDLFVBQXVCLEVBQUU7UUFDNUMsTUFBTSxFQUNKLE9BQU8sR0FBRyxJQUFJLEVBQ2QsYUFBYSxHQUFHLEdBQUcsRUFDbkIsS0FBSyxHQUFHLEtB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O1lBQ1QsTUFBTSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7SUFFTyxLQUFLLENBQUMsRUFBVTtRQUN0QixJQUFJLElBQUksQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUNsQixPQUFPLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUMzQixDQUFDO1FBQ0QsT0FBTyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRTtZQUMzQixNQUFNLFNBQVMsR0FBRyxVQUFVLENBQUMsR0FBRyxFQUFFO2dCQUNoQyxJQUFJLENBQUMsZUFBZSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztnQkFDdkMsT0FBTyxFQUFFLENBQUM7WUFDWixDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDUCxJQUFJLENBQUMsZUFBZSxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN0QyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogRmlsZSBsb2NraW5nIHV0aWxpdHkgZm9yIHByZXZlbnRpbmcgcmFjZSBjb25kaXRpb25zXG4gKlxuICogVXNlcyBhZHZpc29yeSBsb2NrcyB3aXRoIHRpbWVvdXQgYW5kIHJldHJ5IGxvZ2ljXG4gKi9cblxuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMvcHJvbWlzZXMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi9sb2dnZXIuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIExvY2tPcHRpb25zIHtcbiAgdGltZW91dD86IG51bWJlcjsgICAgICAgIC8vIE1heCB0aW1lIHRvIHdhaXQgZm9yIGxvY2sgKG1zKVxuICByZXRyeUludGVydmFsPzogbnVtYmVyOyAgLy8gVGltZSBiZXR3ZWVuIHJldHJpZXMgKG1zKVxuICBzdGFsZT86IG51bWJlcjsgICAgICAgICAvLyBDb25zaWRlciBsb2NrIHN0YWxlIGFmdGVyIHRoaXMgdGltZSAobXMpXG59XG5cbmV4cG9ydCBjbGFzcyBGaWxlTG9jayB7XG4gIHByaXZhdGUgbG9ja1BhdGg6IHN0cmluZztcbiAgcHJpdmF0ZSBhY3F1aXJlZDogYm9vbGVhbiA9IGZhbHNlO1xuICBwcml2YXRlIGxvY2tJZDogc3RyaW5nO1xuICBwcml2YXRlIHBlbmRpbmdUaW1lb3V0cyA9IG5ldyBTZXQ8Tm9kZUpTLlRpbWVvdXQ+KCk7XG4gIHByaXZhdGUgZGlzcG9zZWQgPSBmYWxzZTtcblxuICBjb25zdHJ1Y3RvcihmaWxlUGF0aDogc3RyaW5nKSB7XG4gICAgdGhpcy5sb2NrUGF0aCA9IGAke2ZpbGVQYXRofS5sb2NrYDtcbiAgICB0aGlzLmxvY2tJZCA9IGAke3Byb2Nlc3MucGlkfS0ke0RhdGUubm93KCl9LSR7TWF0aC5yYW5kb20oKS50b1N0cmluZygzNikuc3Vic3RyKDIsIDkpfWA7XG4gIH1cblxuICAvKipcbiAgICogRGlzcG9zZSB0aGUgZmlsZSBsb2NrIGFuZCBjbGVhciBhbnkgcGVuZGluZyB0aW1lcnMuXG4gICAqL1xuICBwdWJsaWMgZGlzcG9zZSgpOiB2b2lkIHtcbiAgICB0aGlzLmRpc3Bvc2VkID0gdHJ1ZTtcbiAgICBmb3IgKGNvbnN0IHRpbWVvdXRJZCBvZiB0aGlzLnBlbmRpbmdUaW1lb3V0cykge1xuICAgICAgY2xlYXJUaW1lb3V0KHRpbWVvdXRJZCk7XG4gICAgfVxuICAgIHRoaXMucGVuZGluZ1RpbWVvdXRzLmNsZWFyKCk7XG4gIH1cblxuICAvKipcbiAgICogQWNxdWlyZSBhIGxvY2sgb24gdGhlIGZpbGVcbiAgICovXG4gIHB1YmxpYyBhc3luYyBhY3F1aXJlKG9wdGlvbnM6IExvY2tPcHRpb25zID0ge30pOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCB7XG4gICAgICB0aW1lb3V0ID0gNTAwMCxcbiAgICAgIHJldHJ5SW50ZXJ2YWwgPSAxMDAsXG4gICAgICBzdGFsZSA9IDMwMDAwICAvLyAzMCBzZWNvbmRzXG4gICAgfSA9IG9wdGlvbnM7XG5cbiAgICBjb25zdCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuXG4gICAgd2hpbGUgKERhdGUubm93KCkgLSBzdGFydFRpbWUgPCB0aW1lb3V0KSB7XG4gICAgICB0cnkge1xuICAgICAgICAvLyBUcnkgdG8gY3JlYXRlIGxvY2sgZmlsZSBleGNsdXNpdmVseVxuICAgICAgICBjb25zdCBoYW5kbGUgPSBhd2FpdCBmcy5vcGVuKHRoaXMubG9ja1BhdGgsICd3eCcpO1xuXG4gICAgICAgIC8vIFdyaXRlIGxvY2sgaW5mb1xuICAgICAgICBhd2FpdCBoYW5kbGUud3JpdGUoSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgIHBpZDogcHJvY2Vzcy5waWQsXG4gICAgICAgICAgaWQ6IHRoaXMubG9ja0lkLFxuICAgICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgICAgICBob3N0bmFtZTogcHJvY2Vzcy5lbnYuSE9TVE5BTUUgfHwgJ3Vua25vd24nXG4gICAgICAgIH0pKTtcblxuICAgICAgICBhd2FpdCBoYW5kbGUuY2xvc2UoKTtcbiAgICAgICAgdGhpcy5hY3F1aXJlZCA9IHRydWU7XG5cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSA9PT0gJ0VFWElTVCcpIHtcbiAgICAgICAgICAvLyBMb2NrIGZpbGUgZXhpc3RzLCBjaGVjayBpZiBpdCdzIHN0YWxlXG4gICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGNvbnN0IGxvY2tEYXRhID0gYXdhaXQgZnMucmVhZEZpbGUodGhpcy5sb2NrUGF0aCwgJ3V0Zi04Jyk7XG4gICAgICAgICAgICBjb25zdCBsb2NrID0gSlNPTi5wYXJzZShsb2NrRGF0YSk7XG5cbiAgICAgICAgICAgIGlmIChEYXRlLm5vdygpIC0gbG9jay50aW1lc3RhbXAgPiBzdGFsZSkge1xuICAgICAgICAgICAgICAvLyBMb2NrIGlzIHN0YWxlLCByZW1vdmUgaXRcbiAgICAgICAgICAgICAgbG9nZ2VyLndhcm4oJ1JlbW92aW5nIHN0YWxlIGxvY2snLCB7XG4gICAgICAgICAgICAgICAgbG9jazogdGhpcy5sb2NrUGF0aCxcbiAgICAgICAgICAgICAgICBhZ2U6IERhdGUubm93KCkgLSBsb2NrLnRpbWVzdGFtcFxuICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICBhd2FpdCB0aGlzLmZvcmNlUmVsZWFzZSgpO1xuICAgICAgICAgICAgICAvLyBDb250aW51ZSB0byB0cnkgYWNxdWlyaW5nXG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAvLyBMb2NrIGlzIGhlbGQgYnkgYW5vdGhlciBwcm9jZXNzXG4gICAgICAgICAgICAgIGF3YWl0IHRoaXMuc2xlZXAocmV0cnlJbnRlcnZhbCk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBjYXRjaCAocmVhZEVycm9yKSB7XG4gICAgICAgICAgICAvLyBDYW4ndCByZWFkIGxvY2sgZmlsZSAtIGNvdWxkIGJlIHBlcm1pc3Npb25zIGlzc3VlIG9yIGNvbmN1cnJlbnQgZGVsZXRpb25cbiAgICAgICAgICAgIGxvZ2dlci5kZWJ1ZygnRmFpbGVkIHRvIHJlYWQgbG9jayBmaWxlLCByZXRyeWluZycsIHtcbiAgICAgICAgICAgICAgbG9jazogdGhpcy5sb2NrUGF0aCxcbiAgICAgICAgICAgICAgZXJyb3I6IHJlYWRFcnJvciBpbnN0YW5jZW9mIEVycm9yID8gcmVhZEVycm9yLm1lc3NhZ2UgOiBTdHJpbmcocmVhZEVycm9yKVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLnNsZWVwKHJldHJ5SW50ZXJ2YWwpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBTb21lIG90aGVyIGVycm9yXG4gICAgICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gYWNxdWlyZSBsb2NrJywgeyBlcnJvciB9KTtcbiAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBsb2dnZXIud2FybignTG9jayBhY3F1aXNpdGlvbiB0aW1lb3V0Jywge1xuICAgICAgbG9jazogdGhpcy5sb2NrUGF0aCxcbiAgICAgIHRpbWVvdXRcbiAgICB9KTtcblxuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWxlYXNlIHRoZSBsb2NrXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcmVsZWFzZSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoIXRoaXMuYWNxdWlyZWQpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgLy8gVmVyaWZ5IHdlIHN0aWxsIG93biB0aGUgbG9ja1xuICAgICAgY29uc3QgbG9ja0RhdGEgPSBhd2FpdCBmcy5yZWFkRmlsZSh0aGlzLmxvY2tQYXRoLCAndXRmLTgnKTtcbiAgICAgIGNvbnN0IGxvY2sgPSBKU09OLnBhcnNlKGxvY2tEYXRhKTtcblxuICAgICAgaWYgKGxvY2suaWQgPT09IHRoaXMubG9ja0lkKSB7XG4gICAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLmxvY2tQYXRoKTtcbiAgICAgICAgdGhpcy5hY3F1aXJlZCA9IGZhbHNlO1xuXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBsb2dnZXIud2FybignTG9jayBvd25lZCBieSBkaWZmZXJlbnQgcHJvY2VzcycsIHtcbiAgICAgICAgICBsb2NrOiB0aGlzLmxvY2tQYXRoLFxuICAgICAgICAgIG91cklkOiB0aGlzLmxvY2tJZCxcbiAgICAgICAgICB0aGVpcklkOiBsb2NrLmlkXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSAhPT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gcmVsZWFzZSBsb2NrJywgeyBlcnJvciB9KTtcbiAgICAgIH1cbiAgICAgIHRoaXMuYWNxdWlyZWQgPSBmYWxzZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogRm9yY2UgcmVsZWFzZSAodXNlIHdpdGggY2F1dGlvbilcbiAgICovXG4gIHB1YmxpYyBhc3luYyBmb3JjZVJlbGVhc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLnVubGluayh0aGlzLmxvY2tQYXRoKTtcbiAgICAgIHRoaXMuYWNxdWlyZWQgPSBmYWxzZTtcbiAgICAgIGxvZ2dlci53YXJuKCdMb2NrIGZvcmNpYmx5IHJlbGVhc2VkJywgeyBsb2NrOiB0aGlzLmxvY2tQYXRoIH0pO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoKGVycm9yIGFzIGFueSkuY29kZSAhPT0gJ0VOT0VOVCcpIHtcbiAgICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gZm9yY2UgcmVsZWFzZSBsb2NrJywgeyBlcnJvciB9KTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQ2hlY2sgaWYgbG9jayBpcyBoZWxkXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgaXNMb2NrZWQoKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLmFjY2Vzcyh0aGlzLmxvY2tQYXRoKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAvLyBMb2NrIGZpbGUgZG9lc24ndCBleGlzdCBvciBub3QgYWNjZXNzaWJsZVxuICAgICAgbG9nZ2VyLmRlYnVnKCdMb2NrIGZpbGUgbm90IGFjY2Vzc2libGUnLCB7XG4gICAgICAgIGxvY2s6IHRoaXMubG9ja1BhdGgsXG4gICAgICAgIGNvZGU6IChlcnJvciBhcyBOb2RlSlMuRXJybm9FeGNlcHRpb24pLmNvZGVcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBFeGVjdXRlIGEgZnVuY3Rpb24gd2l0aCBsb2NrIHByb3RlY3Rpb25cbiAgICovXG4gIHB1YmxpYyBhc3luYyB3aXRoTG9jazxUPihcbiAgICBmbjogKCkgPT4gUHJvbWlzZTxUPixcbiAgICBvcHRpb25zOiBMb2NrT3B0aW9ucyA9IHt9XG4gICk6IFByb21pc2U8VCB8IG51bGw+IHtcbiAgICBjb25zdCBhY3F1aXJlZCA9IGF3YWl0IHRoaXMuYWNxdWlyZShvcHRpb25zKTtcblxuICAgIGlmICghYWNxdWlyZWQpIHtcbiAgICAgIGxvZ2dlci5lcnJvcignRmFpbGVkIHRvIGFjcXVpcmUgbG9jayBmb3Igb3BlcmF0aW9uJyk7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgcmV0dXJuIGF3YWl0IGZuKCk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGF3YWl0IHRoaXMucmVsZWFzZSgpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgc2xlZXAobXM6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICh0aGlzLmRpc3Bvc2VkKSB7XG4gICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHtcbiAgICAgIGNvbnN0IHRpbWVvdXRJZCA9IHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICB0aGlzLnBlbmRpbmdUaW1lb3V0cy5kZWxldGUodGltZW91dElkKTtcbiAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgfSwgbXMpO1xuICAgICAgdGhpcy5wZW5kaW5nVGltZW91dHMuYWRkKHRpbWVvdXRJZCk7XG4gICAgfSk7XG4gIH1cbn0iXX0=