diff --git a/bin/omniroute.mjs b/bin/omniroute.mjs index f0c5bcec44df..39dce0a3a17a 100644 --- a/bin/omniroute.mjs +++ b/bin/omniroute.mjs @@ -245,7 +245,8 @@ if (portIdx !== -1 && args[portIdx + 1]) { const apiPort = parsePort(process.env.API_PORT || String(port), port); const dashboardPort = parsePort(process.env.DASHBOARD_PORT || String(port), port); -const noOpen = args.includes("--no-open"); +const isAndroid = process.platform === 'android'; +let noOpen = args.includes("--no-open") || isAndroid; console.log(` \x1b[36m ____ _ ____ _ @@ -256,6 +257,16 @@ console.log(` \\\\____/|_| |_| |_|_| |_|_|_| \\\\_\\\\___/ \\\\__,_|\\\\__\\\\___| \x1b[0m`); +if (isAndroid) { + console.log(`\x1b[33m[OmniRoute] Running on Android/Termux — headless mode enabled automatically\x1b[0m`); + console.log(`\x1b[33m[OmniRoute] Platform: android/${process.arch} — unsupported features:\x1b[0m + - Codex Responses WebSocket (wreq-js unavailable) + - ChatGPT Web TLS impersonation (tls-client-node unavailable) + - Electron desktop app + - MITM proxy / system certificate install +`); +} + const nodeSupport = getNodeRuntimeSupport(); if (!nodeSupport.nodeCompatible) { const runtimeWarning = getNodeRuntimeWarning() || "Unsupported Node.js runtime detected."; diff --git a/package.json b/package.json index 6f55bf3045c7..bbd3ab0d7f54 100644 --- a/package.json +++ b/package.json @@ -148,18 +148,18 @@ "react-markdown": "^10.1.0", "recharts": "^3.7.0", "selfsigned": "^5.5.0", - "tls-client-node": "^0.1.13", "tsx": "^4.21.0", "undici": "^8.2.0", "uuid": "^14.0.0", - "wreq-js": "^2.0.1", "xxhash-wasm": "^1.1.0", "yazl": "^3.3.1", "zod": "^4.4.3", "zustand": "^5.0.10" }, "optionalDependencies": { - "keytar": "^7.9.0" + "keytar": "^7.9.0", + "tls-client-node": "^0.1.13", + "wreq-js": "^2.0.1" }, "devDependencies": { "@playwright/test": "^1.58.2", diff --git a/scripts/postinstall.mjs b/scripts/postinstall.mjs index a9ef2689df72..9318d4a16da5 100644 --- a/scripts/postinstall.mjs +++ b/scripts/postinstall.mjs @@ -141,10 +141,19 @@ async function fixBetterSqliteBinary() { ? "npm install better-sqlite3 --build-from-source --force" : "npm rebuild better-sqlite3"; + const env = { ...process.env }; + if (isAndroid) { + env.GYP_DEFINES = "android_ndk_path=''"; + } + + const appDir = join(ROOT, "app"); + const rebuildCwd = appDir; + execSync(rebuildCmd, { - cwd: join(ROOT, "app"), + cwd: rebuildCwd, stdio: "inherit", - timeout: 300_000, // 5 minutes for source builds + timeout: isAndroid ? 600_000 : 300_000, // ARM compilation is slower + env, }); process.dlopen({ exports: {} }, appBinary); @@ -153,7 +162,8 @@ async function fixBetterSqliteBinary() { } catch (err) { const isTimeout = err.killed || err.signal === "SIGTERM"; if (isTimeout) { - console.warn(" ⚠️ npm rebuild timed out after 300s."); + const secs = process.platform === "android" ? 600 : 300; + console.warn(` ⚠️ npm rebuild timed out after ${secs}s.`); } else { console.warn(` ⚠️ npm rebuild failed: ${err.message}`); } @@ -189,6 +199,11 @@ async function fixBetterSqliteBinary() { * Fixes: https://github.com/diegosouzapw/OmniRoute/issues/1634 */ async function fixWreqJsBinary() { + if (process.platform === "android") { + console.log(" [postinstall] wreq-js: skipped on android (unsupported platform)"); + return; + } + const appWreqDir = join(ROOT, "app", "node_modules", "wreq-js", "rust"); const rootWreqDir = join(ROOT, "node_modules", "wreq-js", "rust"); diff --git a/scripts/responses-ws-proxy.mjs b/scripts/responses-ws-proxy.mjs index 334840c36fbd..a30a9b649dbb 100644 --- a/scripts/responses-ws-proxy.mjs +++ b/scripts/responses-ws-proxy.mjs @@ -1,6 +1,23 @@ import { createHash, randomUUID } from "node:crypto"; +import { createRequire } from "node:module"; import { STATUS_CODES } from "node:http"; -import { websocket } from "wreq-js"; + +const _wreqRequire = createRequire(import.meta.url); + +let _websocketFn = null; +let _wreqChecked = false; + +function getWebSocketTransport() { + if (_wreqChecked) return _websocketFn; + _wreqChecked = true; + try { + const mod = _wreqRequire("wreq-js"); + _websocketFn = typeof mod.websocket === "function" ? mod.websocket : null; + } catch { + _websocketFn = null; + } + return _websocketFn; +} export const RESPONSES_WS_PUBLIC_PATHS = new Set([ "/responses", @@ -562,7 +579,7 @@ export function createResponsesWsProxy({ baseUrl, bridgeSecret, fetchImpl = fetch, - wsFactory = websocket, + wsFactory = getWebSocketTransport(), pingIntervalMs = 25000, idleTimeoutMs = 90000, maxBufferBytes = DEFAULT_MAX_WS_BUFFER_BYTES, @@ -583,6 +600,21 @@ export function createResponsesWsProxy({ return false; } + if (!wsFactory) { + writeHttpError( + socket, + 503, + JSON.stringify({ + error: { + message: + "Responses WebSocket proxy unavailable: wreq-js is not installed on this platform", + code: "wreq_js_unavailable", + }, + }) + ); + return true; + } + const upgradeHeader = String(req.headers.upgrade || "").toLowerCase(); if (upgradeHeader !== "websocket") { writeHttpError(