smartcontext-proxy 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/PLAN-v2.md +390 -0
  2. package/dist/src/context/ab-test.d.ts +32 -0
  3. package/dist/src/context/ab-test.js +133 -0
  4. package/dist/src/index.js +99 -78
  5. package/dist/src/proxy/classifier.d.ts +14 -0
  6. package/dist/src/proxy/classifier.js +63 -0
  7. package/dist/src/proxy/connect-proxy.d.ts +37 -0
  8. package/dist/src/proxy/connect-proxy.js +234 -0
  9. package/dist/src/proxy/server.js +10 -1
  10. package/dist/src/proxy/tls-interceptor.d.ts +23 -0
  11. package/dist/src/proxy/tls-interceptor.js +211 -0
  12. package/dist/src/proxy/transparent-listener.d.ts +31 -0
  13. package/dist/src/proxy/transparent-listener.js +285 -0
  14. package/dist/src/proxy/tunnel.d.ts +7 -0
  15. package/dist/src/proxy/tunnel.js +33 -0
  16. package/dist/src/system/dns-redirect.d.ts +28 -0
  17. package/dist/src/system/dns-redirect.js +141 -0
  18. package/dist/src/system/installer.d.ts +25 -0
  19. package/dist/src/system/installer.js +180 -0
  20. package/dist/src/system/linux.d.ts +11 -0
  21. package/dist/src/system/linux.js +60 -0
  22. package/dist/src/system/macos.d.ts +24 -0
  23. package/dist/src/system/macos.js +98 -0
  24. package/dist/src/system/pf-redirect.d.ts +25 -0
  25. package/dist/src/system/pf-redirect.js +177 -0
  26. package/dist/src/system/watchdog.d.ts +7 -0
  27. package/dist/src/system/watchdog.js +115 -0
  28. package/dist/src/test/connect-proxy.test.d.ts +1 -0
  29. package/dist/src/test/connect-proxy.test.js +147 -0
  30. package/dist/src/test/dashboard.test.js +1 -0
  31. package/dist/src/tls/ca-manager.d.ts +9 -0
  32. package/dist/src/tls/ca-manager.js +117 -0
  33. package/dist/src/tls/trust-store.d.ts +11 -0
  34. package/dist/src/tls/trust-store.js +121 -0
  35. package/dist/src/tray/bridge.d.ts +8 -0
  36. package/dist/src/tray/bridge.js +66 -0
  37. package/dist/src/ui/dashboard.d.ts +10 -1
  38. package/dist/src/ui/dashboard.js +119 -34
  39. package/dist/src/ui/ws-feed.d.ts +8 -0
  40. package/dist/src/ui/ws-feed.js +30 -0
  41. package/native/macos/SmartContextTray/Package.swift +13 -0
  42. package/native/macos/SmartContextTray/Sources/main.swift +206 -0
  43. package/package.json +6 -2
  44. package/src/context/ab-test.ts +172 -0
  45. package/src/index.ts +104 -74
  46. package/src/proxy/classifier.ts +71 -0
  47. package/src/proxy/connect-proxy.ts +251 -0
  48. package/src/proxy/server.ts +11 -2
  49. package/src/proxy/tls-interceptor.ts +261 -0
  50. package/src/proxy/transparent-listener.ts +328 -0
  51. package/src/proxy/tunnel.ts +32 -0
  52. package/src/system/dns-redirect.ts +144 -0
  53. package/src/system/installer.ts +148 -0
  54. package/src/system/linux.ts +57 -0
  55. package/src/system/macos.ts +89 -0
  56. package/src/system/pf-redirect.ts +175 -0
  57. package/src/system/watchdog.ts +76 -0
  58. package/src/test/connect-proxy.test.ts +170 -0
  59. package/src/test/dashboard.test.ts +1 -0
  60. package/src/tls/ca-manager.ts +140 -0
  61. package/src/tls/trust-store.ts +123 -0
  62. package/src/tray/bridge.ts +61 -0
  63. package/src/ui/dashboard.ts +129 -35
  64. package/src/ui/ws-feed.ts +32 -0
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.cacheRealIPs = cacheRealIPs;
7
+ exports.getRealIP = getRealIP;
8
+ exports.getAllRealIPs = getAllRealIPs;
9
+ exports.enableDNSRedirect = enableDNSRedirect;
10
+ exports.disableDNSRedirect = disableDNSRedirect;
11
+ exports.isDNSRedirectActive = isDNSRedirectActive;
12
+ const node_fs_1 = __importDefault(require("node:fs"));
13
+ const node_child_process_1 = require("node:child_process");
14
+ const node_dns_1 = __importDefault(require("node:dns"));
15
+ const HOSTS_FILE = '/etc/hosts';
16
+ const MARKER_START = '# SmartContext Proxy — START (auto-generated, do not edit)';
17
+ const MARKER_END = '# SmartContext Proxy — END';
18
+ /** LLM provider hostnames to redirect */
19
+ const LLM_HOSTS = [
20
+ 'api.anthropic.com',
21
+ 'api.openai.com',
22
+ 'generativelanguage.googleapis.com',
23
+ 'openrouter.ai',
24
+ 'api.together.xyz',
25
+ 'api.fireworks.ai',
26
+ 'api.mistral.ai',
27
+ 'api.cohere.com',
28
+ 'api.groq.com',
29
+ 'api.deepseek.com',
30
+ ];
31
+ /** Original IP mappings — needed to forward traffic to real providers */
32
+ const originalIPs = new Map();
33
+ /**
34
+ * Resolve and cache real IPs before overriding DNS.
35
+ * MUST be called before enableDNSRedirect.
36
+ */
37
+ async function cacheRealIPs() {
38
+ for (const host of LLM_HOSTS) {
39
+ try {
40
+ const addrs = await new Promise((resolve, reject) => {
41
+ node_dns_1.default.resolve4(host, (err, addresses) => {
42
+ if (err)
43
+ reject(err);
44
+ else
45
+ resolve(addresses);
46
+ });
47
+ });
48
+ originalIPs.set(host, addrs);
49
+ }
50
+ catch { }
51
+ }
52
+ return originalIPs;
53
+ }
54
+ /** Get cached real IP for a hostname */
55
+ function getRealIP(hostname) {
56
+ const ips = originalIPs.get(hostname);
57
+ return ips?.[0] || null;
58
+ }
59
+ /** Get all original IP mappings */
60
+ function getAllRealIPs() {
61
+ return originalIPs;
62
+ }
63
+ /**
64
+ * Add /etc/hosts entries pointing LLM hostnames to 127.0.0.1.
65
+ * Requires sudo.
66
+ * After this, all apps resolve LLM hosts to localhost → our proxy.
67
+ */
68
+ function enableDNSRedirect() {
69
+ try {
70
+ const current = node_fs_1.default.readFileSync(HOSTS_FILE, 'utf-8');
71
+ // Don't add if already present
72
+ if (current.includes(MARKER_START)) {
73
+ return { success: true, message: 'DNS redirect already active', hosts: LLM_HOSTS.length };
74
+ }
75
+ const block = [
76
+ '',
77
+ MARKER_START,
78
+ ...LLM_HOSTS.map((h) => `127.0.0.1 ${h}`),
79
+ MARKER_END,
80
+ '',
81
+ ].join('\n');
82
+ const newContent = current + block;
83
+ // Write via sudo tee
84
+ (0, node_child_process_1.execFileSync)('sudo', ['tee', HOSTS_FILE], {
85
+ input: newContent,
86
+ stdio: ['pipe', 'pipe', 'pipe'],
87
+ });
88
+ // Flush DNS cache
89
+ flushDNS();
90
+ return { success: true, message: `DNS redirect active: ${LLM_HOSTS.length} hosts → 127.0.0.1`, hosts: LLM_HOSTS.length };
91
+ }
92
+ catch (err) {
93
+ return { success: false, message: `Failed: ${err}`, hosts: 0 };
94
+ }
95
+ }
96
+ /**
97
+ * Remove /etc/hosts entries.
98
+ */
99
+ function disableDNSRedirect() {
100
+ try {
101
+ const current = node_fs_1.default.readFileSync(HOSTS_FILE, 'utf-8');
102
+ if (!current.includes(MARKER_START)) {
103
+ return { success: true, message: 'DNS redirect not active' };
104
+ }
105
+ // Remove our block
106
+ const regex = new RegExp(`\\n?${escapeRegex(MARKER_START)}[\\s\\S]*?${escapeRegex(MARKER_END)}\\n?`, 'g');
107
+ const cleaned = current.replace(regex, '\n');
108
+ (0, node_child_process_1.execFileSync)('sudo', ['tee', HOSTS_FILE], {
109
+ input: cleaned,
110
+ stdio: ['pipe', 'pipe', 'pipe'],
111
+ });
112
+ flushDNS();
113
+ return { success: true, message: 'DNS redirect removed' };
114
+ }
115
+ catch (err) {
116
+ return { success: false, message: `Failed: ${err}` };
117
+ }
118
+ }
119
+ /** Check if DNS redirect is active */
120
+ function isDNSRedirectActive() {
121
+ try {
122
+ const content = node_fs_1.default.readFileSync(HOSTS_FILE, 'utf-8');
123
+ return content.includes(MARKER_START);
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ }
129
+ function flushDNS() {
130
+ try {
131
+ if (process.platform === 'darwin') {
132
+ (0, node_child_process_1.execFileSync)('dscacheutil', ['-flushcache'], { stdio: 'pipe' });
133
+ (0, node_child_process_1.execFileSync)('sudo', ['killall', '-HUP', 'mDNSResponder'], { stdio: 'pipe' });
134
+ }
135
+ }
136
+ catch { }
137
+ }
138
+ function escapeRegex(s) {
139
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
140
+ }
141
+ //# sourceMappingURL=dns-redirect.js.map
@@ -0,0 +1,25 @@
1
+ export interface InstallResult {
2
+ success: boolean;
3
+ steps: Array<{
4
+ step: string;
5
+ success: boolean;
6
+ message: string;
7
+ }>;
8
+ }
9
+ /**
10
+ * Full installation:
11
+ * 1. Generate CA cert
12
+ * 2. Install CA in trust store
13
+ * 3. Configure system proxy
14
+ * 4. Install system service
15
+ */
16
+ export declare function install(port: number): InstallResult;
17
+ /**
18
+ * Full uninstallation:
19
+ * 1. Remove system service
20
+ * 2. Clear system proxy
21
+ * 3. Remove CA from trust store
22
+ */
23
+ export declare function uninstall(purge?: boolean): InstallResult;
24
+ /** Check installation status */
25
+ export declare function status(port: number): Record<string, boolean | string>;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.install = install;
37
+ exports.uninstall = uninstall;
38
+ exports.status = status;
39
+ const ca_manager_js_1 = require("../tls/ca-manager.js");
40
+ const trust_store_js_1 = require("../tls/trust-store.js");
41
+ const service_js_1 = require("../daemon/service.js");
42
+ const macos = __importStar(require("./macos.js"));
43
+ const linux = __importStar(require("./linux.js"));
44
+ const platform = process.platform === 'darwin' ? macos : linux;
45
+ /**
46
+ * Full installation:
47
+ * 1. Generate CA cert
48
+ * 2. Install CA in trust store
49
+ * 3. Configure system proxy
50
+ * 4. Install system service
51
+ */
52
+ function install(port) {
53
+ const steps = [];
54
+ let allOk = true;
55
+ // Step 1: Generate CA
56
+ try {
57
+ (0, ca_manager_js_1.ensureCA)();
58
+ steps.push({ step: 'Generate CA certificate', success: true, message: `CA cert at ${(0, ca_manager_js_1.getCACertPath)()}` });
59
+ }
60
+ catch (err) {
61
+ steps.push({ step: 'Generate CA certificate', success: false, message: String(err) });
62
+ allOk = false;
63
+ }
64
+ // Step 2: Install CA in trust store
65
+ if (allOk) {
66
+ const result = (0, trust_store_js_1.installCA)();
67
+ steps.push({ step: 'Install CA in trust store', success: result.success, message: result.message });
68
+ if (!result.success)
69
+ allOk = false;
70
+ }
71
+ // Step 3: Configure system proxy (PAC URL for selective proxying)
72
+ if (allOk) {
73
+ if (process.platform === 'darwin') {
74
+ const result = macos.setAutoproxyURL(`http://127.0.0.1:${port}/proxy.pac`);
75
+ steps.push({ step: 'Configure system proxy (PAC)', success: result.success, message: result.message });
76
+ if (!result.success)
77
+ allOk = false;
78
+ }
79
+ else {
80
+ const result = platform.setSystemProxy('127.0.0.1', port);
81
+ steps.push({ step: 'Configure system proxy', success: result.success, message: result.message });
82
+ if (!result.success)
83
+ allOk = false;
84
+ }
85
+ }
86
+ // Step 4: Install system service
87
+ if (allOk) {
88
+ try {
89
+ const servicePath = (0, service_js_1.installService)(port);
90
+ steps.push({ step: 'Install system service', success: true, message: `Service at ${servicePath}` });
91
+ }
92
+ catch (err) {
93
+ steps.push({ step: 'Install system service', success: false, message: String(err) });
94
+ // Non-fatal: proxy can still run manually
95
+ }
96
+ }
97
+ // Rollback on failure
98
+ if (!allOk) {
99
+ rollback(steps);
100
+ }
101
+ return { success: allOk, steps };
102
+ }
103
+ /**
104
+ * Full uninstallation:
105
+ * 1. Remove system service
106
+ * 2. Clear system proxy
107
+ * 3. Remove CA from trust store
108
+ */
109
+ function uninstall(purge = false) {
110
+ const steps = [];
111
+ // Step 1: Remove service
112
+ try {
113
+ const msg = (0, service_js_1.uninstallService)();
114
+ steps.push({ step: 'Remove system service', success: true, message: msg });
115
+ }
116
+ catch (err) {
117
+ steps.push({ step: 'Remove system service', success: false, message: String(err) });
118
+ }
119
+ // Step 2: Clear proxy
120
+ if (process.platform === 'darwin') {
121
+ const r1 = macos.clearAutoproxyURL();
122
+ steps.push({ step: 'Clear auto-proxy', success: r1.success, message: r1.message });
123
+ const r2 = macos.clearSystemProxy();
124
+ steps.push({ step: 'Clear system proxy', success: r2.success, message: r2.message });
125
+ }
126
+ else {
127
+ const r = linux.clearSystemProxy();
128
+ steps.push({ step: 'Clear system proxy', success: r.success, message: r.message });
129
+ }
130
+ // Step 3: Remove CA
131
+ const caResult = (0, trust_store_js_1.uninstallCA)();
132
+ steps.push({ step: 'Remove CA from trust store', success: caResult.success, message: caResult.message });
133
+ // Step 4: Purge data (optional)
134
+ if (purge) {
135
+ try {
136
+ const fs = require('node:fs');
137
+ const path = require('node:path');
138
+ const dataDir = path.join(process.env['HOME'] || '.', '.smartcontext');
139
+ fs.rmSync(dataDir, { recursive: true, force: true });
140
+ steps.push({ step: 'Purge data directory', success: true, message: `Removed ${dataDir}` });
141
+ }
142
+ catch (err) {
143
+ steps.push({ step: 'Purge data directory', success: false, message: String(err) });
144
+ }
145
+ }
146
+ const allOk = steps.every((s) => s.success);
147
+ return { success: allOk, steps };
148
+ }
149
+ /** Check installation status */
150
+ function status(port) {
151
+ return {
152
+ caExists: require('node:fs').existsSync((0, ca_manager_js_1.getCACertPath)()),
153
+ caInstalled: (0, trust_store_js_1.isCAInstalled)(),
154
+ proxyConfigured: platform.isProxyConfigured(port),
155
+ };
156
+ }
157
+ /** Rollback completed steps on failure */
158
+ function rollback(completedSteps) {
159
+ for (const step of completedSteps.reverse()) {
160
+ if (!step.success)
161
+ continue;
162
+ try {
163
+ if (step.step.includes('CA in trust store'))
164
+ (0, trust_store_js_1.uninstallCA)();
165
+ if (step.step.includes('system proxy') || step.step.includes('PAC')) {
166
+ if (process.platform === 'darwin') {
167
+ macos.clearAutoproxyURL();
168
+ macos.clearSystemProxy();
169
+ }
170
+ else {
171
+ linux.clearSystemProxy();
172
+ }
173
+ }
174
+ if (step.step.includes('system service'))
175
+ (0, service_js_1.uninstallService)();
176
+ }
177
+ catch { }
178
+ }
179
+ }
180
+ //# sourceMappingURL=installer.js.map
@@ -0,0 +1,11 @@
1
+ /** Configure Linux system proxy */
2
+ export declare function setSystemProxy(host: string, port: number): {
3
+ success: boolean;
4
+ message: string;
5
+ };
6
+ /** Clear Linux system proxy */
7
+ export declare function clearSystemProxy(): {
8
+ success: boolean;
9
+ message: string;
10
+ };
11
+ export declare function isProxyConfigured(port: number): boolean;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setSystemProxy = setSystemProxy;
4
+ exports.clearSystemProxy = clearSystemProxy;
5
+ exports.isProxyConfigured = isProxyConfigured;
6
+ const node_child_process_1 = require("node:child_process");
7
+ /** Configure Linux system proxy */
8
+ function setSystemProxy(host, port) {
9
+ const proxyUrl = `http://${host}:${port}`;
10
+ try {
11
+ // Try GNOME settings
12
+ try {
13
+ (0, node_child_process_1.execFileSync)('gsettings', ['set', 'org.gnome.system.proxy', 'mode', "'manual'"], { stdio: 'pipe' });
14
+ (0, node_child_process_1.execFileSync)('gsettings', ['set', 'org.gnome.system.proxy.http', 'host', `'${host}'`], { stdio: 'pipe' });
15
+ (0, node_child_process_1.execFileSync)('gsettings', ['set', 'org.gnome.system.proxy.http', 'port', String(port)], { stdio: 'pipe' });
16
+ (0, node_child_process_1.execFileSync)('gsettings', ['set', 'org.gnome.system.proxy.https', 'host', `'${host}'`], { stdio: 'pipe' });
17
+ (0, node_child_process_1.execFileSync)('gsettings', ['set', 'org.gnome.system.proxy.https', 'port', String(port)], { stdio: 'pipe' });
18
+ }
19
+ catch { }
20
+ // Write environment file for shell sessions
21
+ const envFile = '/etc/profile.d/smartcontext-proxy.sh';
22
+ const content = `# SmartContext Proxy - auto-generated
23
+ export http_proxy="${proxyUrl}"
24
+ export https_proxy="${proxyUrl}"
25
+ export HTTP_PROXY="${proxyUrl}"
26
+ export HTTPS_PROXY="${proxyUrl}"
27
+ export no_proxy="localhost,127.0.0.1,::1"
28
+ `;
29
+ try {
30
+ (0, node_child_process_1.execFileSync)('sudo', ['tee', envFile], { input: content, stdio: ['pipe', 'pipe', 'pipe'] });
31
+ }
32
+ catch { }
33
+ return { success: true, message: `System proxy set to ${proxyUrl}` };
34
+ }
35
+ catch (err) {
36
+ return { success: false, message: `Failed: ${err}` };
37
+ }
38
+ }
39
+ /** Clear Linux system proxy */
40
+ function clearSystemProxy() {
41
+ try {
42
+ try {
43
+ (0, node_child_process_1.execFileSync)('gsettings', ['set', 'org.gnome.system.proxy', 'mode', "'none'"], { stdio: 'pipe' });
44
+ }
45
+ catch { }
46
+ try {
47
+ (0, node_child_process_1.execFileSync)('sudo', ['rm', '-f', '/etc/profile.d/smartcontext-proxy.sh'], { stdio: 'pipe' });
48
+ }
49
+ catch { }
50
+ return { success: true, message: 'System proxy cleared' };
51
+ }
52
+ catch (err) {
53
+ return { success: false, message: `Failed: ${err}` };
54
+ }
55
+ }
56
+ function isProxyConfigured(port) {
57
+ const proxyEnv = process.env['https_proxy'] || process.env['HTTPS_PROXY'] || '';
58
+ return proxyEnv.includes(String(port));
59
+ }
60
+ //# sourceMappingURL=linux.js.map
@@ -0,0 +1,24 @@
1
+ /** Get active network interface name (e.g., "Wi-Fi", "Ethernet") */
2
+ export declare function getActiveInterface(): string;
3
+ /** Configure macOS system proxy to use SmartContext */
4
+ export declare function setSystemProxy(host: string, port: number): {
5
+ success: boolean;
6
+ message: string;
7
+ };
8
+ /** Remove system proxy configuration */
9
+ export declare function clearSystemProxy(): {
10
+ success: boolean;
11
+ message: string;
12
+ };
13
+ /** Set PAC URL as auto-proxy configuration */
14
+ export declare function setAutoproxyURL(pacUrl: string): {
15
+ success: boolean;
16
+ message: string;
17
+ };
18
+ /** Clear auto-proxy configuration */
19
+ export declare function clearAutoproxyURL(): {
20
+ success: boolean;
21
+ message: string;
22
+ };
23
+ /** Check if system proxy is currently set to SmartContext */
24
+ export declare function isProxyConfigured(port: number): boolean;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getActiveInterface = getActiveInterface;
4
+ exports.setSystemProxy = setSystemProxy;
5
+ exports.clearSystemProxy = clearSystemProxy;
6
+ exports.setAutoproxyURL = setAutoproxyURL;
7
+ exports.clearAutoproxyURL = clearAutoproxyURL;
8
+ exports.isProxyConfigured = isProxyConfigured;
9
+ const node_child_process_1 = require("node:child_process");
10
+ /** Get active network interface name (e.g., "Wi-Fi", "Ethernet") */
11
+ function getActiveInterface() {
12
+ try {
13
+ const route = (0, node_child_process_1.execFileSync)('route', ['-n', 'get', 'default'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
14
+ const ifMatch = route.match(/interface:\s*(\S+)/);
15
+ if (!ifMatch)
16
+ return 'Wi-Fi';
17
+ const ifName = ifMatch[1];
18
+ // Map interface ID to service name
19
+ const services = (0, node_child_process_1.execFileSync)('networksetup', ['-listallhardwareports'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
20
+ const lines = services.split('\n');
21
+ for (let i = 0; i < lines.length; i++) {
22
+ if (lines[i].includes(`Device: ${ifName}`)) {
23
+ const nameLine = lines[i - 1];
24
+ const nameMatch = nameLine?.match(/Hardware Port:\s*(.+)/);
25
+ if (nameMatch)
26
+ return nameMatch[1];
27
+ }
28
+ }
29
+ return 'Wi-Fi';
30
+ }
31
+ catch {
32
+ return 'Wi-Fi';
33
+ }
34
+ }
35
+ /** Configure macOS system proxy to use SmartContext */
36
+ function setSystemProxy(host, port) {
37
+ const iface = getActiveInterface();
38
+ try {
39
+ // Set HTTPS proxy
40
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setsecurewebproxy', iface, host, String(port)], { stdio: 'pipe' });
41
+ // Set HTTP proxy
42
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setwebproxy', iface, host, String(port)], { stdio: 'pipe' });
43
+ // Enable them
44
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setsecurewebproxystate', iface, 'on'], { stdio: 'pipe' });
45
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setwebproxystate', iface, 'on'], { stdio: 'pipe' });
46
+ return { success: true, message: `System proxy set to ${host}:${port} on ${iface}` };
47
+ }
48
+ catch (err) {
49
+ return { success: false, message: `Failed to set proxy on ${iface}: ${err}` };
50
+ }
51
+ }
52
+ /** Remove system proxy configuration */
53
+ function clearSystemProxy() {
54
+ const iface = getActiveInterface();
55
+ try {
56
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setsecurewebproxystate', iface, 'off'], { stdio: 'pipe' });
57
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setwebproxystate', iface, 'off'], { stdio: 'pipe' });
58
+ return { success: true, message: `System proxy cleared on ${iface}` };
59
+ }
60
+ catch (err) {
61
+ return { success: false, message: `Failed to clear proxy on ${iface}: ${err}` };
62
+ }
63
+ }
64
+ /** Set PAC URL as auto-proxy configuration */
65
+ function setAutoproxyURL(pacUrl) {
66
+ const iface = getActiveInterface();
67
+ try {
68
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setautoproxyurl', iface, pacUrl], { stdio: 'pipe' });
69
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setautoproxystate', iface, 'on'], { stdio: 'pipe' });
70
+ return { success: true, message: `Auto-proxy URL set to ${pacUrl} on ${iface}` };
71
+ }
72
+ catch (err) {
73
+ return { success: false, message: `Failed: ${err}` };
74
+ }
75
+ }
76
+ /** Clear auto-proxy configuration */
77
+ function clearAutoproxyURL() {
78
+ const iface = getActiveInterface();
79
+ try {
80
+ (0, node_child_process_1.execFileSync)('networksetup', ['-setautoproxystate', iface, 'off'], { stdio: 'pipe' });
81
+ return { success: true, message: `Auto-proxy cleared on ${iface}` };
82
+ }
83
+ catch (err) {
84
+ return { success: false, message: `Failed: ${err}` };
85
+ }
86
+ }
87
+ /** Check if system proxy is currently set to SmartContext */
88
+ function isProxyConfigured(port) {
89
+ const iface = getActiveInterface();
90
+ try {
91
+ const info = (0, node_child_process_1.execFileSync)('networksetup', ['-getsecurewebproxy', iface], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
92
+ return info.includes(`Port: ${port}`) && info.includes('Enabled: Yes');
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ }
98
+ //# sourceMappingURL=macos.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Install pf redirect rules.
3
+ * Requires sudo.
4
+ */
5
+ export declare function enablePFRedirect(proxyPort: number): Promise<{
6
+ success: boolean;
7
+ message: string;
8
+ ips: number;
9
+ }>;
10
+ /**
11
+ * Remove pf redirect rules.
12
+ */
13
+ export declare function disablePFRedirect(): {
14
+ success: boolean;
15
+ message: string;
16
+ };
17
+ /**
18
+ * Check if pf redirect is active.
19
+ */
20
+ export declare function isPFRedirectActive(): boolean;
21
+ /**
22
+ * Refresh IP addresses (IPs can change due to DNS).
23
+ * Call periodically to keep rules current.
24
+ */
25
+ export declare function refreshPFRules(proxyPort: number): Promise<void>;