UNPKG

llmverify

Version:

AI Output Verification Toolkit — Local-first LLM safety, hallucination detection, PII redaction, prompt injection defense, and runtime monitoring. Zero telemetry. OWASP LLM Top 10 aligned.

251 lines (248 loc) 27.6 kB
#!/usr/bin/env node "use strict"; /** * llmverify HTTP Server * * Long-running HTTP API server for IDE and external tool integration. * Provides REST endpoints for AI output verification. * * @module server * @author KingCaliber Labs * @license MIT */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.app = void 0; exports.startServer = startServer; const express_1 = __importDefault(require("express")); const verify_1 = require("./verify"); const security_1 = require("./csm6/security"); const classification_1 = require("./engines/classification"); const constants_1 = require("./constants"); const app = (0, express_1.default)(); exports.app = app; // Middleware app.use(express_1.default.json({ limit: '10mb' })); app.use(express_1.default.urlencoded({ extended: true, limit: '10mb' })); // CORS for local development app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.sendStatus(200); return; } next(); }); // Health check endpoint app.get('/health', (req, res) => { res.json({ ok: true, version: constants_1.VERSION, service: 'llmverify', timestamp: new Date().toISOString() }); }); // Main verification endpoint app.post('/verify', async (req, res) => { try { const { text, content, prompt, config } = req.body; // Accept both 'text' and 'content' for flexibility const inputText = text || content; if (!inputText) { res.status(400).json({ error: 'Missing required field: text or content', example: { text: 'Your AI output here' } }); return; } if (typeof inputText !== 'string') { res.status(400).json({ error: 'Field "text" must be a string' }); return; } // Run verification const result = await (0, verify_1.verify)({ content: inputText, config: config || undefined }); res.json({ success: true, result, meta: { version: constants_1.VERSION, timestamp: new Date().toISOString() } }); } catch (error) { console.error('Verification error:', error); res.status(500).json({ success: false, error: error.message || 'Internal server error', version: constants_1.VERSION }); } }); // Input safety check endpoint app.post('/check-input', async (req, res) => { try { const { text, input } = req.body; const inputText = text || input; if (!inputText) { res.status(400).json({ error: 'Missing required field: text or input' }); return; } const safe = (0, security_1.isInputSafe)(inputText); res.json({ success: true, safe, input: inputText, recommendation: safe ? 'allow' : 'block', version: constants_1.VERSION }); } catch (error) { res.status(500).json({ success: false, error: error.message || 'Internal server error' }); } }); // PII detection endpoint app.post('/check-pii', async (req, res) => { try { const { text, content } = req.body; const inputText = text || content; if (!inputText) { res.status(400).json({ error: 'Missing required field: text or content' }); return; } const hasPII = (0, security_1.containsPII)(inputText); const { redacted, piiCount } = (0, security_1.redactPII)(inputText); res.json({ success: true, hasPII, piiCount, redacted, version: constants_1.VERSION }); } catch (error) { res.status(500).json({ success: false, error: error.message || 'Internal server error' }); } }); // Classification endpoint app.post('/classify', async (req, res) => { try { const { prompt, output, config } = req.body; if (!prompt || !output) { res.status(400).json({ error: 'Missing required fields: prompt and output' }); return; } const result = (0, classification_1.classify)(prompt, output, config); res.json({ success: true, result, version: constants_1.VERSION }); } catch (error) { res.status(500).json({ success: false, error: error.message || 'Internal server error' }); } }); // Root endpoint - API info app.get('/', (req, res) => { res.json({ service: 'llmverify', version: constants_1.VERSION, description: 'AI Output Verification API', endpoints: { 'GET /health': 'Health check', 'POST /verify': 'Verify AI output (main endpoint)', 'POST /check-input': 'Check input for prompt injection', 'POST /check-pii': 'Detect and redact PII', 'POST /classify': 'Classify output intent and hallucination risk' }, documentation: 'https://github.com/subodhkc/llmverify-npm#readme', privacy: 'All processing is local. Zero telemetry.' }); }); // 404 handler app.use((req, res) => { res.status(404).json({ error: 'Endpoint not found', available: ['GET /', 'GET /health', 'POST /verify', 'POST /check-input', 'POST /check-pii', 'POST /classify'] }); }); // Error handler app.use((err, req, res, next) => { console.error('Server error:', err); res.status(500).json({ error: 'Internal server error', message: err.message }); }); // Start server function startServer(port = 9009) { const server = app.listen(port, () => { console.log(` ╔══════════════════════════════════════════════════════════════════════════════╗ ║ llmverify server v${constants_1.VERSION} ║ ║ Running on http://localhost:${port} ║ ╚══════════════════════════════════════════════════════════════════════════════╝ Available endpoints: GET http://localhost:${port}/health - Health check POST http://localhost:${port}/verify - Verify AI output POST http://localhost:${port}/check-input - Check input safety POST http://localhost:${port}/check-pii - Detect PII POST http://localhost:${port}/classify - Classify output Privacy: All processing is 100% local. Zero telemetry. Press Ctrl+C to stop the server. `); }); // Graceful shutdown process.on('SIGTERM', () => { console.log('\nShutting down server...'); server.close(() => { console.log('Server stopped.'); process.exit(0); }); }); process.on('SIGINT', () => { console.log('\nShutting down server...'); server.close(() => { console.log('Server stopped.'); process.exit(0); }); }); return server; } // CLI entry point if (require.main === module) { const args = process.argv.slice(2); const portArg = args.find(arg => arg.startsWith('--port=')); const port = portArg ? parseInt(portArg.split('=')[1], 10) : 9009; if (isNaN(port) || port < 1 || port > 65535) { console.error('Invalid port number. Must be between 1 and 65535.'); process.exit(1); } startServer(port); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3NlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBOzs7Ozs7Ozs7R0FTRzs7Ozs7O0FBbU5ILGtDQXVDQztBQXhQRCxzREFBcUQ7QUFDckQscUNBQWtDO0FBQ2xDLDhDQUFzRTtBQUN0RSw2REFBb0Q7QUFDcEQsMkNBQXNDO0FBRXRDLE1BQU0sR0FBRyxHQUFHLElBQUEsaUJBQU8sR0FBRSxDQUFDO0FBcVBiLGtCQUFHO0FBblBaLGFBQWE7QUFDYixHQUFHLENBQUMsR0FBRyxDQUFDLGlCQUFPLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUMsQ0FBQztBQUN6QyxHQUFHLENBQUMsR0FBRyxDQUFDLGlCQUFPLENBQUMsVUFBVSxDQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQyxDQUFDO0FBRS9ELDZCQUE2QjtBQUM3QixHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRTtJQUN6QixHQUFHLENBQUMsTUFBTSxDQUFDLDZCQUE2QixFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQy9DLEdBQUcsQ0FBQyxNQUFNLENBQUMsOEJBQThCLEVBQUUsb0JBQW9CLENBQUMsQ0FBQztJQUNqRSxHQUFHLENBQUMsTUFBTSxDQUFDLDhCQUE4QixFQUFFLGNBQWMsQ0FBQyxDQUFDO0lBQzNELElBQUksR0FBRyxDQUFDLE1BQU0sS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUM3QixHQUFHLENBQUMsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ3BCLE9BQU87SUFDVCxDQUFDO0lBQ0QsSUFBSSxFQUFFLENBQUM7QUFDVCxDQUFDLENBQUMsQ0FBQztBQUVILHdCQUF3QjtBQUN4QixHQUFHLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxD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LE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO0lBQzVELE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUVsRSxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxJQUFJLEdBQUcsQ0FBQyxJQUFJLElBQUksR0FBRyxLQUFLLEVBQUUsQ0FBQztRQUM1QyxPQUFPLENBQUMsS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUM7UUFDbkUsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsQixDQUFDO0lBRUQsV0FBVyxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ3BCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIjIS91c3IvYmluL2VudiBub2RlXG5cbi8qKlxuICogbGxtdmVyaWZ5IEhUVFAgU2VydmVyXG4gKiBcbiAqIExvbmctcnVubmluZyBIVFRQIEFQSSBzZXJ2ZXIgZm9yIElERSBhbmQgZXh0ZXJuYWwgdG9vbCBpbnRlZ3JhdGlvbi5cbiAqIFByb3ZpZGVzIFJFU1QgZW5kcG9pbnRzIGZvciBBSSBvdXRwdXQgdmVyaWZpY2F0aW9uLlxuICogXG4gKiBAbW9kdWxlIHNlcnZlclxuICogQGF1dGhvciBLaW5nQ2FsaWJlciBMYWJzXG4gKiBAbGljZW5zZSBNSVRcbiAqL1xuXG5pbXBvcnQgZXhwcmVzcywgeyBSZXF1ZXN0LCBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgdmVyaWZ5IH0gZnJvbSAnLi92ZXJpZnknO1xuaW1wb3J0IHsgaXNJbnB1dFNhZmUsIHJlZGFjdFBJSSwgY29udGFpbnNQSUkgfSBmcm9tICcuL2NzbTYvc2VjdXJpdHknO1xuaW1wb3J0IHsgY2xhc3NpZnkgfSBmcm9tICcuL2VuZ2luZXMvY2xhc3NpZmljYXRpb24nO1xuaW1wb3J0IHsgVkVSU0lPTiB9IGZyb20gJy4vY29uc3RhbnRzJztcblxuY29uc3QgYXBwID0gZXhwcmVzcygpO1xuXG4vLyBNaWRkbGV3YXJlXG5hcHAudXNlKGV4cHJlc3MuanNvbih7IGxpbWl0OiAnMTBtYicgfSkpO1xuYXBwLnVzZShleHByZXNzLnVybGVuY29kZWQoeyBleHRlbmRlZDogdHJ1ZSwgbGltaXQ6ICcxMG1iJyB9KSk7XG5cbi8vIENPUlMgZm9yIGxvY2FsIGRldmVsb3BtZW50XG5hcHAudXNlKChyZXEsIHJlcywgbmV4dCkgPT4ge1xuICByZXMuaGVhZGVyKCdBY2Nlc3MtQ29udHJvbC1BbGxvdy1PcmlnaW4nLCAnKicpO1xuICByZXMuaGVhZGVyKCdBY2Nlc3MtQ29udHJvbC1BbGxvdy1NZXRob2RzJywgJ0dFVCwgUE9TVCwgT1BUSU9OUycpO1xuICByZXMuaGVhZGVyKCdBY2Nlc3MtQ29udHJvbC1BbGxvdy1IZWFkZXJzJywgJ0NvbnRlbnQtVHlwZScpO1xuICBpZiAocmVxLm1ldGhvZCA9PT0gJ09QVElPTlMnKSB7XG4gICAgcmVzLnNlbmRTdGF0dXMoMjAwKTtcbiAgICByZXR1cm47XG4gIH1cbiAgbmV4dCgpO1xufSk7XG5cbi8vIEhlYWx0aCBjaGVjayBlbmRwb2ludFxuYXBwLmdldCgnL2hlYWx0aCcsIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpID0+IHtcbiAgcmVzLmpzb24oe1xuICAgIG9rOiB0cnVlLFxuICAgIHZlcnNpb246IFZFUlNJT04sXG4gICAgc2VydmljZTogJ2xsbXZlcmlmeScsXG4gICAgdGltZXN0YW1wOiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKClcbiAgfSk7XG59KTtcblxuLy8gTWFpbiB2ZXJpZmljYXRpb24gZW5kcG9pbnRcbmFwcC5wb3N0KCcvdmVyaWZ5JywgYXN5bmMgKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSk6IFByb21pc2U8dm9pZD4gPT4ge1xuICB0cnkge1xuICAgIGNvbnN0IHsgdGV4dCwgY29udGVudCwgcHJvbXB0LCBjb25maWcgfSA9IHJlcS5ib2R5O1xuICAgIFxuICAgIC8vIEFjY2VwdCBib3RoICd0ZXh0JyBhbmQgJ2NvbnRlbnQnIGZvciBmbGV4aWJpbGl0eVxuICAgIGNvbnN0IGlucHV0VGV4dCA9IHRleHQgfHwgY29udGVudDtcbiAgICBcbiAgICBpZiAoIWlucHV0VGV4dCkge1xuICAgICAgcmVzLnN0YXR1cyg0MDApLmpzb24oe1xuICAgICAgICBlcnJvcjogJ01pc3NpbmcgcmVxdWlyZWQgZmllbGQ6IHRleHQgb3IgY29udGVudCcsXG4gICAgICAgIGV4YW1wbGU6IHsgdGV4dDogJ1lvdXIgQUkgb3V0cHV0IGhlcmUnIH1cbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICh0eXBlb2YgaW5wdXRUZXh0ICE9PSAnc3RyaW5nJykge1xuICAgICAgcmVzLnN0YXR1cyg0MDApLmpzb24oe1xuICAgICAgICBlcnJvcjogJ0ZpZWxkIFwidGV4dFwiIG11c3QgYmUgYSBzdHJpbmcnXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBSdW4gdmVyaWZpY2F0aW9uXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdmVyaWZ5KHsgXG4gICAgICBjb250ZW50OiBpbnB1dFRleHQsXG4gICAgICBjb25maWc6IGNvbmZpZyB8fCB1bmRlZmluZWRcbiAgICB9KTtcblxuICAgIHJlcy5qc29uKHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICByZXN1bHQsXG4gICAgICBtZXRhOiB7XG4gICAgICAgIHZlcnNpb246IFZFUlNJT04sXG4gICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoKS50b0lTT1N0cmluZygpXG4gICAgICB9XG4gICAgfSk7XG5cbiAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgIGNvbnNvbGUuZXJyb3IoJ1ZlcmlmaWNhdGlvbiBlcnJvcjonLCBlcnJvcik7XG4gICAgcmVzLnN0YXR1cyg1MDApLmpzb24oe1xuICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnSW50ZXJuYWwgc2VydmVyIGVycm9yJyxcbiAgICAgIHZlcnNpb246IFZFUlNJT05cbiAgICB9KTtcbiAgfVxufSk7XG5cbi8vIElucHV0IHNhZmV0eSBjaGVjayBlbmRwb2ludFxuYXBwLnBvc3QoJy9jaGVjay1pbnB1dCcsIGFzeW5jIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCB7IHRleHQsIGlucHV0IH0gPSByZXEuYm9keTtcbiAgICBjb25zdCBpbnB1dFRleHQgPSB0ZXh0IHx8IGlucHV0O1xuICAgIFxuICAgIGlmICghaW5wdXRUZXh0KSB7XG4gICAgICByZXMuc3RhdHVzKDQwMCkuanNvbih7XG4gICAgICAgIGVycm9yOiAnTWlzc2luZyByZXF1aXJlZCBmaWVsZDogdGV4dCBvciBpbnB1dCdcbiAgICAgIH0pO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHNhZmUgPSBpc0lucHV0U2FmZShpbnB1dFRleHQpO1xuXG4gICAgcmVzLmpzb24oe1xuICAgICAgc3VjY2VzczogdHJ1ZSxcbiAgICAgIHNhZmUsXG4gICAgICBpbnB1dDogaW5wdXRUZXh0LFxuICAgICAgcmVjb21tZW5kYXRpb246IHNhZmUgPyAnYWxsb3cnIDogJ2Jsb2NrJyxcbiAgICAgIHZlcnNpb246IFZFUlNJT05cbiAgICB9KTtcblxuICB9IGNhdGNoIChlcnJvcjogYW55KSB7XG4gICAgcmVzLnN0YXR1cyg1MDApLmpzb24oe1xuICAgICAgc3VjY2VzczogZmFsc2UsXG4gICAgICBlcnJvcjogZXJyb3IubWVzc2FnZSB8fCAnSW50ZXJuYWwgc2VydmVyIGVycm9yJ1xuICAgIH0pO1xuICB9XG59KTtcblxuLy8gUElJIGRldGVjdGlvbiBlbmRwb2ludFxuYXBwLnBvc3QoJy9jaGVjay1waWknLCBhc3luYyAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gIHRyeSB7XG4gICAgY29uc3QgeyB0ZXh0LCBjb250ZW50IH0gPSByZXEuYm9keTtcbiAgICBjb25zdCBpbnB1dFRleHQgPSB0ZXh0IHx8IGNvbnRlbnQ7XG4gICAgXG4gICAgaWYgKCFpbnB1dFRleHQpIHtcbiAgICAgIHJlcy5zdGF0dXMoNDAwKS5qc29uKHtcbiAgICAgICAgZXJyb3I6ICdNaXNzaW5nIHJlcXVpcmVkIGZpZWxkOiB0ZXh0IG9yIGNvbnRlbnQnXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBoYXNQSUkgPSBjb250YWluc1BJSShpbnB1dFRleHQpO1xuICAgIGNvbnN0IHsgcmVkYWN0ZWQsIHBpaUNvdW50IH0gPSByZWRhY3RQSUkoaW5wdXRUZXh0KTtcblxuICAgIHJlcy5qc29uKHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICBoYXNQSUksXG4gICAgICBwaWlDb3VudCxcbiAgICAgIHJlZGFjdGVkLFxuICAgICAgdmVyc2lvbjogVkVSU0lPTlxuICAgIH0pO1xuXG4gIH0gY2F0Y2ggKGVycm9yOiBhbnkpIHtcbiAgICByZXMuc3RhdHVzKDUwMCkuanNvbih7XG4gICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgIGVycm9yOiBlcnJvci5tZXNzYWdlIHx8ICdJbnRlcm5hbCBzZXJ2ZXIgZXJyb3InXG4gICAgfSk7XG4gIH1cbn0pO1xuXG4vLyBDbGFzc2lmaWNhdGlvbiBlbmRwb2ludFxuYXBwLnBvc3QoJy9jbGFzc2lmeScsIGFzeW5jIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgdHJ5IHtcbiAgICBjb25zdCB7IHByb21wdCwgb3V0cHV0LCBjb25maWcgfSA9IHJlcS5ib2R5O1xuICAgIFxuICAgIGlmICghcHJvbXB0IHx8ICFvdXRwdXQpIHtcbiAgICAgIHJlcy5zdGF0dXMoNDAwKS5qc29uKHtcbiAgICAgICAgZXJyb3I6ICdNaXNzaW5nIHJlcXVpcmVkIGZpZWxkczogcHJvbXB0IGFuZCBvdXRwdXQnXG4gICAgICB9KTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCByZXN1bHQgPSBjbGFzc2lmeShwcm9tcHQsIG91dHB1dCwgY29uZmlnKTtcblxuICAgIHJlcy5qc29uKHtcbiAgICAgIHN1Y2Nlc3M6IHRydWUsXG4gICAgICByZXN1bHQsXG4gICAgICB2ZXJzaW9uOiBWRVJTSU9OXG4gICAgfSk7XG5cbiAgfSBjYXRjaCAoZXJyb3I6IGFueSkge1xuICAgIHJlcy5zdGF0dXMoNTAwKS5qc29uKHtcbiAgICAgIHN1Y2Nlc3M6IGZhbHNlLFxuICAgICAgZXJyb3I6IGVycm9yLm1lc3NhZ2UgfHwgJ0ludGVybmFsIHNlcnZlciBlcnJvcidcbiAgICB9KTtcbiAgfVxufSk7XG5cbi8vIFJvb3QgZW5kcG9pbnQgLSBBUEkgaW5mb1xuYXBwLmdldCgnLycsIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpID0+IHtcbiAgcmVzLmpzb24oe1xuICAgIHNlcnZpY2U6ICdsbG12ZXJpZnknLFxuICAgIHZlcnNpb246IFZFUlNJT04sXG4gICAgZGVzY3JpcHRpb246ICdBSSBPdXRwdXQgVmVyaWZpY2F0aW9uIEFQSScsXG4gICAgZW5kcG9pbnRzOiB7XG4gICAgICAnR0VUIC9oZWFsdGgnOiAnSGVhbHRoIGNoZWNrJyxcbiAgICAgICdQT1NUIC92ZXJpZnknOiAnVmVyaWZ5IEFJIG91dHB1dCAobWFpbiBlbmRwb2ludCknLFxuICAgICAgJ1BPU1QgL2NoZWNrLWlucHV0JzogJ0NoZWNrIGlucHV0IGZvciBwcm9tcHQgaW5qZWN0aW9uJyxcbiAgICAgICdQT1NUIC9jaGVjay1waWknOiAnRGV0ZWN0IGFuZCByZWRhY3QgUElJJyxcbiAgICAgICdQT1NUIC9jbGFzc2lmeSc6ICdDbGFzc2lmeSBvdXRwdXQgaW50ZW50IGFuZCBoYWxsdWNpbmF0aW9uIHJpc2snXG4gICAgfSxcbiAgICBkb2N1bWVudGF0aW9uOiAnaHR0cHM6Ly9naXRodWIuY29tL3N1Ym9kaGtjL2xsbXZlcmlmeS1ucG0jcmVhZG1lJyxcbiAgICBwcml2YWN5OiAnQWxsIHByb2Nlc3NpbmcgaXMgbG9jYWwuIFplcm8gdGVsZW1ldHJ5LidcbiAgfSk7XG59KTtcblxuLy8gNDA0IGhhbmRsZXJcbmFwcC51c2UoKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSkgPT4ge1xuICByZXMuc3RhdHVzKDQwNCkuanNvbih7XG4gICAgZXJyb3I6ICdFbmRwb2ludCBub3QgZm91bmQnLFxuICAgIGF2YWlsYWJsZTogWydHRVQgLycsICdHRVQgL2hlYWx0aCcsICdQT1NUIC92ZXJpZnknLCAnUE9TVCAvY2hlY2staW5wdXQnLCAnUE9TVCAvY2hlY2stcGlpJywgJ1BPU1QgL2NsYXNzaWZ5J11cbiAgfSk7XG59KTtcblxuLy8gRXJyb3IgaGFuZGxlclxuYXBwLnVzZSgoZXJyOiBhbnksIHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSwgbmV4dDogYW55KSA9PiB7XG4gIGNvbnNvbGUuZXJyb3IoJ1NlcnZlciBlcnJvcjonLCBlcnIpO1xuICByZXMuc3RhdHVzKDUwMCkuanNvbih7XG4gICAgZXJyb3I6ICdJbnRlcm5hbCBzZXJ2ZXIgZXJyb3InLFxuICAgIG1lc3NhZ2U6IGVyci5tZXNzYWdlXG4gIH0pO1xufSk7XG5cbi8vIFN0YXJ0IHNlcnZlclxuZXhwb3J0IGZ1bmN0aW9uIHN0YXJ0U2VydmVyKHBvcnQ6IG51bWJlciA9IDkwMDkpIHtcbiAgY29uc3Qgc2VydmVyID0gYXBwLmxpc3Rlbihwb3J0LCAoKSA9PiB7XG4gICAgY29uc29sZS5sb2coYFxu4pWU4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWXXG7ilZEgIGxsbXZlcmlmeSBzZXJ2ZXIgdiR7VkVSU0lPTn0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg4pWRXG7ilZEgIFJ1bm5pbmcgb24gaHR0cDovL2xvY2FsaG9zdDoke3BvcnR9ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIOKVkVxu4pWa4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWQ4pWdXG5cbkF2YWlsYWJsZSBlbmRwb2ludHM6XG4gIEdFVCAgaHR0cDovL2xvY2FsaG9zdDoke3BvcnR9L2hlYWx0aCAgICAgICAgLSBIZWFsdGggY2hlY2tcbiAgUE9TVCBodHRwOi8vbG9jYWxob3N0OiR7cG9ydH0vdmVyaWZ5ICAgICAgICAtIFZlcmlmeSBBSSBvdXRwdXRcbiAgUE9TVCBodHRwOi8vbG9jYWxob3N0OiR7cG9ydH0vY2hlY2staW5wdXQgICAtIENoZWNrIGlucHV0IHNhZmV0eVxuICBQT1NUIGh0dHA6Ly9sb2NhbGhvc3Q6JHtwb3J0fS9jaGVjay1waWkgICAgIC0gRGV0ZWN0IFBJSVxuICBQT1NUIGh0dHA6Ly9sb2NhbGhvc3Q6JHtwb3J0fS9jbGFzc2lmeSAgICAgIC0gQ2xhc3NpZnkgb3V0cHV0XG5cblByaXZhY3k6IEFsbCBwcm9jZXNzaW5nIGlzIDEwMCUgbG9jYWwuIFplcm8gdGVsZW1ldHJ5LlxuXG5QcmVzcyBDdHJsK0MgdG8gc3RvcCB0aGUgc2VydmVyLlxuYCk7XG4gIH0pO1xuXG4gIC8vIEdyYWNlZnVsIHNodXRkb3duXG4gIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCAoKSA9PiB7XG4gICAgY29uc29sZS5sb2coJ1xcblNodXR0aW5nIGRvd24gc2VydmVyLi4uJyk7XG4gICAgc2VydmVyLmNsb3NlKCgpID0+IHtcbiAgICAgIGNvbnNvbGUubG9nKCdTZXJ2ZXIgc3RvcHBlZC4nKTtcbiAgICAgIHByb2Nlc3MuZXhpdCgwKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgcHJvY2Vzcy5vbignU0lHSU5UJywgKCkgPT4ge1xuICAgIGNvbnNvbGUubG9nKCdcXG5TaHV0dGluZyBkb3duIHNlcnZlci4uLicpO1xuICAgIHNlcnZlci5jbG9zZSgoKSA9PiB7XG4gICAgICBjb25zb2xlLmxvZygnU2VydmVyIHN0b3BwZWQuJyk7XG4gICAgICBwcm9jZXNzLmV4aXQoMCk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIHJldHVybiBzZXJ2ZXI7XG59XG5cbi8vIEV4cG9ydCBhcHAgZm9yIHRlc3RpbmdcbmV4cG9ydCB7IGFwcCB9O1xuXG4vLyBDTEkgZW50cnkgcG9pbnRcbmlmIChyZXF1aXJlLm1haW4gPT09IG1vZHVsZSkge1xuICBjb25zdCBhcmdzID0gcHJvY2Vzcy5hcmd2LnNsaWNlKDIpO1xuICBjb25zdCBwb3J0QXJnID0gYXJncy5maW5kKGFyZyA9PiBhcmcuc3RhcnRzV2l0aCgnLS1wb3J0PScpKTtcbiAgY29uc3QgcG9ydCA9IHBvcnRBcmcgPyBwYXJzZUludChwb3J0QXJnLnNwbGl0KCc9JylbMV0sIDEwKSA6IDkwMDk7XG4gIFxuICBpZiAoaXNOYU4ocG9ydCkgfHwgcG9ydCA8IDEgfHwgcG9ydCA+IDY1NTM1KSB7XG4gICAgY29uc29sZS5lcnJvcignSW52YWxpZCBwb3J0IG51bWJlci4gTXVzdCBiZSBiZXR3ZWVuIDEgYW5kIDY1NTM1LicpO1xuICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgfVxuICBcbiAgc3RhcnRTZXJ2ZXIocG9ydCk7XG59XG4iXX0=