green-tunnel 2.0.2 → 3.0.3

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 (156) hide show
  1. package/dist/core/config.d.ts +13 -0
  2. package/dist/core/config.d.ts.map +1 -0
  3. package/dist/core/config.js +87 -0
  4. package/dist/core/config.js.map +1 -0
  5. package/dist/core/dns/decode.d.ts +12 -0
  6. package/dist/core/dns/decode.d.ts.map +1 -0
  7. package/dist/core/dns/decode.js +23 -0
  8. package/dist/core/dns/decode.js.map +1 -0
  9. package/dist/core/dns/doh.d.ts +19 -0
  10. package/dist/core/dns/doh.d.ts.map +1 -0
  11. package/dist/core/dns/doh.js +54 -0
  12. package/dist/core/dns/doh.js.map +1 -0
  13. package/dist/core/dns/dot.d.ts +13 -0
  14. package/dist/core/dns/dot.d.ts.map +1 -0
  15. package/dist/core/dns/dot.js +79 -0
  16. package/dist/core/dns/dot.js.map +1 -0
  17. package/dist/core/dns/index.d.ts +8 -0
  18. package/dist/core/dns/index.d.ts.map +1 -0
  19. package/dist/core/dns/index.js +18 -0
  20. package/dist/core/dns/index.js.map +1 -0
  21. package/dist/core/dns/plain.d.ts +17 -0
  22. package/dist/core/dns/plain.d.ts.map +1 -0
  23. package/dist/core/dns/plain.js +45 -0
  24. package/dist/core/dns/plain.js.map +1 -0
  25. package/dist/core/dns/resolver.d.ts +27 -0
  26. package/dist/core/dns/resolver.d.ts.map +1 -0
  27. package/dist/core/dns/resolver.js +81 -0
  28. package/dist/core/dns/resolver.js.map +1 -0
  29. package/dist/core/errors.d.ts +32 -0
  30. package/dist/core/errors.d.ts.map +1 -0
  31. package/dist/core/errors.js +37 -0
  32. package/dist/core/errors.js.map +1 -0
  33. package/dist/core/http/head.d.ts +40 -0
  34. package/dist/core/http/head.d.ts.map +1 -0
  35. package/dist/core/http/head.js +122 -0
  36. package/dist/core/http/head.js.map +1 -0
  37. package/dist/core/http/rewriter.d.ts +20 -0
  38. package/dist/core/http/rewriter.d.ts.map +1 -0
  39. package/dist/core/http/rewriter.js +184 -0
  40. package/dist/core/http/rewriter.js.map +1 -0
  41. package/dist/core/index.d.ts +28 -0
  42. package/dist/core/index.d.ts.map +1 -0
  43. package/dist/core/index.js +27 -0
  44. package/dist/core/index.js.map +1 -0
  45. package/dist/core/logger.d.ts +38 -0
  46. package/dist/core/logger.d.ts.map +1 -0
  47. package/dist/core/logger.js +92 -0
  48. package/dist/core/logger.js.map +1 -0
  49. package/dist/core/net/socket.d.ts +30 -0
  50. package/dist/core/net/socket.d.ts.map +1 -0
  51. package/dist/core/net/socket.js +98 -0
  52. package/dist/core/net/socket.js.map +1 -0
  53. package/dist/core/proxy/connect-tunnel.d.ts +14 -0
  54. package/dist/core/proxy/connect-tunnel.d.ts.map +1 -0
  55. package/dist/core/proxy/connect-tunnel.js +44 -0
  56. package/dist/core/proxy/connect-tunnel.js.map +1 -0
  57. package/dist/core/proxy/connection.d.ts +12 -0
  58. package/dist/core/proxy/connection.d.ts.map +1 -0
  59. package/dist/core/proxy/connection.js +88 -0
  60. package/dist/core/proxy/connection.js.map +1 -0
  61. package/dist/core/proxy/context.d.ts +15 -0
  62. package/dist/core/proxy/context.d.ts.map +1 -0
  63. package/dist/core/proxy/context.js +2 -0
  64. package/dist/core/proxy/context.js.map +1 -0
  65. package/dist/core/proxy/index.d.ts +33 -0
  66. package/dist/core/proxy/index.d.ts.map +1 -0
  67. package/dist/core/proxy/index.js +162 -0
  68. package/dist/core/proxy/index.js.map +1 -0
  69. package/dist/core/proxy/pipe.d.ts +12 -0
  70. package/dist/core/proxy/pipe.d.ts.map +1 -0
  71. package/dist/core/proxy/pipe.js +42 -0
  72. package/dist/core/proxy/pipe.js.map +1 -0
  73. package/dist/core/proxy/plain-tunnel.d.ts +12 -0
  74. package/dist/core/proxy/plain-tunnel.d.ts.map +1 -0
  75. package/dist/core/proxy/plain-tunnel.js +29 -0
  76. package/dist/core/proxy/plain-tunnel.js.map +1 -0
  77. package/dist/core/system-proxy/darwin.d.ts +32 -0
  78. package/dist/core/system-proxy/darwin.d.ts.map +1 -0
  79. package/dist/core/system-proxy/darwin.js +146 -0
  80. package/dist/core/system-proxy/darwin.js.map +1 -0
  81. package/dist/core/system-proxy/driver.d.ts +21 -0
  82. package/dist/core/system-proxy/driver.d.ts.map +1 -0
  83. package/dist/core/system-proxy/driver.js +2 -0
  84. package/dist/core/system-proxy/driver.js.map +1 -0
  85. package/dist/core/system-proxy/exec.d.ts +21 -0
  86. package/dist/core/system-proxy/exec.d.ts.map +1 -0
  87. package/dist/core/system-proxy/exec.js +62 -0
  88. package/dist/core/system-proxy/exec.js.map +1 -0
  89. package/dist/core/system-proxy/index.d.ts +45 -0
  90. package/dist/core/system-proxy/index.d.ts.map +1 -0
  91. package/dist/core/system-proxy/index.js +151 -0
  92. package/dist/core/system-proxy/index.js.map +1 -0
  93. package/dist/core/system-proxy/linux.d.ts +18 -0
  94. package/dist/core/system-proxy/linux.d.ts.map +1 -0
  95. package/dist/core/system-proxy/linux.js +100 -0
  96. package/dist/core/system-proxy/linux.js.map +1 -0
  97. package/dist/core/system-proxy/recovery.d.ts +23 -0
  98. package/dist/core/system-proxy/recovery.d.ts.map +1 -0
  99. package/dist/core/system-proxy/recovery.js +147 -0
  100. package/dist/core/system-proxy/recovery.js.map +1 -0
  101. package/dist/core/system-proxy/windows.d.ts +15 -0
  102. package/dist/core/system-proxy/windows.d.ts.map +1 -0
  103. package/dist/core/system-proxy/windows.js +96 -0
  104. package/dist/core/system-proxy/windows.js.map +1 -0
  105. package/dist/core/tls/fragment.d.ts +28 -0
  106. package/dist/core/tls/fragment.d.ts.map +1 -0
  107. package/dist/core/tls/fragment.js +81 -0
  108. package/dist/core/tls/fragment.js.map +1 -0
  109. package/dist/core/types.d.ts +138 -0
  110. package/dist/core/types.d.ts.map +1 -0
  111. package/dist/core/types.js +7 -0
  112. package/dist/core/types.js.map +1 -0
  113. package/dist/index.d.ts +11 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +11 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/main.d.ts +3 -0
  118. package/dist/main.d.ts.map +1 -0
  119. package/dist/main.js +155 -0
  120. package/dist/main.js.map +1 -0
  121. package/dist/options.d.ts +25 -0
  122. package/dist/options.d.ts.map +1 -0
  123. package/dist/options.js +144 -0
  124. package/dist/options.js.map +1 -0
  125. package/dist/ui.d.ts +17 -0
  126. package/dist/ui.d.ts.map +1 -0
  127. package/dist/ui.js +89 -0
  128. package/dist/ui.js.map +1 -0
  129. package/dist/version.d.ts +3 -0
  130. package/dist/version.d.ts.map +1 -0
  131. package/dist/version.js +13 -0
  132. package/dist/version.js.map +1 -0
  133. package/package.json +31 -30
  134. package/LICENSE +0 -21
  135. package/README.md +0 -172
  136. package/bin/gt.js +0 -170
  137. package/src/config.js +0 -15
  138. package/src/dns/base.js +0 -47
  139. package/src/dns/https.js +0 -30
  140. package/src/dns/tls.js +0 -15
  141. package/src/dns/unencrypted.js +0 -25
  142. package/src/handlers/http.js +0 -65
  143. package/src/handlers/https.js +0 -78
  144. package/src/handlers/request.js +0 -32
  145. package/src/http/request.js +0 -45
  146. package/src/http/response.js +0 -45
  147. package/src/http/utils.js +0 -86
  148. package/src/index.cjs +0 -7
  149. package/src/index.js +0 -4
  150. package/src/logger.js +0 -11
  151. package/src/proxy.js +0 -74
  152. package/src/scripts/windows/wininet-reset-settings.ps1 +0 -14
  153. package/src/utils/analytics.js +0 -59
  154. package/src/utils/buffer.js +0 -35
  155. package/src/utils/socket.js +0 -32
  156. package/src/utils/system-proxy.js +0 -153
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-tunnel.d.ts","sourceRoot":"","sources":["../../../src/core/proxy/plain-tunnel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAIhC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,GAAG,CAAC,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACtC,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,IAAI,CAAC,CAuBf"}
@@ -0,0 +1,29 @@
1
+ import { connect } from '../net/socket.js';
2
+ import { createRequestRewriteStream } from '../http/rewriter.js';
3
+ import { tunnel } from './pipe.js';
4
+ /**
5
+ * Plain-HTTP proxying. No fragmentation happens here — there is no SNI to hide,
6
+ * and the Host header is in cleartext regardless. Its only job is to relay the
7
+ * stream while rewriting absolute-form request targets into origin-form.
8
+ */
9
+ export async function openPlainTunnel(client, initial, target, ctx) {
10
+ const { settings, dns } = ctx;
11
+ const address = await dns.resolve(target.host);
12
+ const server = await connect({
13
+ host: address,
14
+ port: target.port,
15
+ timeoutMs: settings.connectTimeoutMs,
16
+ signal: ctx.signal,
17
+ });
18
+ ctx.onOpen({
19
+ id: ctx.nextTunnelId(),
20
+ kind: 'http',
21
+ host: target.host,
22
+ port: target.port,
23
+ startedAt: Date.now(),
24
+ });
25
+ // Put the bytes we already consumed back so the rewriter sees the full stream.
26
+ client.unshift(initial);
27
+ await tunnel(client, server, settings.idleTimeoutMs, createRequestRewriteStream());
28
+ }
29
+ //# sourceMappingURL=plain-tunnel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-tunnel.js","sourceRoot":"","sources":["../../../src/core/proxy/plain-tunnel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGnC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAkB,EAClB,OAAe,EACf,MAAsC,EACtC,GAAkB;IAElB,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IAE9B,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;QAC3B,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,SAAS,EAAE,QAAQ,CAAC,gBAAgB;QACpC,MAAM,EAAE,GAAG,CAAC,MAAM;KACnB,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC;QACT,EAAE,EAAE,GAAG,CAAC,YAAY,EAAE;QACtB,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC,CAAC;IAEH,+EAA+E;IAC/E,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAExB,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,aAAa,EAAE,0BAA0B,EAAE,CAAC,CAAC;AACrF,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { SystemProxySettings, SystemProxySnapshot } from '../types.js';
2
+ import type { SystemProxyDriver } from './driver.js';
3
+ /**
4
+ * macOS driver.
5
+ *
6
+ * v2 derived a single Wi-Fi service from the default route, so an Ethernet dock
7
+ * or a second interface was left unproxied — and it never recorded what it
8
+ * overwrote. This applies to every *enabled* service and restores each one.
9
+ *
10
+ * Two properties matter more than they look:
11
+ *
12
+ * 1. `apply` uses the snapshot's target list, never its own enumeration, so the
13
+ * set of services we touch and the set we can restore are identical by
14
+ * construction. See `SystemProxyDriver.apply`.
15
+ * 2. `restore` is all-or-as-much-as-possible: one service that refuses a write
16
+ * must not abandon the rest. macOS happily lists VPN services (Tailscale,
17
+ * Shadowrocket) that reject proxy writes, and v3.0's `for` loop threw on the
18
+ * first one and left every later service proxied.
19
+ *
20
+ * Stranded HTTP proxies on macOS are unusually nasty: the OS publishes the
21
+ * *highest-ranked active* service's config to `State:/Network/Global/Proxies`,
22
+ * which is what every app reads. A leftover proxy on an unplugged dongle sits
23
+ * invisible until the dongle reappears, then silently captures all traffic —
24
+ * and System Settings shows Wi-Fi as clean, because the setting is elsewhere.
25
+ */
26
+ export declare class DarwinSystemProxy implements SystemProxyDriver {
27
+ readonly platform: NodeJS.Platform;
28
+ snapshot(): Promise<SystemProxySnapshot>;
29
+ apply(settings: SystemProxySettings, snapshot: SystemProxySnapshot): Promise<void>;
30
+ restore(snapshot: SystemProxySnapshot): Promise<void>;
31
+ }
32
+ //# sourceMappingURL=darwin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"darwin.d.ts","sourceRoot":"","sources":["../../../src/core/system-proxy/darwin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,mBAAmB,EACnB,mBAAmB,EAEpB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAQrD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,iBAAkB,YAAW,iBAAiB;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAY;IAExC,QAAQ,IAAI,OAAO,CAAC,mBAAmB,CAAC;IASxC,KAAK,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlF,OAAO,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;CAoB5D"}
@@ -0,0 +1,146 @@
1
+ import { exec, settle, tryExec } from './exec.js';
2
+ const NETWORKSETUP = '/usr/sbin/networksetup';
3
+ /** `networksetup` spells "no bypass domains" as this single literal argument. */
4
+ const EMPTY_BYPASS = 'Empty';
5
+ /**
6
+ * macOS driver.
7
+ *
8
+ * v2 derived a single Wi-Fi service from the default route, so an Ethernet dock
9
+ * or a second interface was left unproxied — and it never recorded what it
10
+ * overwrote. This applies to every *enabled* service and restores each one.
11
+ *
12
+ * Two properties matter more than they look:
13
+ *
14
+ * 1. `apply` uses the snapshot's target list, never its own enumeration, so the
15
+ * set of services we touch and the set we can restore are identical by
16
+ * construction. See `SystemProxyDriver.apply`.
17
+ * 2. `restore` is all-or-as-much-as-possible: one service that refuses a write
18
+ * must not abandon the rest. macOS happily lists VPN services (Tailscale,
19
+ * Shadowrocket) that reject proxy writes, and v3.0's `for` loop threw on the
20
+ * first one and left every later service proxied.
21
+ *
22
+ * Stranded HTTP proxies on macOS are unusually nasty: the OS publishes the
23
+ * *highest-ranked active* service's config to `State:/Network/Global/Proxies`,
24
+ * which is what every app reads. A leftover proxy on an unplugged dongle sits
25
+ * invisible until the dongle reappears, then silently captures all traffic —
26
+ * and System Settings shows Wi-Fi as clean, because the setting is elsewhere.
27
+ */
28
+ export class DarwinSystemProxy {
29
+ platform = 'darwin';
30
+ async snapshot() {
31
+ const entries = [];
32
+ for (const service of await listEnabledServices()) {
33
+ const entry = await readService(service);
34
+ if (entry)
35
+ entries.push(entry);
36
+ }
37
+ return { platform: 'darwin', entries };
38
+ }
39
+ async apply(settings, snapshot) {
40
+ const port = String(settings.port);
41
+ for (const { target } of snapshot.entries) {
42
+ await exec(NETWORKSETUP, ['-setwebproxy', target, settings.host, port]);
43
+ await exec(NETWORKSETUP, ['-setsecurewebproxy', target, settings.host, port]);
44
+ // Always write the bypass list, even when empty: leaving the previous
45
+ // list in place while our proxy is active would route the user's old
46
+ // exceptions through us.
47
+ await exec(NETWORKSETUP, [
48
+ '-setproxybypassdomains',
49
+ target,
50
+ ...(settings.bypass.length > 0 ? settings.bypass : [EMPTY_BYPASS]),
51
+ ]);
52
+ }
53
+ }
54
+ async restore(snapshot) {
55
+ // Every entry gets an attempt; failures are collected and reported together
56
+ // so a single stubborn service cannot strand the others.
57
+ await settle(snapshot.entries.map((entry) => async () => {
58
+ await restoreEndpoint('-setwebproxy', '-setwebproxystate', entry.target, entry);
59
+ await restoreEndpoint('-setsecurewebproxy', '-setsecurewebproxystate', entry.target, entry.secure ?? entry);
60
+ await exec(NETWORKSETUP, [
61
+ '-setproxybypassdomains',
62
+ entry.target,
63
+ ...(entry.bypass && entry.bypass.length > 0 ? entry.bypass : [EMPTY_BYPASS]),
64
+ ]);
65
+ }));
66
+ }
67
+ }
68
+ /**
69
+ * Restoring an endpoint takes two writes, not one: `-setwebproxy` sets host and
70
+ * port *and* enables it, so a service that had a host recorded but was switched
71
+ * off must be re-disabled afterwards. Otherwise "restore" would turn on a proxy
72
+ * the user had deliberately unchecked.
73
+ */
74
+ async function restoreEndpoint(setFlag, stateFlag, target, state) {
75
+ if (state.host.length > 0 && state.port > 0) {
76
+ await exec(NETWORKSETUP, [setFlag, target, state.host, String(state.port)]);
77
+ if (!state.enabled)
78
+ await exec(NETWORKSETUP, [stateFlag, target, 'off']);
79
+ return;
80
+ }
81
+ // There was no proxy here, so blank the host/port too rather than only
82
+ // unticking the box. macOS keeps the last value in the field, so skipping this
83
+ // leaves our address sitting in System Settings looking like a configured
84
+ // proxy — the exact thing a user checks when they suspect us.
85
+ //
86
+ // Best-effort: `state off` below is what actually guarantees correctness, so a
87
+ // service that rejects the empty form must not fail the restore.
88
+ await tryExec(NETWORKSETUP, [setFlag, target, '', '0']);
89
+ await exec(NETWORKSETUP, [stateFlag, target, 'off']);
90
+ }
91
+ /**
92
+ * `networksetup -listallnetworkservices` prints a header line, then one service
93
+ * per line. Disabled services are prefixed with `*`.
94
+ */
95
+ async function listEnabledServices() {
96
+ const output = await exec(NETWORKSETUP, ['-listallnetworkservices']);
97
+ return output
98
+ .split('\n')
99
+ .slice(1)
100
+ .map((line) => line.trim())
101
+ .filter((line) => line.length > 0 && !line.startsWith('*'));
102
+ }
103
+ async function readService(service) {
104
+ const web = await readEndpoint('-getwebproxy', service);
105
+ if (!web)
106
+ return undefined;
107
+ const secure = await readEndpoint('-getsecurewebproxy', service);
108
+ return {
109
+ target: service,
110
+ ...web,
111
+ ...(secure ? { secure } : {}),
112
+ bypass: await readBypassDomains(service),
113
+ };
114
+ }
115
+ async function readEndpoint(flag, service) {
116
+ const output = await tryExec(NETWORKSETUP, [flag, service]);
117
+ if (output === undefined)
118
+ return undefined;
119
+ const fields = new Map();
120
+ for (const line of output.split('\n')) {
121
+ const separator = line.indexOf(':');
122
+ if (separator > 0) {
123
+ fields.set(line.slice(0, separator).trim().toLowerCase(), line.slice(separator + 1).trim());
124
+ }
125
+ }
126
+ return {
127
+ enabled: fields.get('enabled')?.toLowerCase() === 'yes',
128
+ host: fields.get('server') ?? '',
129
+ port: Number.parseInt(fields.get('port') ?? '0', 10) || 0,
130
+ };
131
+ }
132
+ /**
133
+ * Either one domain per line, or the sentence "There aren't any bypass domains
134
+ * set on <service>." — which is prose, not a domain, and must not be restored
135
+ * as one.
136
+ */
137
+ async function readBypassDomains(service) {
138
+ const output = await tryExec(NETWORKSETUP, ['-getproxybypassdomains', service]);
139
+ if (output === undefined)
140
+ return [];
141
+ return output
142
+ .split('\n')
143
+ .map((line) => line.trim())
144
+ .filter((line) => line.length > 0 && !line.includes(' '));
145
+ }
146
+ //# sourceMappingURL=darwin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"darwin.js","sourceRoot":"","sources":["../../../src/core/system-proxy/darwin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { SystemProxySettings, SystemProxySnapshot } from '../types.js';
2
+ /** One implementation per OS. */
3
+ export interface SystemProxyDriver {
4
+ readonly platform: NodeJS.Platform;
5
+ /** Capture the current OS proxy configuration so it can be put back. */
6
+ snapshot(): Promise<SystemProxySnapshot>;
7
+ /**
8
+ * Point the OS at our proxy, touching **exactly** the targets in `snapshot`.
9
+ *
10
+ * Taking the target list from the snapshot rather than re-enumerating is the
11
+ * whole point of this signature. v3.0's macOS driver called
12
+ * `listAllNetworkServices()` separately in `snapshot()` and `apply()`, so any
13
+ * service that appeared in between — a USB-C ethernet dongle, an iPhone
14
+ * tether, a VPN connecting — was proxied with no snapshot entry to restore it
15
+ * from, and stayed pointed at a dead port permanently.
16
+ */
17
+ apply(settings: SystemProxySettings, snapshot: SystemProxySnapshot): Promise<void>;
18
+ /** Put back exactly what `snapshot()` recorded, for every entry it holds. */
19
+ restore(snapshot: SystemProxySnapshot): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../../src/core/system-proxy/driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE5E,iCAAiC;AACjC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IACnC,wEAAwE;IACxE,QAAQ,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACzC;;;;;;;;;OASG;IACH,KAAK,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnF,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../../src/core/system-proxy/driver.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Run a command with an argument array — never a shell string.
3
+ *
4
+ * v2 interpolated network-service names straight into `sh -c "..."`, which is
5
+ * both fragile (names contain spaces) and a command-injection hazard.
6
+ */
7
+ export declare function exec(file: string, args: readonly string[]): Promise<string>;
8
+ /** Same as `exec`, but resolves to `undefined` instead of throwing. */
9
+ export declare function tryExec(file: string, args: readonly string[]): Promise<string | undefined>;
10
+ /** Is `file` on PATH / runnable? */
11
+ export declare function isAvailable(file: string, args?: readonly string[]): Promise<boolean>;
12
+ /**
13
+ * Run every task even if some throw, then report the failures together.
14
+ *
15
+ * This is what makes teardown safe. A plain `for (…) await …` loop abandons
16
+ * everything after the first failure, and during *restore* that means leaving
17
+ * services pointed at a port that is about to disappear. Getting an error is
18
+ * fine; getting an error and silently skipping nine services is not.
19
+ */
20
+ export declare function settle(tasks: Iterable<() => Promise<void>>): Promise<void>;
21
+ //# sourceMappingURL=exec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../../src/core/system-proxy/exec.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CASjF;AAED,uEAAuE;AACvE,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAMhG;AAED,oCAAoC;AACpC,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,SAAS,MAAM,EAAkB,oBAEtF;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBhF"}
@@ -0,0 +1,62 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { SystemProxyError, toError } from '../errors.js';
4
+ const run = promisify(execFile);
5
+ /**
6
+ * Run a command with an argument array — never a shell string.
7
+ *
8
+ * v2 interpolated network-service names straight into `sh -c "..."`, which is
9
+ * both fragile (names contain spaces) and a command-injection hazard.
10
+ */
11
+ export async function exec(file, args) {
12
+ try {
13
+ const { stdout } = await run(file, [...args], { windowsHide: true });
14
+ return stdout;
15
+ }
16
+ catch (cause) {
17
+ throw new SystemProxyError(`\`${file} ${args.join(' ')}\` failed: ${toError(cause).message}`, {
18
+ cause,
19
+ });
20
+ }
21
+ }
22
+ /** Same as `exec`, but resolves to `undefined` instead of throwing. */
23
+ export async function tryExec(file, args) {
24
+ try {
25
+ return await exec(file, args);
26
+ }
27
+ catch {
28
+ return undefined;
29
+ }
30
+ }
31
+ /** Is `file` on PATH / runnable? */
32
+ export async function isAvailable(file, args = ['--version']) {
33
+ return (await tryExec(file, args)) !== undefined;
34
+ }
35
+ /**
36
+ * Run every task even if some throw, then report the failures together.
37
+ *
38
+ * This is what makes teardown safe. A plain `for (…) await …` loop abandons
39
+ * everything after the first failure, and during *restore* that means leaving
40
+ * services pointed at a port that is about to disappear. Getting an error is
41
+ * fine; getting an error and silently skipping nine services is not.
42
+ */
43
+ export async function settle(tasks) {
44
+ const failures = [];
45
+ for (const task of tasks) {
46
+ try {
47
+ await task();
48
+ }
49
+ catch (error) {
50
+ failures.push(toError(error));
51
+ }
52
+ }
53
+ const [first] = failures;
54
+ if (!first)
55
+ return;
56
+ if (failures.length === 1)
57
+ throw first;
58
+ throw new SystemProxyError(failures.map((failure) => failure.message).join('; '), {
59
+ cause: new AggregateError(failures),
60
+ });
61
+ }
62
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/core/system-proxy/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,GAAG,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAY,EAAE,IAAuB;IAC9D,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,gBAAgB,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE;YAC5F,KAAK;SACN,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAY,EAAE,IAAuB;IACjE,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,oCAAoC;AACpC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,OAA0B,CAAC,WAAW,CAAC;IACrF,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,SAAS,CAAC;AACnD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAoC;IAC/D,MAAM,QAAQ,GAAY,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;IACzB,IAAI,CAAC,KAAK;QAAE,OAAO;IACnB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,KAAK,CAAC;IACvC,MAAM,IAAI,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;QAChF,KAAK,EAAE,IAAI,cAAc,CAAC,QAAQ,CAAC;KACpC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,45 @@
1
+ import type { SystemProxySettings, SystemProxySnapshot } from '../types.js';
2
+ import type { SystemProxyDriver } from './driver.js';
3
+ export type { SystemProxyDriver } from './driver.js';
4
+ export { DarwinSystemProxy } from './darwin.js';
5
+ export { LinuxSystemProxy } from './linux.js';
6
+ export { WindowsSystemProxy } from './windows.js';
7
+ export { defaultRecoveryPath } from './recovery.js';
8
+ export declare const DEFAULT_BYPASS: readonly ["localhost", "127.0.0.1", "::1", "*.local"];
9
+ export declare function isSystemProxySupported(): boolean;
10
+ export declare function createSystemProxyDriver(): SystemProxyDriver;
11
+ export interface SystemProxyOptions {
12
+ readonly driver?: SystemProxyDriver;
13
+ /**
14
+ * Where to persist the pending snapshot so a killed process can still be
15
+ * cleaned up. `null` disables persistence (used by tests).
16
+ */
17
+ readonly recoveryFile?: string | null;
18
+ }
19
+ /**
20
+ * Snapshot-and-restore wrapper around the platform drivers.
21
+ *
22
+ * The important property: `disable()` puts back exactly what was there before,
23
+ * rather than v2's "turn everything off" — which quietly wiped a corporate or
24
+ * developer proxy the user had configured.
25
+ */
26
+ export declare class SystemProxy {
27
+ #private;
28
+ constructor(options?: SystemProxyOptions);
29
+ get active(): boolean;
30
+ enable(settings: SystemProxySettings): Promise<void>;
31
+ disable(): Promise<void>;
32
+ }
33
+ /**
34
+ * Undo a system proxy left behind by a run that never got to clean up — a
35
+ * `SIGKILL`, a force-quit, a panic, a pulled power cord.
36
+ *
37
+ * Call this at startup, before `enable()`. Returns the snapshot it restored, or
38
+ * `null` when there was nothing to recover.
39
+ *
40
+ * Without this, v3.0 had no path back at all: nothing recorded what had been
41
+ * changed, so a killed process left the machine pointed at a dead port with no
42
+ * way for any later run to know, or to fix it.
43
+ */
44
+ export declare function recoverSystemProxy(options?: SystemProxyOptions): Promise<SystemProxySnapshot | null>;
45
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/system-proxy/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,mBAAmB,EACnB,mBAAmB,EAEpB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAUrD,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,cAAc,uDAAwD,CAAC;AAEpF,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED,wBAAgB,uBAAuB,IAAI,iBAAiB,CAW3D;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED;;;;;;GAMG;AACH,qBAAa,WAAW;;gBAKV,OAAO,GAAE,kBAAuB;IAM5C,IAAI,MAAM,IAAI,OAAO,CAEpB;IAEK,MAAM,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBpD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAa/B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAmBrC"}
@@ -0,0 +1,151 @@
1
+ import { platform } from 'node:process';
2
+ import { SystemProxyError, toError } from '../errors.js';
3
+ import { DarwinSystemProxy } from './darwin.js';
4
+ import { LinuxSystemProxy } from './linux.js';
5
+ import { defaultRecoveryPath, readRecoveryFile, removeRecoveryFile, writeRecoveryFile, } from './recovery.js';
6
+ import { WindowsSystemProxy } from './windows.js';
7
+ export { DarwinSystemProxy } from './darwin.js';
8
+ export { LinuxSystemProxy } from './linux.js';
9
+ export { WindowsSystemProxy } from './windows.js';
10
+ export { defaultRecoveryPath } from './recovery.js';
11
+ export const DEFAULT_BYPASS = ['localhost', '127.0.0.1', '::1', '*.local'];
12
+ export function isSystemProxySupported() {
13
+ return platform === 'darwin' || platform === 'linux' || platform === 'win32';
14
+ }
15
+ export function createSystemProxyDriver() {
16
+ switch (platform) {
17
+ case 'darwin':
18
+ return new DarwinSystemProxy();
19
+ case 'linux':
20
+ return new LinuxSystemProxy();
21
+ case 'win32':
22
+ return new WindowsSystemProxy();
23
+ default:
24
+ throw new SystemProxyError(`Setting the system proxy is not supported on ${platform}`);
25
+ }
26
+ }
27
+ /**
28
+ * Snapshot-and-restore wrapper around the platform drivers.
29
+ *
30
+ * The important property: `disable()` puts back exactly what was there before,
31
+ * rather than v2's "turn everything off" — which quietly wiped a corporate or
32
+ * developer proxy the user had configured.
33
+ */
34
+ export class SystemProxy {
35
+ #driver;
36
+ #recoveryFile;
37
+ #snapshot = null;
38
+ constructor(options = {}) {
39
+ this.#driver = options.driver ?? createSystemProxyDriver();
40
+ this.#recoveryFile =
41
+ options.recoveryFile === undefined ? defaultRecoveryPath() : options.recoveryFile;
42
+ }
43
+ get active() {
44
+ return this.#snapshot !== null;
45
+ }
46
+ async enable(settings) {
47
+ if (this.#snapshot)
48
+ return;
49
+ // Snapshot first: if `apply` throws halfway we can still put things back.
50
+ // `disown` keeps us from mistaking our own leftovers for a user setting.
51
+ const snapshot = disown(await this.#driver.snapshot(), settings);
52
+ // Persist *before* touching the OS. If we are killed between here and
53
+ // `disable()`, the next run finds this file and undoes the damage.
54
+ if (this.#recoveryFile) {
55
+ await writeRecoveryFile(this.#recoveryFile, snapshot).catch(() => undefined);
56
+ }
57
+ this.#snapshot = snapshot;
58
+ try {
59
+ await this.#driver.apply(settings, snapshot);
60
+ }
61
+ catch (error) {
62
+ await this.#driver.restore(snapshot).catch(() => undefined);
63
+ this.#snapshot = null;
64
+ if (this.#recoveryFile)
65
+ await removeRecoveryFile(this.#recoveryFile).catch(() => undefined);
66
+ throw error;
67
+ }
68
+ }
69
+ async disable() {
70
+ const snapshot = this.#snapshot;
71
+ if (!snapshot)
72
+ return;
73
+ this.#snapshot = null;
74
+ await this.#driver.restore(snapshot);
75
+ // Drop the recovery file only once the OS is genuinely back. If `restore`
76
+ // threw above, the file survives on purpose: the machine is still (partly)
77
+ // pointed at us, and the next run must be able to finish the job. Clearing
78
+ // it unconditionally is how a stranded proxy becomes permanent.
79
+ if (this.#recoveryFile)
80
+ await removeRecoveryFile(this.#recoveryFile).catch(() => undefined);
81
+ }
82
+ }
83
+ /**
84
+ * Undo a system proxy left behind by a run that never got to clean up — a
85
+ * `SIGKILL`, a force-quit, a panic, a pulled power cord.
86
+ *
87
+ * Call this at startup, before `enable()`. Returns the snapshot it restored, or
88
+ * `null` when there was nothing to recover.
89
+ *
90
+ * Without this, v3.0 had no path back at all: nothing recorded what had been
91
+ * changed, so a killed process left the machine pointed at a dead port with no
92
+ * way for any later run to know, or to fix it.
93
+ */
94
+ export async function recoverSystemProxy(options = {}) {
95
+ const path = options.recoveryFile === undefined ? defaultRecoveryPath() : options.recoveryFile;
96
+ if (!path)
97
+ return null;
98
+ const snapshot = await readRecoveryFile(path);
99
+ if (!snapshot)
100
+ return null;
101
+ const driver = options.driver ?? createSystemProxyDriver();
102
+ try {
103
+ await driver.restore(snapshot);
104
+ }
105
+ catch (error) {
106
+ throw new SystemProxyError(`Could not undo the system proxy left by a previous run: ${toError(error).message}`, { cause: error });
107
+ }
108
+ await removeRecoveryFile(path).catch(() => undefined);
109
+ return snapshot;
110
+ }
111
+ /**
112
+ * Rewrite any entry that is really *our own* leftover into "was off".
113
+ *
114
+ * Without this the damage is self-perpetuating: a run that fails to clean up
115
+ * leaves `127.0.0.1:<our port>` behind, the next run records that as the user's
116
+ * original proxy, and dutifully "restores" it on exit — forever. The recovery
117
+ * file is the primary defence; this is the backstop for when it is missing,
118
+ * stale, or was never written.
119
+ *
120
+ * A proxy pointing at loopback on the port we are about to bind cannot be a
121
+ * useful upstream — nothing else is listening there once we take the port.
122
+ */
123
+ function disown(snapshot, settings) {
124
+ return {
125
+ ...snapshot,
126
+ entries: snapshot.entries.map((entry) => disownEntry(entry, settings)),
127
+ };
128
+ }
129
+ function disownEntry(entry, settings) {
130
+ const web = isOurs(entry, settings);
131
+ const secure = entry.secure ? isOurs(entry.secure, settings) : web;
132
+ if (!web && !secure)
133
+ return entry;
134
+ return {
135
+ ...entry,
136
+ ...(web ? OFF : { enabled: entry.enabled, host: entry.host, port: entry.port }),
137
+ ...(entry.secure ? { secure: secure ? OFF : entry.secure } : {}),
138
+ // If the proxy was ours, the bypass list we found next to it was ours too.
139
+ ...(web && secure ? { bypass: [] } : {}),
140
+ };
141
+ }
142
+ const OFF = { enabled: false, host: '', port: 0 };
143
+ function isOurs(state, settings) {
144
+ if (state.port !== settings.port)
145
+ return false;
146
+ return state.host === settings.host || isLoopback(state.host);
147
+ }
148
+ function isLoopback(host) {
149
+ return host === 'localhost' || host === '::1' || host.startsWith('127.');
150
+ }
151
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/system-proxy/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { SystemProxySettings, SystemProxySnapshot } from '../types.js';
2
+ import type { SystemProxyDriver } from './driver.js';
3
+ /**
4
+ * Linux driver, GNOME/GSettings flavour — which also covers Unity, Cinnamon,
5
+ * Budgie and anything else reading the same schema.
6
+ *
7
+ * Note this only affects applications that honour the desktop proxy settings.
8
+ * Terminal tools read `http_proxy`/`https_proxy`, which no process can set for
9
+ * another; the CLI prints them for you to export.
10
+ */
11
+ export declare class LinuxSystemProxy implements SystemProxyDriver {
12
+ #private;
13
+ readonly platform: NodeJS.Platform;
14
+ snapshot(): Promise<SystemProxySnapshot>;
15
+ apply(settings: SystemProxySettings): Promise<void>;
16
+ restore(snapshot: SystemProxySnapshot): Promise<void>;
17
+ }
18
+ //# sourceMappingURL=linux.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linux.d.ts","sourceRoot":"","sources":["../../../src/core/system-proxy/linux.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAChG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAMrD;;;;;;;GAOG;AACH,qBAAa,gBAAiB,YAAW,iBAAiB;;IACxD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAW;IAEvC,QAAQ,IAAI,OAAO,CAAC,mBAAmB,CAAC;IAqBxC,KAAK,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAanD,OAAO,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;CAkC5D"}