( opts: SystemProxyControllerOptions, )
| 32 | * for the whole machine — callers MUST disable() on quit. |
| 33 | */ |
| 34 | export function createSystemProxyController( |
| 35 | opts: SystemProxyControllerOptions, |
| 36 | ): SystemProxyController { |
| 37 | const { host, port } = opts |
| 38 | const platform = opts.platform ?? process.platform |
| 39 | const exec = opts.exec ?? defaultExec |
| 40 | |
| 41 | // Last applied bypass list, surfaced via describe(). |
| 42 | let lastBypass: string[] = [] |
| 43 | |
| 44 | const describe = () => ({ port, bypass: lastBypass }) |
| 45 | |
| 46 | // ---- macOS (networksetup) ---- |
| 47 | |
| 48 | // List enabled network services, skipping the informational header line and |
| 49 | // any '*'-prefixed (disabled) service. |
| 50 | async function darwinEnabledServices(): Promise<string[]> { |
| 51 | const { stdout } = await exec('networksetup -listallnetworkservices') |
| 52 | return stdout |
| 53 | .split('\n') |
| 54 | .slice(1) // drop the "An asterisk (*) denotes..." header |
| 55 | .map((l) => l.trim()) |
| 56 | .filter((l) => l.length > 0 && !l.startsWith('*')) |
| 57 | } |
| 58 | |
| 59 | async function darwinEnable(bypass: string[]): Promise<void> { |
| 60 | const services = await darwinEnabledServices() |
| 61 | for (const svc of services) { |
| 62 | await exec(`networksetup -setwebproxy "${svc}" ${host} ${port}`) |
| 63 | await exec(`networksetup -setsecurewebproxy "${svc}" ${host} ${port}`) |
| 64 | await exec(`networksetup -setsocksfirewallproxy "${svc}" ${host} ${port}`) |
| 65 | await exec(`networksetup -setwebproxystate "${svc}" on`) |
| 66 | await exec(`networksetup -setsecurewebproxystate "${svc}" on`) |
| 67 | await exec(`networksetup -setsocksfirewallproxystate "${svc}" on`) |
| 68 | if (bypass.length > 0) { |
| 69 | await exec( |
| 70 | `networksetup -setproxybypassdomains "${svc}" ${bypass.join(' ')}`, |
| 71 | ) |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | async function darwinDisable(): Promise<void> { |
| 77 | const services = await darwinEnabledServices() |
| 78 | for (const svc of services) { |
| 79 | await exec(`networksetup -setwebproxystate "${svc}" off`) |
| 80 | await exec(`networksetup -setsecurewebproxystate "${svc}" off`) |
| 81 | await exec(`networksetup -setsocksfirewallproxystate "${svc}" off`) |
| 82 | // Anti-lockout: also clear any PAC/auto-config so quit/disable can never |
| 83 | // leave the machine pointing at a dead loopback PAC URL. |
| 84 | await exec(`networksetup -setautoproxystate "${svc}" off`) |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | async function darwinSetAutoProxy(url: string): Promise<void> { |
| 89 | const services = await darwinEnabledServices() |
| 90 | for (const svc of services) { |
| 91 | await exec(`networksetup -setautoproxyurl "${svc}" ${url}`) |
no outgoing calls
no test coverage detected