class WebFetcherModule { constructor() { this.commands = {}; this.terminal = null; } getHelpInfo() { return { 'fetch': 'fetch — Fetch and display raw HTML/text content from a URL', 'web': 'web — Alias for fetch', 'extract': 'extract — Extract readable article content (title + body text) from a URL', }; } registerCommands() { this.commands['fetch'] = (args) => this.fetchUrl(args); this.commands['web'] = (args) => this.fetchUrl(args); this.commands['extract'] = (args) => this.extractUrl(args); } async fetchUrl(args) { if (!args[0]) { this.terminal.print("Usage: fetch ", "warning"); this.terminal.print("Example: fetch https://example.com", "info"); return; } let url = args[0]; if (!url.match(/^https?:\/\//i)) url = 'https://' + url; this.terminal.print(`🌐 Fetching: ${url}`, "info"); try { const resp = await fetch('/api/fetch?url=' + encodeURIComponent(url)); if (resp.status === 403) { this.terminal.print('Blocked: internal network addresses not allowed', 'error'); return; } if (!resp.ok) { this.terminal.print('Fetch failed: HTTP ' + resp.status, 'error'); return; } const text = await resp.text(); const cleanText = text.replace(/\s+/g, ' ').trim(); this.terminal.print(`\n=== Content from ${url} ===\n`, 'success'); this.terminal.print(cleanText.substring(0, 3000) + (cleanText.length > 3000 ? '...' : '')); this.terminal.print(`\nšŸ“ Content Length: ${cleanText.length} chars`, 'info'); } catch (e) { this.terminal.print('Fetch failed: ' + e.message, 'error'); } } async extractUrl(args) { if (!args[0]) { this.terminal.print("Usage: extract ", "warning"); this.terminal.print("Example: extract https://example.com", "info"); return; } let url = args[0]; if (!url.match(/^https?:\/\//i)) url = 'https://' + url; this.terminal.print(`šŸ“„ Extracting: ${url}`, "info"); try { const resp = await fetch('/api/extract?url=' + encodeURIComponent(url)); if (resp.status === 403) { this.terminal.print('Blocked: internal network addresses not allowed', 'error'); return; } if (!resp.ok) { const err = await resp.json().catch(() => ({})); this.terminal.print('Extract failed: ' + (err.error || 'HTTP ' + resp.status), 'error'); return; } const data = await resp.json(); if (data.title) this.terminal.print(`\nšŸ“° ${data.title}\n`, 'success'); this.terminal.print(data.content || '(no content extracted)'); this.terminal.print(`\nšŸ“ ${data.word_count || 0} words (via ${data.method || 'unknown'})`, 'info'); } catch (e) { this.terminal.print('Extract failed: ' + e.message, 'error'); } } }