()
| 2857 | } |
| 2858 | |
| 2859 | export async function start() { |
| 2860 | // Clear old log files |
| 2861 | safeUnlink(CONSOLE_LOG_PATH); |
| 2862 | safeUnlink(NETWORK_LOG_PATH); |
| 2863 | safeUnlink(DIALOG_LOG_PATH); |
| 2864 | |
| 2865 | const port = await findPort(); |
| 2866 | LOCAL_LISTEN_PORT = port; |
| 2867 | |
| 2868 | // ─── Proxy config (D8 + codex F5) ────────────────────────────── |
| 2869 | // BROWSE_PROXY_URL is set by the CLI when --proxy was passed. For SOCKS5 |
| 2870 | // with auth, we run a local 127.0.0.1 bridge that relays to the |
| 2871 | // authenticated upstream (Chromium can't do SOCKS5 auth itself). For |
| 2872 | // HTTP/HTTPS or unauthenticated SOCKS5, we pass the URL directly to |
| 2873 | // Chromium's proxy.server option. |
| 2874 | let proxyBridge: BridgeHandle | null = null; |
| 2875 | const proxyUrl = process.env.BROWSE_PROXY_URL; |
| 2876 | if (proxyUrl) { |
| 2877 | let parsed; |
| 2878 | try { |
| 2879 | parsed = parseProxyConfig({ |
| 2880 | proxyUrl, |
| 2881 | envUser: process.env.BROWSE_PROXY_USER, |
| 2882 | envPass: process.env.BROWSE_PROXY_PASS, |
| 2883 | }); |
| 2884 | } catch (err) { |
| 2885 | if (err instanceof ProxyConfigError) { |
| 2886 | console.error(`[browse] error: ${err.message} (${err.hint})`); |
| 2887 | process.exit(1); |
| 2888 | } |
| 2889 | throw err; |
| 2890 | } |
| 2891 | |
| 2892 | if (parsed.scheme === 'socks5' && parsed.hasAuth) { |
| 2893 | // Pre-flight: verify upstream accepts our creds before launching |
| 2894 | // Chromium. 5s budget, 3 retries with 500ms backoff (D4: handles VPN |
| 2895 | // warm-up race). On failure, exit with redacted error. |
| 2896 | console.log(`[browse] Testing SOCKS5 upstream ${redactProxyUrl(proxyUrl)}...`); |
| 2897 | try { |
| 2898 | const test = await testUpstream({ |
| 2899 | upstream: toUpstreamConfig(parsed), |
| 2900 | budgetMs: 5000, |
| 2901 | retries: 3, |
| 2902 | backoffMs: 500, |
| 2903 | }); |
| 2904 | console.log(`[browse] [proxy] upstream test ok in ${test.ms}ms (${test.attempts} attempt${test.attempts === 1 ? '' : 's'})`); |
| 2905 | } catch (err) { |
| 2906 | const msg = err instanceof Error ? err.message : String(err); |
| 2907 | console.error(`[browse] [proxy] FAIL upstream ${redactProxyUrl(proxyUrl)}: ${msg}`); |
| 2908 | process.exit(1); |
| 2909 | } |
| 2910 | |
| 2911 | proxyBridge = await startSocksBridge({ upstream: toUpstreamConfig(parsed) }); |
| 2912 | console.log(`[browse] [proxy] bridge listening on 127.0.0.1:${proxyBridge.port}`); |
| 2913 | browserManager.setProxyConfig({ server: `socks5://127.0.0.1:${proxyBridge.port}` }); |
| 2914 | } else { |
| 2915 | // HTTP/HTTPS or unauth SOCKS5 — pass through to Chromium directly. |
| 2916 | browserManager.setProxyConfig({ |
no test coverage detected