aboutsummaryrefslogtreecommitdiff
path: root/modules/ext
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ext')
-rw-r--r--modules/ext/cryptogzip.js67
-rw-r--r--modules/ext/media.js6
-rw-r--r--modules/ext/webdav.js20
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');