const vscode = require('vscode'); const https = require('https'); const http = require('http'); const os = require('os'); const fs = require('fs'); const crypto = require('crypto'); let currentAgent = 'alfred'; let convId = null; let csrfToken = null; let sessionCookie = null; let userProfile = null; let hmacSecretCache = null; const IDE_SESSION_BRIDGES = [ '/home/gositeme/domains/gositeme.com/logs/alfred-ide/session.json', '/home/gositeme/.alfred-ide/session.json' ]; function getAlfredHmacSecret() { if (hmacSecretCache) return hmacSecretCache; const candidates = [ '/home/gositeme/domains/gocodeme.com/public_html/.env', '/home/gositeme/domains/gositeme.com/public_html/gocodeme/mcp-server/.env', ]; for (const p of candidates) { try { if (!fs.existsSync(p)) continue; const txt = fs.readFileSync(p, 'utf8'); const m = txt.match(/ALFRED_HMAC_SECRET=([^\r\n]+)/); if (m && m[1]) { hmacSecretCache = String(m[1]).trim(); return hmacSecretCache; } } catch (_) {} } hmacSecretCache = process.env.ALFRED_HMAC_SECRET || 'gositeme-alfred-hmac-2026'; return hmacSecretCache; } function buildIdeIdentityPayload() { try { if (!userProfile || !userProfile.client_id) return null; const cid = String(userProfile.client_id).replace(/[^0-9]/g, ''); if (!cid) return null; const name = String(userProfile.name || userProfile.user || 'User').replace(/[^a-zA-Z0-9 _\-.]/g, '').slice(0, 120); const ts = Math.floor(Date.now() / 1000); const secret = getAlfredHmacSecret(); const sig = crypto.createHmac('sha256', secret).update(`${cid}|${name}|${ts}`).digest('hex'); return { ide_client_id: parseInt(cid, 10), ide_name: name, ide_ts: ts, ide_sig: sig, ide_email: String(userProfile.email || '').slice(0, 190), }; } catch (_) { return null; } } function readLocalIdeToken() { const envToken = (process.env.ALFRED_IDE_TOKEN || '').trim(); if (envToken) return envToken; try { for (const bridgePath of IDE_SESSION_BRIDGES) { if (!fs.existsSync(bridgePath)) continue; const raw = fs.readFileSync(bridgePath, 'utf8'); const data = JSON.parse(raw || '{}'); const token = String(data.token || '').trim(); const exp = Number(data.expires_at || 0); if (!token) continue; if (exp > 0 && Date.now() >= (exp * 1000)) continue; return token; } return ''; } catch (_) { return ''; } } function activate(context) { const provider = new AlfredCommanderProvider(context.extensionUri, context); context.subscriptions.push( vscode.window.registerWebviewViewProvider('alfred-commander.panel', provider, { webviewOptions: { retainContextWhenHidden: true } }) ); context.subscriptions.push( vscode.commands.registerCommand('alfred-commander.open', () => { vscode.commands.executeCommand('alfred-commander.panel.focus'); }) ); context.subscriptions.push( vscode.commands.registerCommand('alfred-commander.toggle', () => { if (provider.view) { provider.view.webview.postMessage({ type: 'toggle-listening' }); } else { vscode.commands.executeCommand('alfred-commander.panel.focus'); } }) ); context.subscriptions.push( vscode.commands.registerCommand('alfred-commander.showStats', () => { showStatsPanel(context); }) ); context.subscriptions.push( vscode.commands.registerCommand('alfred-commander.welcome', () => { vscode.commands.executeCommand('workbench.action.openWalkthrough', 'gositeme.alfred-commander#alfred-ide-getting-started', true); }) ); // --- Workspace Status Panel --- context.subscriptions.push( vscode.commands.registerCommand('alfred-commander.workspaceStatus', async () => { const panel = vscode.window.createWebviewPanel( 'alfredWorkspaceStatus', 'Workspace Status', vscode.ViewColumn.One, { enableScripts: true } ); const sessionFile = require('path').join(context.globalStorageUri?.fsPath || '', '..', '..', '..', '..', '.alfred-ide-session.json'); let bearerToken = ''; try { const sData = JSON.parse(require('fs').readFileSync( require('path').join(require('os').homedir(), '.alfred-ide-session.json'), 'utf8' )); bearerToken = sData.token || ''; } catch (_) {} panel.webview.html = getWorkspaceStatusHTML(bearerToken); }) ); const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); statusBar.text = '$(mic) Alfred'; statusBar.tooltip = 'Alfred — Toggle mic (Ctrl+Shift+Alt+A)'; statusBar.command = 'alfred-commander.toggle'; statusBar.color = '#e2b340'; statusBar.show(); context.subscriptions.push(statusBar); const userStatus = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 0); userStatus.command = 'alfred-commander.showStats'; context.subscriptions.push(userStatus); fetchUserProfile(context).then(profile => { userProfile = profile; userStatus.text = `$(account) Logged in as ${profile.name}`; userStatus.tooltip = `${profile.name} (${profile.email || profile.user}) — Click for Account & Usage Stats`; userStatus.color = '#4ec9b0'; userStatus.show(); if (provider.view) { provider.view.webview.postMessage({ type: 'user-profile', profile }); } }).catch(() => { const osUser = os.userInfo().username || 'gositeme'; userStatus.text = `$(account) Logged in as ${osUser}`; userStatus.tooltip = `Authenticated as ${osUser} — Click for Account & Usage Stats`; userStatus.color = '#4ec9b0'; userStatus.show(); }); } async function fetchUserProfile(context) { const saved = context.globalState.get('alfredCommanderUserProfile'); if (saved && saved.name && saved.fetchedAt) { const age = Date.now() - saved.fetchedAt; const authenticated = !!saved.client_id; if ((authenticated && age < 3600000) || (!authenticated && age < 60000)) { return saved; } } const ideToken = readLocalIdeToken(); try { const profile = await new Promise((resolve, reject) => { const headers = { 'X-Alfred-Source': 'ide-extension' }; if (ideToken) { headers['Authorization'] = 'Bearer ' + ideToken; headers['X-Alfred-IDE-Token'] = ideToken; } const req = https.request({ hostname: 'gositeme.com', port: 443, path: '/api/alfred-ide-session.php', method: 'GET', headers, timeout: 10000 }, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const j = JSON.parse(data); if (j.valid && j.name) resolve(j); else reject(new Error('Invalid session')); } catch { reject(new Error('Parse error')); } }); }); req.on('error', reject); req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); }); req.end(); }); const result = { name: profile.name, email: profile.email, avatar: profile.avatar || '', user: profile.email, client_id: profile.client_id, fetchedAt: Date.now() }; context.globalState.update('alfredCommanderUserProfile', result); return result; } catch { // Fallback: read identity from session bridge file (works without network) for (const bridgePath of IDE_SESSION_BRIDGES) { try { if (!fs.existsSync(bridgePath)) continue; const bData = JSON.parse(fs.readFileSync(bridgePath, 'utf8') || '{}'); if (bData.client_id) { return { name: bData.name || 'User', user: bData.email || '', email: bData.email || '', client_id: bData.client_id, fetchedAt: Date.now() }; } } catch (_) {} } const osUser = os.userInfo().username || 'gositeme'; return { name: osUser, user: osUser, email: '', fetchedAt: Date.now() }; } } async function ensureFreshUserProfile(context) { if (!userProfile || !userProfile.client_id || !userProfile.fetchedAt || (Date.now() - userProfile.fetchedAt) > 300000) { userProfile = await fetchUserProfile(context); } return userProfile; } class AlfredCommanderProvider { constructor(extensionUri, context) { this.extensionUri = extensionUri; this.context = context; this.view = null; } resolveWebviewView(webviewView) { this.view = webviewView; webviewView.webview.options = { enableScripts: true, localResourceRoots: [this.extensionUri] }; // Inject session token and identity so webview can call API directly (bypasses broken IPC) const injToken = readLocalIdeToken(); const injIdentity = buildIdeIdentityPayload(); webviewView.webview.html = getWebviewContent(injToken, injIdentity || {}); webviewView.webview.onDidReceiveMessage(async (msg) => { if (msg.type === 'ai-request') { console.log('[alfred-commander] ai-request received:', msg.text && msg.text.substring(0, 80), 'agent=' + msg.agent, 'model=' + msg.model, 'id=' + msg.id); try { ensureFreshUserProfile(this.context).then((refreshed) => { if (!refreshed) return; webviewView.webview.postMessage({ type: 'user-profile', profile: refreshed }); }).catch(() => {}); const ctx = getEditorContext(); console.log('[alfred-commander] Calling queryAlfredAPI...'); const response = await queryAlfredAPI(msg.text, msg.agent || 'alfred', ctx, msg.model || 'sonnet', msg.images || [], msg.pdf_files || [], msg.attachment_texts || [], msg.zip_files || [], msg.multiplier || 30); console.log('[alfred-commander] Got response:', response.text && response.text.substring(0, 100)); webviewView.webview.postMessage({ type: 'ai-response', text: response.text, agent: response.agent, id: msg.id, identity: response.identity || null, attachment_report: response.attachmentReport || [] }); generateTTS(response.text).then(audioB64 => { if (audioB64) { webviewView.webview.postMessage({ type: 'play-audio', audio: audioB64 }); } }).catch(() => {}); } catch (err) { console.error('[alfred-commander] ai-request ERROR:', err.message); webviewView.webview.postMessage({ type: 'ai-response', text: 'Sorry, I had trouble processing that. ' + err.message, agent: 'alfred', id: msg.id }); } } else if (msg.type === 'stt-request') { transcribeAudio(msg.audio, msg.mime).then(text => { webviewView.webview.postMessage({ type: 'stt-result', text: text || '', id: msg.id }); }).catch(() => { webviewView.webview.postMessage({ type: 'stt-result', text: '', id: msg.id, error: 'Transcription failed' }); }); } else if (msg.type === 'ide-quick') { const map = { terminal: 'workbench.action.terminal.new', save: 'workbench.action.files.save', saveAll: 'workbench.action.files.saveAll', palette: 'workbench.action.showCommands', split: 'workbench.action.splitEditor', newFile: 'workbench.action.files.newUntitledFile', }; const cmd = map[msg.cmd]; if (cmd) vscode.commands.executeCommand(cmd); } else if (msg.type === 'insert-code') { const editor = vscode.window.activeTextEditor; if (editor) { editor.edit(editBuilder => { editBuilder.insert(editor.selection.active, msg.code); }); } } else if (msg.type === 'run-terminal') { const terminal = vscode.window.createTerminal('Alfred Command'); terminal.show(); terminal.sendText(msg.command); webviewView.webview.postMessage({ type: 'command-result', text: 'Running in terminal: ' + msg.command }); } else if (msg.type === 'set-agent') { currentAgent = msg.agent; } else if (msg.type === 'tts-request') { generateTTS(msg.text).then(audioB64 => { if (audioB64) { webviewView.webview.postMessage({ type: 'play-audio', audio: audioB64 }); } }).catch(() => {}); } else if (msg.type === 'save-profile') { if (msg.profile) { this.context.globalState.update('alfredCommanderUserProfile', msg.profile); userProfile = msg.profile; } } }); if (userProfile) { setTimeout(() => { webviewView.webview.postMessage({ type: 'user-profile', profile: userProfile }); }, 500); } } } function transcribeAudio(audioB64, mime) { if (!audioB64) return Promise.resolve(''); return new Promise((resolve) => { const raw = Buffer.from(audioB64, 'base64'); if (raw.length < 100) { resolve(''); return; } const ext = (mime || '').includes('wav') ? 'wav' : (mime || '').includes('ogg') ? 'ogg' : (mime || '').includes('mp4') || (mime || '').includes('m4a') ? 'm4a' : 'webm'; const boundary = '----AlfredSTT' + Date.now(); const filename = 'audio.' + ext; const contentType = mime || 'audio/webm'; const header = Buffer.from( `--${boundary}\r\nContent-Disposition: form-data; name="audio"; filename="${filename}"\r\nContent-Type: ${contentType}\r\n\r\n` ); const modelField = Buffer.from( `\r\n--${boundary}\r\nContent-Disposition: form-data; name="model"\r\n\r\nwhisper-1\r\n--${boundary}--\r\n` ); const body = Buffer.concat([header, raw, modelField]); const req = https.request({ hostname: 'gositeme.com', port: 443, path: '/api/stt.php', method: 'POST', headers: { 'Content-Type': 'multipart/form-data; boundary=' + boundary, 'Content-Length': body.length, 'X-Alfred-Source': 'ide-extension' }, timeout: 60000 }, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const j = JSON.parse(data); resolve(j.text || ''); } catch { resolve(''); } }); }); req.on('error', () => resolve('')); req.on('timeout', () => { req.destroy(); resolve(''); }); req.write(body); req.end(); }); } function generateTTS(text) { if (!text || text.length < 2) return Promise.resolve(null); const clean = text.replace(/```[\s\S]*?```/g, ' code block ') .replace(/[*_`#~\[\]]/g, '') .replace(/https?:\/\/\S+/g, '') .replace(/\s+/g, ' ') .trim() .substring(0, 4000); if (clean.length < 2) return Promise.resolve(null); return new Promise((resolve) => { const body = JSON.stringify({ text: clean, voice: 'onyx' }); const req = https.request({ hostname: 'gositeme.com', port: 443, path: '/api/tts.php', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'X-Alfred-Source': 'ide-extension' }, timeout: 30000 }, (res) => { if (res.statusCode !== 200) { resolve(null); return; } const chunks = []; res.on('data', chunk => chunks.push(chunk)); res.on('end', () => { const buf = Buffer.concat(chunks); if (buf.length < 200) { resolve(null); return; } const contentType = res.headers['content-type'] || 'audio/mpeg'; resolve('data:' + contentType + ';base64,' + buf.toString('base64')); }); }); req.on('error', () => resolve(null)); req.on('timeout', () => { req.destroy(); resolve(null); }); req.write(body); req.end(); }); } function getEditorContext() { const editor = vscode.window.activeTextEditor; if (!editor) return ''; const doc = editor.document; const sel = editor.selection; let ctx = `[IDE Context] File: ${doc.fileName}, Language: ${doc.languageId}, Lines: ${doc.lineCount}`; if (!sel.isEmpty) { const selected = doc.getText(sel).substring(0, 500); ctx += `, Selected code:\n\`\`\`${doc.languageId}\n${selected}\n\`\`\``; } else { const line = doc.lineAt(sel.active.line); ctx += `, Current line ${sel.active.line + 1}: ${line.text.substring(0, 200)}`; } return ctx; } async function queryAlfredAPI(prompt, agent, editorContext, selectedModel = 'sonnet', images = [], pdfFiles = [], attachmentTexts = [], zipFiles = [], multiplier = 30) { const payload = { message: prompt, agent: agent || 'alfred', context: editorContext || '', channel: 'ide-chat', conv_id: convId || '', model: selectedModel, token_multiplier: multiplier || 30 }; const ideToken = readLocalIdeToken(); const ideIdentity = buildIdeIdentityPayload(); // Duplicate token in JSON body — some stacks strip Authorization / X-Alfred-IDE-Token before PHP sees them. if (ideToken) { payload.ide_session_token = ideToken; } if (ideIdentity) { payload.ide_client_id = ideIdentity.ide_client_id; payload.ide_name = ideIdentity.ide_name; payload.ide_ts = ideIdentity.ide_ts; payload.ide_sig = ideIdentity.ide_sig; payload.ide_email = ideIdentity.ide_email; } if (Array.isArray(images) && images.length > 0) { payload.images = images; } if (Array.isArray(pdfFiles) && pdfFiles.length > 0) { payload.pdf_files = pdfFiles; } if (Array.isArray(attachmentTexts) && attachmentTexts.length > 0) { payload.attachment_texts = attachmentTexts; } if (Array.isArray(zipFiles) && zipFiles.length > 0) { payload.zip_files = zipFiles; } const body = JSON.stringify(payload); const doRequest = (csrf, cookie) => { return new Promise((resolve, reject) => { const headers = { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body), 'X-Alfred-Source': 'alfred-ide' }; if (csrf) headers['X-CSRF-Token'] = csrf; if (cookie) headers['Cookie'] = cookie; if (ideToken) { headers['Authorization'] = 'Bearer ' + ideToken; // Apache/CGI often strips Authorization before PHP — duplicate token for alfred_resolve_ide_bearer_token() headers['X-Alfred-IDE-Token'] = ideToken; } const req = https.request({ hostname: 'gositeme.com', port: 443, path: '/api/alfred-chat.php', method: 'POST', headers, timeout: 120000 }, (res) => { // Node may give Set-Cookie as a string OR an array — iterating a string walks each CHARACTER and breaks PHPSESSID capture. const setCookie = res.headers['set-cookie']; if (setCookie) { const parts = Array.isArray(setCookie) ? setCookie : [setCookie]; for (let i = 0; i < parts.length; i++) { const m = String(parts[i]).match(/PHPSESSID=([^;]+)/); if (m) { sessionCookie = 'PHPSESSID=' + m[1]; break; } } } let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { resolve(JSON.parse(data)); } catch (e) { console.error('[alfred-commander] Non-JSON response HTTP ' + res.statusCode + ':', data.substring(0, 600)); resolve({ response: data.trim() || 'Alfred returned an empty response. Please send your message again.' }); } }); }); req.on('error', (err) => reject(new Error('API failed: ' + err.message))); req.on('timeout', () => { req.destroy(); reject(new Error('Timed out (120s)')); }); req.write(body); req.end(); }); }; let result = await doRequest(csrfToken, sessionCookie); if (result.csrf_token) csrfToken = result.csrf_token; for (let csrfAttempt = 0; csrfAttempt < 4 && result && (result.csrf_refresh || result.response === 'Session initialized. Please retry.' || result.error === 'CSRF validation failed'); csrfAttempt++) { result = await doRequest(csrfToken, sessionCookie); if (result.csrf_token) csrfToken = result.csrf_token; } if (!result || (!result.response && !result.message && !result.error)) { logTelemetry('empty-response: retrying once'); result = await doRequest(csrfToken, sessionCookie); if (result && result.csrf_token) csrfToken = result.csrf_token; } if (result.conv_id) convId = result.conv_id; const errText = result && result.error ? String(result.error) : ''; const textOut = (result && (result.response || result.message)) ? (result.response || result.message) : (errText ? ('⚠️ ' + errText) : 'No response from AI'); return { text: textOut, agent: (result && result.agent) ? result.agent : agent, identity: result && result.identity ? result.identity : null, attachmentReport: (result && result.attachment_report) ? result.attachment_report : [] }; } let statsPanel = null; function showStatsPanel(context) { if (statsPanel) { statsPanel.reveal(vscode.ViewColumn.One); return; } statsPanel = vscode.window.createWebviewPanel( 'alfredStats', 'Account & Usage Stats', vscode.ViewColumn.One, { enableScripts: true, retainContextWhenHidden: false } ); statsPanel.onDidDispose(() => { statsPanel = null; }); const ideToken = readLocalIdeToken(); const headers = { 'X-Alfred-Source': 'ide-extension' }; if (ideToken) { headers['Authorization'] = 'Bearer ' + ideToken; headers['X-Alfred-IDE-Token'] = ideToken; } const req = https.request({ hostname: 'gositeme.com', port: 443, path: '/api/alfred-ide-session.php?action=stats', method: 'GET', headers, timeout: 15000 }, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const stats = JSON.parse(data); if (statsPanel) { statsPanel.webview.html = getStatsHtml(stats); } } catch (e) { if (statsPanel) { statsPanel.webview.html = getStatsHtml({ valid: false, error: 'Failed to load stats: ' + e.message }); } } }); }); req.on('error', (e) => { if (statsPanel) { statsPanel.webview.html = getStatsHtml({ valid: false, error: 'Connection error: ' + e.message }); } }); req.on('timeout', () => { req.destroy(); }); req.end(); // Show loading state immediately statsPanel.webview.html = getStatsHtml(null); } function getStatsHtml(stats) { if (!stats) { return `

Loading your stats...

`; } if (!stats.valid) { return `

Account Stats

` + escHtml(stats.error || 'Unable to load stats') + `

`; } const name = escHtml(stats.name || 'User'); const email = escHtml(stats.email || ''); const plan = (stats.plan || 'free').toLowerCase(); const planDisplay = plan === 'commander' ? 'Commander' : (stats.plan || 'Free').charAt(0).toUpperCase() + (stats.plan || 'free').slice(1); const planColors = { commander: '#e2b340', free: '#6a737d', starter: '#3b82f6', professional: '#a855f7', enterprise: '#22c55e' }; const planColor = planColors[plan] || '#6a737d'; const tokensUsed = stats.tokens_used || 0; const tokensIncluded = stats.tokens_included || 50000; const tokensOverage = stats.tokens_overage || 0; const costOverage = stats.cost_overage_usd || 0; const isUnlimited = stats.unlimited || plan === 'commander'; const remaining = Math.max(0, tokensIncluded - tokensUsed); const pct = tokensIncluded > 0 ? Math.min(100, Math.round((tokensUsed / tokensIncluded) * 100)) : 0; const barColor = pct >= 90 ? '#ef4444' : pct >= 70 ? '#f59e0b' : '#3b82f6'; const fmtK = (n) => n >= 1000000 ? (n / 1000000).toFixed(1) + 'M' : n >= 1000 ? Math.round(n / 1000) + 'K' : String(n); // Services section let servicesHtml = ''; if (stats.services && stats.services.length > 0) { servicesHtml = stats.services.map(s => `${escHtml(s.product || 'Service')}${escHtml(s.domain || '—')}${escHtml(s.status)}$${parseFloat(s.amount || 0).toFixed(2)}/${escHtml(s.billing_cycle || 'N/A')}` ).join(''); } else { servicesHtml = 'No active services'; } // Recent invoices let invoicesHtml = ''; if (stats.recent_invoices && stats.recent_invoices.length > 0) { invoicesHtml = stats.recent_invoices.map(i => { const statusClass = i.status === 'Paid' ? 'status-active' : i.status === 'Unpaid' ? 'status-unpaid' : 'status-other'; return `#${escHtml(i.invoice_number || String(i.id))}$${parseFloat(i.total || 0).toFixed(2)}${escHtml(i.status)}${escHtml((i.due_date || i.created_at || '').substring(0, 10))}`; }).join(''); } else { invoicesHtml = 'No invoices'; } // Usage breakdown let usageBreakdownHtml = ''; if (stats.usage_breakdown && stats.usage_breakdown.length > 0) { usageBreakdownHtml = stats.usage_breakdown.map(u => `${escHtml(u.feature)}${escHtml(u.model)}${fmtK(parseInt(u.input_tokens || 0) + parseInt(u.output_tokens || 0))}${parseInt(u.requests || 0)}$${parseFloat(u.cost_usd || 0).toFixed(4)}` ).join(''); } else { usageBreakdownHtml = 'No usage this period'; } return `

$(account) ${name}

${email} · Last login: ${escHtml((stats.last_login || '').substring(0, 16))}
Plan
${planDisplay}
Credit Balance
$${escHtml(stats.credit_balance || '0.00')}
Active Services
${stats.active_services || 0}
Unpaid Invoices
${stats.unpaid_invoices || 0}
${(stats.unpaid_invoices || 0) > 0 ? 'Total due: $' + escHtml(stats.total_due || '0.00') : 'All clear'}
Token Usage — ${new Date().toLocaleString('en-US', { month: 'long', year: 'numeric' })} ${planDisplay}
${isUnlimited ? `
Unlimited usage — Commander plan
` : `
${fmtK(tokensUsed)} used of ${fmtK(tokensIncluded)} included (${fmtK(remaining)} remaining)${tokensOverage > 0 ? ' · Overage: ' + fmtK(tokensOverage) + ' tokens ($' + costOverage.toFixed(2) + ')' : ''}
` }
Usage Breakdown
${usageBreakdownHtml}
FeatureModelTokensRequestsCost
Active Services
${servicesHtml}
ProductDomainStatusAmount
Recent Invoices
${invoicesHtml}
InvoiceAmountStatusDate
`; } function escHtml(str) { return String(str || '').replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } function getWebviewContent(injectedToken, injectedIdentity) { const safeToken = (injectedToken || '').replace(/[^a-f0-9]/g, ''); const safeIdentity = JSON.stringify(injectedIdentity || {}); return `

Alfred

All models · Voice STT/TTS · Attachments · Ctrl+Shift+Alt+A · Enter sends
Plan · Loading...
IDE
Alfred
Ready. Claude Sonnet 4 connected. Speak or type.
Payload: 0 KB
Ready
`; } function deactivate() {} function getWorkspaceStatusHTML(token) { return `

🖥 Workspace Status

Loading workspace data...

`; } module.exports = { activate, deactivate };