diff options
Diffstat (limited to 'modules/ext')
| -rw-r--r-- | modules/ext/cryptogzip.js | 67 | ||||
| -rw-r--r-- | modules/ext/media.js | 6 | ||||
| -rw-r--r-- | modules/ext/webdav.js | 20 |
3 files changed, 71 insertions, 22 deletions
diff --git a/modules/ext/cryptogzip.js b/modules/ext/cryptogzip.js index 200abdd..cc275df 100644 --- a/modules/ext/cryptogzip.js +++ b/modules/ext/cryptogzip.js @@ -15,7 +15,8 @@ class CryptoGzipModule { 'decrypt': 'decrypt <path.enc> <passphrase> — Decrypt a .enc file', 'tar': 'tar <src-dir> <out.tar[.gz]> — Bundle a VFS directory into a tar archive (gzip if .gz)', 'untar': 'untar <path.tar[.gz]> [dest-dir] — Extract a tar archive into VFS (auto-detects gzip)', - 'wget': 'wget <url> [filename] — Download a URL and save to VFS' + 'wget': 'wget <url> [filename] — Download a URL (via /api/fetch) and save it to VFS', + 'curl': 'curl [-s] [-o FILE] <url> — Same as wget: proxy fetch + verified VFS write' }; } @@ -342,7 +343,10 @@ class CryptoGzipModule { } // ============================================================ - // WGET — fetch URL and save to VFS (binary-safe) + // WGET — fetch a URL through the /api/fetch proxy and save to VFS + // A direct browser fetch() only works same-origin: cross-origin URLs + // throw "Failed to fetch" (CORS), measured 2026-11-06 on the served + // v4.6.150 page. /api/fetch returns the raw source as UTF-8 text. // ============================================================ async _cmdWget(args) { @@ -350,20 +354,40 @@ class CryptoGzipModule { this.terminal.print('Usage: wget <url> [filename] — Download a URL and save to VFS', 'warning'); return; } - const url = args[0]; - const filename = args[1] || url.split('/').pop() || 'download.bin'; + const url = /^https?:\/\//i.test(args[0]) ? args[0] : 'https://' + args[0]; + const filename = args[1] || url.split('/').pop().split('?')[0] || 'download.bin'; this.terminal.print(`Downloading ${url}...`, 'info'); + let text; try { - const resp = await fetch(url); - if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${resp.statusText}`); - const buf = await resp.arrayBuffer(); - const bytes = new Uint8Array(buf); - this._vfsWriteBytes(filename, bytes); - const sizeKb = (bytes.length / 1024).toFixed(1); - this.terminal.print(`✅ wget: ${filename} (${sizeKb} KB, ${bytes.length} bytes)`, 'success'); + const resp = await fetch('/api/fetch?url=' + encodeURIComponent(url)); + if (resp.status === 403) { + this.terminal.print('wget error: URL blocked — internal network', 'error'); + return; + } + if (!resp.ok) { + let detail = 'HTTP ' + resp.status; + try { const j = await resp.json(); detail = j.error || detail; } catch (e) {} + this.terminal.print('wget error: ' + detail, 'error'); + return; + } + text = await resp.text(); } catch (e) { - this.terminal.print(`wget error: ${e.message}`, 'error'); + this.terminal.print('wget error: ' + e.message, 'error'); + return; + } + // writeFile() returns false rather than throwing when the parent directory is + // missing — the old code printed ✅ on that path (measured: writeFile('nosuchdir/x.txt') === false). + const dir = filename.includes('/') ? filename.slice(0, filename.lastIndexOf('/')) : ''; + if (dir) { try { this.terminal.fs.mkdir(dir); } catch (e) {} } + if (!this.terminal.fs.writeFile(filename, text)) { + this.terminal.print(`wget error: could not write ${filename} (missing directory, or path is a directory)`, 'error'); + return; } + if (this.terminal.fs.readFile(filename) !== text) { + this.terminal.print(`wget error: write verification failed for ${filename}`, 'error'); + return; + } + this.terminal.print(`✅ wget: ${filename} (${(text.length / 1024).toFixed(1)} KB, ${text.length} chars)`, 'success'); } // ============================================================ @@ -582,6 +606,22 @@ class CryptoGzipModule { this.commands['tar'] = async (args) => self._cmdTar(args); this.commands['untar'] = async (args) => self._cmdUntar(args); this.commands['wget'] = async (args) => self._cmdWget(args); + // curl — the flags an agent actually types. No shell semantics: -s/-L/-f are + // accepted and ignored, -o/--output selects the VFS target. Delegates to the + // single verified writer above so the two cannot drift. + this.commands['curl'] = async (args) => { + const positional = []; + let out = null; + for (let i = 0; i < args.length; i++) { + const a = String(args[i]); + if (a === '-o' || a === '-O' || a === '--output') { out = args[++i]; } + else if (a.startsWith('-')) { /* accepted, ignored */ } + else positional.push(a); + } + if (!positional[0]) { this.terminal.print('Usage: curl [-s] [-o FILE] <url>', 'warning'); return; } + const target = out || (positional[0].split('/').pop().split('?')[0] || 'download.bin'); + return self._cmdWget([positional[0], target]); + }; this.commandsMeta = { gzip: { argType: 'file', description: 'Compress a VFS file to .gz' }, gunzip: { argType: 'file', description: 'Decompress a .gz file (binary-safe)' }, @@ -589,7 +629,8 @@ class CryptoGzipModule { decrypt: { argType: 'file', description: 'Decrypt a .enc file' }, tar: { argType: 'file', description: 'Bundle a VFS directory into a tar archive' }, untar: { argType: 'file', description: 'Extract a tar archive into VFS' }, - wget: { argType: 'url', description: 'Download a URL and save to VFS' } + wget: { argType: 'url', description: 'Download a URL and save to VFS' }, + curl: { argType: 'url', description: 'curl [-s] [-o FILE] <url> — same as wget' } }; } } diff --git a/modules/ext/media.js b/modules/ext/media.js index 5723e27..bdc50e2 100644 --- a/modules/ext/media.js +++ b/modules/ext/media.js @@ -1,13 +1,13 @@ // newOS4 Media Analysis Module (v4.5.79) // Commands: analyze <path> [--flags] // Uses gemini-3-flash-preview:cloud for vision (images, video frames) -// Uses deepseek-v4-flash:cloud for text analysis (audio features, summaries) +// Uses deepseek-v4.1-flash:cloud for text analysis (audio features, summaries) class MediaModule { constructor() { this.commands = {}; this.terminal = null; this._visionModel = 'gemini-3-flash-preview:cloud'; - this._textModel = 'deepseek-v4-flash:cloud'; + this._textModel = 'deepseek-v4.1-flash:cloud'; this._maxFrames = 10; // max video frames to analyze this._frameInterval = 5; // seconds between video frames } @@ -118,7 +118,7 @@ class MediaModule { } // Send features to text model for AI interpretation - this.terminal.print('\n🤖 AI interpretation (deepseek-v4-flash)...', 'info'); + this.terminal.print('\n🤖 AI interpretation (deepseek-v4.1-flash)...', 'info'); const prompt = `Analyze these audio features and describe what the audio likely sounds like. Consider the genre, mood, and characteristics: Duration: ${features.duration.toFixed(1)}s diff --git a/modules/ext/webdav.js b/modules/ext/webdav.js index a97a8bc..fa42b96 100644 --- a/modules/ext/webdav.js +++ b/modules/ext/webdav.js @@ -976,12 +976,20 @@ class WebDAVModule { }; const mount = document.getElementById('wdav-tg-mount'); // A Telegram widget cannot log anyone in inside an Android WebView or an in-app - // browser: it needs a popup that can postMessage back to the opener, and those hosts - // hand the link to the system browser, which has no opener. Measured 2026-11-02 (the - // operator's phone UA ends "...; wv)"): every attempt landed in Chrome and died there. - // In that case skip the widget entirely and start the code flow, which needs no popup. - const _tgWV = /;\s*wv\)/.test(navigator.userAgent || '') || - /\bTelegram\b/i.test(navigator.userAgent || ''); + // browser: tapping it hands the whole tab to oauth.telegram.org and must come back to + // this page with #tgAuthResult. Those hosts either hand the navigation to the system + // browser (no way back) or never return. Measured 2026-11-03, operator's phone. + // Fallback only: modules/wx11-tg-webview.js is the source of truth when the host page + // loaded it (the dashboard does). This inline copy missed "Version/4.0" WebViews — the + // operator's own phone — until 2026-11-03, and the Meta/Line classes until the review + // that followed. Probe: /tg-widget-test.html. + const _ua = navigator.userAgent || ''; + const _tgWV = (window.WX11TG && window.WX11TG.isWebView) + ? window.WX11TG.isWebView() + : (/;\s*wv\)/.test(_ua) || + (/\bVersion\/4\.0\b/.test(_ua) && /Android/i.test(_ua) && !/\bSamsungBrowser\b/.test(_ua)) || + /\bTelegram\b/i.test(_ua) || + /\bFBAN\b|\bFBAV\b|\bInstagram\b|\bLine\//i.test(_ua)); const tw = document.createElement('script'); tw.src = 'https://telegram.org/js/telegram-widget.js?22'; tw.setAttribute('data-telegram-login', 'roducbot'); |