UNPKG

@vectorchat/mcp-server

Version:

VectorChat MCP Server - Encrypted AI-to-AI communication with hardware security (YubiKey/TPM). 45+ MCP tools for Windsurf, Claude, and AI assistants. Model-based identity with EMDM encryption. Dynamic AI playbook system, communication zones, message relay

439 lines (372 loc) 15.6 kB
// VectorChat Setup Wizard - Client-side JavaScript let currentStep = 1; const totalSteps = 6; // Parse URL parameters to pre-fill form function getUrlParams() { const params = new URLSearchParams(window.location.search); return { username: params.get('username') || 'Allicia', modelType: params.get('modelType') || 'auto', modelPath: params.get('modelPath') || '', contextSize: params.get('contextSize') || '2048', enableIPFS: params.get('enableIPFS') === 'true' }; } // Pre-fill form with URL parameters function preFillForm() { const params = getUrlParams(); // Pre-fill identity if (document.getElementById('userId')) { document.getElementById('userId').value = params.username; } // Pre-fill model settings if (document.getElementById('modelType')) { document.getElementById('modelType').value = params.modelType; updateModelOptions(); } if (document.getElementById('modelPath') && params.modelPath) { document.getElementById('modelPath').value = params.modelPath; } if (document.getElementById('contextSize')) { document.getElementById('contextSize').value = params.contextSize; } if (document.getElementById('enableIPFS')) { document.getElementById('enableIPFS').checked = params.enableIPFS; } } function updateProgress() { const progress = ((currentStep - 1) / (totalSteps - 1)) * 100; document.getElementById('progressBar').style.width = progress + '%'; } function nextStep() { document.getElementById('step' + currentStep).classList.remove('active'); currentStep++; document.getElementById('step' + currentStep).classList.add('active'); updateProgress(); // Run step-specific actions if (currentStep === 2) { runSystemCheck(); } if (currentStep === 4) { // Check default model when entering model configuration step const modelType = document.getElementById('modelType').value; if (modelType === 'qwen3-1.7b') { checkDefaultModel(); } } } function prevStep() { document.getElementById('step' + currentStep).classList.remove('active'); currentStep--; document.getElementById('step' + currentStep).classList.add('active'); updateProgress(); } function updateModelOptions() { const modelType = document.getElementById('modelType').value; const customPath = document.getElementById('customModelPath'); const defaultModelInfo = document.getElementById('defaultModelInfo'); customPath.style.display = modelType === 'custom' ? 'block' : 'none'; // Show default model info for Qwen 3 1.7B if (defaultModelInfo) { defaultModelInfo.style.display = modelType === 'qwen3-1.7b' ? 'block' : 'none'; } // Check if model exists when selecting default model if (modelType === 'qwen3-1.7b') { checkDefaultModel(); } } // Check if default model exists async function checkDefaultModel() { const modelCheckIcon = document.getElementById('modelCheckIcon'); const modelCheckStatus = document.getElementById('modelCheckStatus'); const downloadSection = document.getElementById('downloadSection'); const step4Next = document.getElementById('step4Next'); try { const response = await fetch('/api/check-model'); const data = await response.json(); if (data.exists) { modelCheckIcon.textContent = '✓'; modelCheckIcon.className = 'status-icon success'; modelCheckStatus.textContent = `Model found: ${data.path}`; downloadSection.style.display = 'none'; step4Next.disabled = false; } else { modelCheckIcon.textContent = '⚠️'; modelCheckIcon.className = 'status-icon warning'; modelCheckStatus.textContent = 'Model not found - download required'; downloadSection.style.display = 'block'; step4Next.disabled = true; } } catch (error) { console.error('Model check failed:', error); modelCheckIcon.textContent = '✗'; modelCheckIcon.className = 'status-icon error'; modelCheckStatus.textContent = 'Error checking model'; } } // Download model from IPFS async function downloadModelFromIPFS() { const downloadBtn = document.getElementById('downloadBtn'); const downloadProgress = document.getElementById('downloadProgress'); const downloadProgressBar = document.getElementById('downloadProgressBar'); const downloadStatus = document.getElementById('downloadStatus'); const step4Next = document.getElementById('step4Next'); // Disable button and show progress downloadBtn.disabled = true; downloadBtn.textContent = '⏳ Downloading...'; downloadProgress.style.display = 'block'; try { const response = await fetch('/api/download-model', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ cid: 'QmXmPxxtLxtDgh4CYscgTDXyCbNfVWdzhG2UgS6fcX6mXS', name: 'Qwen3-1.7B-Q6_K.gguf', size: 1717986816 }) }); const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const text = decoder.decode(value); const lines = text.split('\n'); for (const line of lines) { if (!line.trim()) continue; try { const data = JSON.parse(line); if (data.progress !== undefined) { const percent = Math.round(data.progress * 100); downloadProgressBar.style.width = percent + '%'; downloadProgressBar.textContent = percent + '%'; const downloaded = formatBytes(data.downloaded); const total = formatBytes(data.total); downloadStatus.textContent = `Downloaded: ${downloaded} / ${total}`; } if (data.complete) { downloadProgressBar.style.width = '100%'; downloadProgressBar.textContent = '100%'; downloadStatus.textContent = '✓ Download complete!'; downloadBtn.textContent = '✓ Downloaded'; step4Next.disabled = false; // Update model check status const modelCheckIcon = document.getElementById('modelCheckIcon'); const modelCheckStatus = document.getElementById('modelCheckStatus'); modelCheckIcon.textContent = '✓'; modelCheckIcon.className = 'status-icon success'; modelCheckStatus.textContent = `Model downloaded: ${data.path}`; } if (data.error) { throw new Error(data.error); } } catch (e) { console.error('Parse error:', e); } } } } catch (error) { console.error('Download failed:', error); downloadStatus.textContent = '✗ Download failed: ' + error.message; downloadBtn.disabled = false; downloadBtn.textContent = '🔄 Retry Download'; } } // Format bytes to human-readable function formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]; } async function runSystemCheck() { try { const response = await fetch('/api/system-check'); const data = await response.json(); // Update Python status updateStatus('python', data.python); // Update Node status updateStatus('node', data.node); // Update dependencies status updateStatus('deps', data.dependencies); // Enable next button if all checks pass const allPassed = data.python.ok && data.node.ok && data.dependencies.ok; document.getElementById('step2Next').disabled = !allPassed; } catch (error) { console.error('System check failed:', error); // Show error state updateStatus('python', { ok: false, message: 'Connection error' }); updateStatus('node', { ok: false, message: 'Connection error' }); updateStatus('deps', { ok: false, message: 'Connection error' }); } } function updateStatus(type, data) { const icon = document.getElementById(type + 'Icon'); const status = document.getElementById(type + 'Status'); if (data.ok) { icon.textContent = '✓'; icon.className = 'status-icon success'; status.textContent = data.message; if (type === 'deps') { const fixBox = document.getElementById('depsFixBox'); if (fixBox) { fixBox.style.display = 'none'; } } } else { icon.textContent = '✗'; icon.className = 'status-icon error'; status.textContent = data.message; if (type === 'deps') { const fixBox = document.getElementById('depsFixBox'); const fixCommands = document.getElementById('depsFixCommands'); const fixTitle = document.getElementById('depsFixTitle'); const fixNote = document.getElementById('depsFixNote'); if (fixBox && fixCommands && fixTitle && fixNote && data.fix) { fixBox.style.display = 'block'; fixTitle.textContent = data.fix.title || 'Install required build tools:'; fixCommands.textContent = (data.fix.commands || []).join('\n'); fixNote.textContent = data.fix.note || ''; } } } } async function applyConfiguration() { const config = { userId: document.getElementById('userId').value, modelType: document.getElementById('modelType').value, modelPath: document.getElementById('modelPath').value, contextSize: parseInt(document.getElementById('contextSize').value), enableIPFS: document.getElementById('enableIPFS').checked, mcpPort: parseInt(document.getElementById('mcpPort').value), wsPort: parseInt(document.getElementById('wsPort').value) }; try { const response = await fetch('/api/configure', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); const result = await response.json(); if (result.success) { nextStep(); } else { alert('Configuration failed: ' + result.error); } } catch (error) { alert('Failed to apply configuration: ' + error.message); } } async function startDaemon() { try { const response = await fetch('/api/start-daemon', { method: 'POST' }); const result = await response.json(); if (result.success) { alert('✓ Daemon started successfully!\n\nYou can now use VectorChat with your AI assistants.'); window.close(); } else { alert('Failed to start daemon: ' + result.error); } } catch (error) { alert('Failed to start daemon: ' + error.message); } } // Quick Setup (minimal interaction) async function quickSetup() { const aiName = document.getElementById('aiName').value.trim(); const userName = document.getElementById('userName').value.trim(); if (!aiName) { alert('Please enter a name for your AI assistant'); return; } if (!userName) { alert('Please enter your name'); return; } // Use default configuration const config = { userId: aiName, // AI assistant name displayName: aiName, identityType: 'ai_assistant', modelType: 'qwen3-1.7b', modelPath: `~/.vectorchat/models/Qwen3-1.7B-Q6_K.gguf`, contextSize: 2048, enableIPFS: true, ipfsApi: 'http://localhost:5001', mcpPort: 8766, wsPort: 8767, userName: userName, // Store user name separately quickSetup: true }; // Save configuration try { const response = await fetch('/api/save-config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(config) }); if (response.ok) { // Check if model exists const modelCheck = await fetch('/api/check-model'); const modelData = await modelCheck.json(); if (!modelData.exists) { // Show model download step currentStep = 4; document.getElementById('step1').classList.remove('active'); document.getElementById('step4').classList.add('active'); updateProgress(); updateModelOptions(); } else { // Complete setup showSuccess(); } } else { alert('Failed to save configuration. Please try again.'); } } catch (error) { console.error('Quick setup error:', error); alert('Setup failed. Please try the advanced setup.'); } } function showSuccess() { // Hide all steps for (let i = 1; i <= 6; i++) { const step = document.getElementById(`step${i}`); if (step) step.style.display = 'none'; } // Show success message document.body.innerHTML = ` <div style="text-align: center; padding: 50px; font-family: Arial, sans-serif;"> <h1>🎉 VectorChat Setup Complete!</h1> <p>Your AI assistant is ready to use.</p> <p>You can now close this window and start using VectorChat.</p> <button onclick="window.location.href='/ui'" style="padding: 10px 20px; font-size: 16px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer;"> Open VectorChat UI </button> </div> `; } // Show advanced setup wizard function showAdvancedSetup() { // Copy values from quick setup to advanced fields const aiName = document.getElementById('aiName').value; const userName = document.getElementById('userName').value; document.getElementById('aiNameAdvanced').value = aiName; document.getElementById('userId').value = userName; // Go to system check step nextStep(); } // Initialize updateProgress(); // Pre-fill form when page loads window.addEventListener('DOMContentLoaded', () => { preFillForm(); // Show message if pre-filled const params = getUrlParams(); if (params.username !== 'Allicia') { console.log('Form pre-filled with AI-gathered preferences:', params); } // Initialize model selection display updateModelOptions(); });